Installation
Requirements
- Node.js
>=22.12.0(Node 20 reached end-of-life in April 2026; CI runs Node 22 and 24) - One of: pnpm, npm, yarn, or bun
Scaffold a new project
The fastest way to start is the create-rudder scaffolder. It detects your package manager and adapts every generated file accordingly.
pnpm create rudder my-app
# or: npm create rudder@latest my-app
# or: yarn create rudder my-app
# or: bunx create-rudder my-appThe legacy
create-rudder-appinvocation is deprecated — the published alias still resolves and scaffolds identically (printing a one-line nudge), but usecreate-rudderdirectly.
The scaffolder asks a short recipe-driven sequence, then generates only the code for the choices you made. Selected packages get added to package.json, registered via auto-discovery, and have their config files generated. Unselected packages are excluded entirely — no dead code, no orphan config.
| # | Prompt | Notes |
|---|---|---|
| 1 | Project name | — |
| 2 | What are you building? (recipe) | Web app · SaaS · API service · Realtime · Minimal · Custom |
| 3 | Database | Native (default — built-in, no external ORM) · Prisma · Drizzle (+ None for Minimal/Custom) |
| 4 | Database driver | SQLite (recommended — no setup) · PostgreSQL · MySQL — asked for every engine, Native included |
| 5 | Frontend framework | React · Vue · Solid · None — skipped for api-service / minimal |
| 6 | Styling | Tailwind+shadcn · Tailwind · Plain CSS — only when a framework is selected |
| 7 | Is your DB running now? | Only for PostgreSQL/MySQL — if yes, the installer runs your migrations (Native) or pushes the schema (Prisma/Drizzle) |
| 8 | Install and run setup? | yes triggers the full auto-cascade described below |
Each recipe is a curated bundle of packages — pick one of the five named recipes for the common shapes, or Custom to walk through the full 25-package multiselect.
| Recipe | Adds on top of the framework core | Frontend? |
|---|---|---|
| Web app (default) | auth |
yes |
| SaaS | auth + queue + mail + notifications |
yes |
| API service | auth + http |
no |
| Realtime | auth + broadcast + sync |
yes |
| Minimal | nothing beyond the framework core | no |
| Custom | (prompts the full multiselect) | optional |
When Install and run setup is yes (the default), the scaffolder runs the entire post-scaffold sequence for you — pnpm install, rudder providers:discover, the database setup (rudder migrate on the native engine; rudder db:generate + rudder db:push for Prisma/Drizzle — on every engine this runs immediately on SQLite, and after your confirmation on Postgres/MySQL), rudder vendor:publish --tag=auth-views-* (when needed), rudder passport:keys (when Passport is selected), and git init + initial commit. On the happy path the final panel says one thing:
cd my-app && pnpm devYour app runs at http://localhost:3000.
Frontend combinations
The scaffolder supports any combination of React, Vue, and Solid. The primary framework drives the main pages (pages/index/, pages/_error/); each secondary framework gets a minimal demo at pages/{fw}-demo/. When React and Solid coexist, the Vite config automatically routes .tsx files to the correct plugin.
| Primary | Page extension | tsconfig |
|---|---|---|
| React | .tsx |
jsx: react-jsx |
| Vue | .vue |
— |
| Solid | .tsx |
jsx: preserve + jsxImportSource: solid-js |
Tailwind & shadcn
shadcn/ui is offered only when React and Tailwind are both selected.
| Selection | What's generated |
|---|---|
| Tailwind + shadcn | src/index.css with full shadcn CSS variables |
| Tailwind only | src/index.css with @import "tailwindcss" |
| Neither | No src/index.css; pages use semantic CSS classes |
Non-interactive (CI / AI agents)
When create-rudder runs inside an AI coding agent — Claude Code, Cursor, GitHub Copilot, Codex, Gemini CLI, or Windsurf — it auto-detects via env vars and switches from interactive prompts to a flag-driven flow with structured JSON output to stdout. Agents get a parseable success/failure result instead of garbled TTY redraws.
CLAUDECODE=1 npx create-rudder my-app \
--recipe=web-app \
--framework=react --styling=tailwind+shadcn \
--install=true// success — single line of JSON to stdout; cascade fields appear only when --install=true
{
"success": true, "name": "my-app", "directory": "/abs/path/my-app", "files": 36,
"agent": "claude-code",
"installed": true, "providersDiscovered": true,
"dbPushed": true, // native engine: carries the `rudder migrate` result;
// prisma/drizzle additionally report "dbGenerated"
"gitInitialized": true
}
// missing flags (exit 1)
{ "success": false, "error": "Missing required flags...", "requiredFlags": ["--recipe", "--db"], "agent": "claude-code" }Every prompt has a corresponding flag. Each flag also works in interactive mode — pass --recipe=web-app to skip the first question, useful for CI templates.
| Flag | Values |
|---|---|
--recipe |
web-app, saas, api-service, realtime, minimal, custom |
--orm |
native (default), prisma, drizzle, none |
--db |
sqlite (default), postgresql, mysql — works with every engine; the native default scaffolds all three. Only required with an explicit --orm=prisma|drizzle |
--framework |
react, vue, solid, none (omit for api-service / minimal) |
--styling |
tailwind+shadcn, tailwind, plain (optional — recipe picks a sensible default) |
--packages |
comma-separated package names (only when --recipe=custom) |
--db-ready |
true, false — pre-answers the Postgres/MySQL "is your DB running?" prompt |
--git |
true, false — whether to run git init + initial commit (default true) |
--install |
true, false |
--json |
force JSON output regardless of detection |
--interactive |
force the prompt UI even inside an agent |
The legacy explicit-flag shape (--orm without --recipe, plus --packages, --frameworks, --primary-framework, --tailwind, --shadcn) still parses for backwards-compat with pre-recipe scripts. The --demos flag is preserved as a silent no-op — demos were dropped from the default scaffolder.
Set RUDDER_NONINTERACTIVE=1 in the environment to opt into JSON mode without an agent (e.g. CI scripts).
Manual installation
For an existing Vite project, install the foundation packages and wire them up by hand.
1. Add the core packages
pnpm add @rudderjs/core @rudderjs/server-hono @rudderjs/router
pnpm add -D vite typescript
# Choose your data layer — native engine (built-in, no external ORM):
pnpm add @rudderjs/orm better-sqlite3
pnpm add -D @types/better-sqlite3
# …or the Prisma adapter:
pnpm add @rudderjs/orm @rudderjs/orm-prisma @prisma/client
pnpm add -D prisma2. Bootstrap the application
bootstrap/app.ts is both the bootstrap file and the application entry point. It must import 'reflect-metadata' — that one import enables the entire DI container.
// bootstrap/app.ts
import 'reflect-metadata'
import 'dotenv/config'
import { Application } from '@rudderjs/core'
import providers from './providers.ts'
import config from '../config/index.ts'
export default Application.configure({ config, providers })
.withRouting({
web: () => import('../routes/web.ts'),
api: () => import('../routes/api.ts'),
commands: () => import('../routes/console.ts'),
})
.create()// bootstrap/providers.ts
import { defaultProviders } from '@rudderjs/core'
import { AppServiceProvider } from '../app/Providers/AppServiceProvider.js'
export default [
...(await defaultProviders()),
AppServiceProvider,
]3. Wire Vike
+server.ts at the project root connects Vike to the Rudder application:
// +server.ts
import type { Server } from 'vike/types'
import app from './bootstrap/app.js'
export default { fetch: app.fetch } satisfies ServerFor single-framework apps, declare the renderer in pages/+config.ts:
// pages/+config.ts
import type { Config } from 'vike/types'
import vikeReact from 'vike-react/config'
export default { extends: [vikeReact] } satisfies ConfigFor Vue or Solid replace vike-react/config with vike-vue/config or vike-solid/config. For multiple frameworks, see Frontend.
4. Vite config
// vite.config.ts
import { defineConfig } from 'vite'
import vike from 'vike/plugin'
import rudder from '@rudderjs/vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [rudder(), vike(), react()],
})Plugin order matters. Put
rudder()beforevike()— the views-scanner writes auto-generated stubs topages/__view/during plugin construction, and Vike scanspages/during its own construction, so the stubs must exist beforevike()is called.
5. Environment variables
APP_NAME=MyApp
APP_ENV=local
APP_DEBUG=true
PORT=3000
DATABASE_URL="file:./dev.db"
AUTH_SECRET=your-32-char-secret-hereNever commit .env. Provide .env.example as a template.
Keeping up to date
Rudder releases frequently — sometimes daily across the 48 published @rudderjs/* packages. To bump every @rudderjs/* dependency to the latest version published on npm in one step:
pnpm rudder upgradeWhat it does:
- Finds every
@rudderjs/*package in yourdependencies,devDependencies, andpeerDependencies. - Queries the npm registry for each one's latest version.
- Rewrites
package.jsonwith the new caret ranges. - Runs your package manager's install (detected from your lockfile — pnpm / npm / yarn / bun).
Flags
| Flag | Behavior |
|---|---|
--check |
Print the upgrade plan and exit 1 if updates are available. Doesn't modify anything — CI-friendly. |
--dry-run |
Show what would change. Doesn't modify package.json, doesn't install. |
--latest (default) |
Bump to the latest version, including across majors. |
--minor |
Cap each bump within the current major (no breaking changes). |
--patch |
Cap each bump within the current minor (bug fixes only). |
--registry <url> |
Override the npm registry URL (e.g. a private mirror). |
# Conservative — bug fixes only
pnpm rudder upgrade --patch
# Within-major — pick up new features but no breaking changes
pnpm rudder upgrade --minor
# Preview without applying
pnpm rudder upgrade --dry-run
# CI gate — fail the build when updates are available
pnpm rudder upgrade --checkMajor bumps are highlighted in red — review the relevant CHANGELOG.md before applying. The framework follows semver: a feat: release goes minor, a feat!: release goes major.
CHANGELOG snippets
For every package being bumped, rudder upgrade fetches its CHANGELOG.md from the framework's public GitHub repo, parses out every ## X.Y.Z section in the window between your current and target versions, and prints a one-line headline per version under the bump row:
@rudderjs/cli 4.6.5 → 4.7.1 (devDependencies)
4.7.1 rudder upgrade — handle floating dist-tag ranges (latest, *, next)
4.7.0 rudder upgrade — one-step bump of every @rudderjs/* dep to latest
4.6.9 stripInternal: true is now set in tsconfig.base.json
4.6.8 make:controller (and --resource / --api / --singleton) — fix generated stub
4.6.7 rudder --version and the rudder banner printed a hardcoded 0.0.2
4.6.6 fix(doctor): load .env before env-var checksHeadlines are pulled from the first non-trivial bullet of each version's changeset entry; the cite-prefix (abc1234:) is stripped and the noisy Updated dependencies [...] lines are skipped.
Flags:
| Flag | Behavior |
|---|---|
--no-changelog |
Skip the CHANGELOG fetch entirely. Faster, quieter output — useful for CI gates that only care about --check's exit code. |
--changelog-base <url> |
Override the GitHub raw base URL. Useful for fork-based development; default is https://raw.githubusercontent.com/rudderjs/rudder/main. |
Fetch failures degrade gracefully — a row whose CHANGELOG can't be fetched simply renders without the indented detail block.
Peer-dependency mismatches
rudder upgrade also fetches each upgraded @rudderjs/* package's peerDependencies and diffs them against the peers declared in your package.json. If a framework package has bumped a peer major past what your app carries, you'll see a warning like:
⚠ Peer-dependency mismatches:
vite — required by @rudderjs/vite@3.0.0
your package.json: devDependencies.vite = "^7.1.0"
framework needs: "^8.0.0"
reason: consumer accepts major 7, framework needs major 8
Update these peer ranges in your package.json (then re-run upgrade).The warning is informational — rudder upgrade still completes — but --check treats peer mismatches as part of the exit-1 condition, so CI gates catch them.
This closes the gap where pnpm update --latest "@rudderjs/*" happily bumps the framework but leaves a stale peer (the vite 7 → 8 situation behind the scenes on rudderjs.com).
Next steps
- Configuration — environment variables, runtime config, framework wiring
- Directory Structure — what goes where
- Service Providers — register your own services