Scaffold a minimal React + Vite project with optional TypeScript, Tailwind CSS, React Router, ESLint + Prettier, and Zustand — pick only what you need, get a project that boots immediately.
The official Vite scaffold only asks you to pick a framework and a JS/TS variant. It does not know about Tailwind, Router, state management, or linting — after generating, you still have to wire those up by hand.
rv covers those extras:
| What you get | npm create vite@latest |
rv |
|---|---|---|
| React + Vite baseline | Yes | Yes |
| TypeScript variant | Yes | Yes |
| Tailwind v4 (fully wired) | No — manual setup | Yes — plugin + @import in CSS |
| React Router with a working demo | No | Yes — 2 routes (/, /about) |
| Zustand store + counter demo | No | Yes |
| ESLint flat config + Prettier | ESLint only, no Prettier | Both, TS-aware when TS is on |
git init + editor recommendations |
No | Yes — plus .vscode/extensions.json |
| Init into an existing directory | No (create-* makes new dirs) |
Yes — rv init with safe merge |
| Generators after scaffolding | No | Yes — rv add page/component/store |
| Project health check | No | Yes — rv doctor |
| Runs install for you (npm/pnpm/yarn) | No | Yes — prompts for PM on PATH |
Every feature is opt-in. Select none and you get the same minimal output as Vite's own template — no extra files or dependencies.
Node.js ^20.19.0 || ^22.13.0 || >=24.0.0 (same range the generated
projects target).
npm install -g react-vite
# or
pnpm add -g react-vite
# or
yarn global add react-viteTwo equivalent global commands are installed: rv (short) and react-vite
(fallback if rv ever collides with an alias on your machine). You can also
run it without installing:
npx react-vite create my-apprv create my-appCreates ./my-app (the directory must be empty or missing), asks which
features you want and which package manager should install dependencies,
then scaffolds the project and runs git init (skipped with --no-git, or
automatically when you are already inside a git repository or git is not
installed — the first commit is always left to you). Omit the directory
argument to be prompted for it.
mkdir my-app && cd my-app
rv initWorks in non-empty directories too:
- An existing
package.jsonis merged, not replaced — your name, version, and unrelated fields/scripts are preserved; the generated dependencies and scripts are added. - If generated files would overwrite existing ones, you choose to overwrite them, keep the existing files, or cancel (nothing is written).
rv add page blog-post # lazy-loaded route + nav link (router projects)
rv add page blog/detail # nested route paths work too
rv add component user-card
rv add store cart # zustand store (zustand projects)Each generator detects TypeScript and Tailwind from your package.json and
writes files in the same style as the rest of the project:
rv add page <name>— generatessrc/pages/BlogPost.tsx(or.jsx), registers a lazy-loaded/blog-postroute insrc/router(so every extra page becomes its own chunk), and adds a navigation link insrc/layouts/RootLayout. Requires the React Router feature.rv add component <name>— generatessrc/components/UserCard.tsx.rv add store <name>— generatessrc/store/useCart.tswith a typed zustand store skeleton. Requires the Zustand feature.
Route and nav registration rely on the small // rv:route-style anchor
comments that scaffolded projects contain. If you have restructured those
files (or the project was created before v0.0.7), the page is still
generated and rv prints the exact lines to add manually.
rv doctor # report only
rv update # pin drifted dependencies back to the rv versions
rv update --dry-run # preview what update would changerv doctor reports dependency drift (versions that differ from the set rv
pins and verifies), whether your Node version is supported, and whether the
rv anchors are still in place so the generators keep working. rv update
applies the dependency part of that report to package.json; run your
package manager's install afterwards.
create, init, and add accept --dry-run: the command prints exactly
which files would be written (and, for init, which existing files
conflict) without touching the disk.
Both commands ask three things (skipping whatever you already provided as a flag):
- Project directory (
create) or package name (init) - Optional features — a checkbox list: TypeScript, Tailwind CSS, React Router, ESLint + Prettier, Zustand. Toggle each on or off; all 32 combinations are supported and tested.
- Dependency installation — pick any package manager found on your
PATH(npm / pnpm / yarn, with the one matching an existing lockfile preselected), or skip installation.
Every prompt has a flag, so the CLI can run without a TTY:
rv create my-app --features all --install npm
rv create my-app --features typescript,tailwind --install none
rv init --name my-app --features router,zustand --install pnpm --conflicts keep| Option | Commands | Values |
|---|---|---|
-f, --features <list> |
create init |
Comma-separated: typescript, tailwind, router, eslint, zustand — or all / none |
--install <manager> |
create init |
npm, pnpm, yarn, or none to skip installation |
--name <package-name> |
init |
Package name (defaults to existing package.json or the directory name) |
--conflicts <strategy> |
init |
overwrite, keep, or cancel |
--no-git |
create |
Skip the automatic git init |
--dry-run |
create init add update |
Preview the changes without writing anything |
Feature aliases are accepted: ts, tailwindcss, react-router,
react-router-dom, lint, prettier, store.
| Feature | What gets added |
|---|---|
| TypeScript | Strict *.tsx sources, tsconfig.json project references (app + node), a typecheck script, tsc -b in the build script |
| Tailwind CSS (v4) | @tailwindcss/vite plugin in vite.config, @import 'tailwindcss' in src/index.css — no tailwind.config.js or PostCSS needed |
| React Router (v7) | src/router, a layout with navigation, Home and About pages; non-index routes are code-split via route-level lazy |
| ESLint + Prettier | eslint.config.js (flat config, TS-aware when TypeScript is selected), .prettierrc.json, and lint, lint:fix, format, format:check scripts |
| Zustand | src/store/useCounter plus a working counter component on the start page |
Features compose — for example TS + Tailwind + Router + Zustand gives you a typed router with a Tailwind-styled counter on the home page out of the box.
If you let rv run the install, start coding right away; otherwise:
cd my-app
npm install
npm run devGenerated scripts: dev, build, preview, plus typecheck (TypeScript)
and lint / format (ESLint + Prettier) when selected.
Template dependencies are pinned to a known-good set in the CLI itself (see
src/package-json.js), so the project you generate
today is reproducible and independent of transient latest tags on npm.
When rv updates, the pinned versions move forward in lockstep.
Current defaults:
- React 19 · React DOM 19
- Vite 8 ·
@vitejs/plugin-react6 - TypeScript 6 (when TS selected)
- Tailwind CSS 4 ·
@tailwindcss/vite4 - React Router 7 (when Router selected)
- Zustand 5 (when Zustand selected)
- ESLint 10 flat config ·
typescript-eslint8 · Prettier 3 (when ESLint selected)
Every release is verified by a smoke test that scaffolds, installs, builds, type-checks, and lints four project profiles (minimal/complete × JS/TS).
pnpm install
pnpm test # unit tests (node --test)
pnpm smoke # scaffold + install + build real projects
pnpm verify:release # tests + smoke + npm pack --dry-runSee CONTRIBUTING.md for the pull-request workflow.