Everything is a plugin. Every plugin is your code. Like shadcn/ui, but for your entire stack.
How merging works
Installing a plugin doesn’t add a dependency you import fromnode_modules — it copies files into the three places your code already lives:
apps/api/src/resources/<name>/— endpoints, methods, handlers, middlewares, schemasapps/web/src/routes/<name>/— TanStack Router routes, auto-discovered by file pathpackages/<pkg>/— any shared package the plugin ships (e.g.@ship/ai,@ship/emails)
package.json, and any infra it needs (a docker-compose.<plugin>.yml, an infra:<plugin> script, extra .env lines) is wired in too.
After files land, codegen runs so the new resources and routes are live:
src/router.ts, src/contract.ts, and src/db.ts to include the plugin’s endpoints and tables. For a fresh scaffold, the CLI runs this for you after install.
Installing plugins
Pick plugins during scaffolding — theinit flow shows a multiselect, then merges your choices into the new project:
Auth is plugin-delivered. The base
apps/web ships only a landing page; the Auth plugin (auth-starter) adds the API wiring, the sign-in/up and reset pages, the authenticated app shell, and the typed oRPC client. A full-stack project that needs accounts starts with this plugin.The catalog
| Plugin | What it adds | Requires |
|---|---|---|
| auth-starter | better-auth wiring — email/password, verification, reset, Google OAuth — plus the web auth pages, app shell, and oRPC client. | postgres, mailer, cloud-storage |
| admin | An admin dashboard with a paginated user list at /app/admin. The canonical TanStack Router plugin example. | postgres, auth-starter |
| notes | A small notes CRUD resource and UI — the minimal end-to-end plugin example. | postgres |
| ai-chat | Streaming AI chat via the @ship/ai package (Google Gemini): conversations, messages, AI responses. | postgres, auth-starter |
| mailer | Transactional email — Resend + React Email templates, exposed as @ship/emails. | — |
| cloud-storage | S3-compatible file storage with a local Garage dev server, exposed as @ship/cloud-storage. | — |
requires are merged in automatically — selecting admin pulls in auth-starter, which pulls in mailer and cloud-storage.
Anatomy of a plugin
A plugin mirrors the resource-owns-everything layout of the app it merges into:@/endpoint base and the same @/middlewares/* gates (is-authorized, is-admin, can-access, can-edit) as the rest of your code — there’s nothing plugin-specific to learn. Web routes go under web/routes/** and TanStack Router discovers them by path.
plugin.json
The manifest names the plugin, lists what it requires, and declares which dependencies go to which app:
plugin.json
dependencies.api is appended to apps/api/package.json; dependencies.web to apps/web/package.json.
