create-rv
v0.1.0
Published
Scaffold a Vite+React app with the rollout stack: Vite, TS/JS, Tailwind v4, shadcn/ui, TanStack Router/Query, Zustand, RHF+Zod, ESLint, Prettier.
Maintainers
Readme
create-rv
A zero-config CLI that scaffolds a Vite + React 19 + Tailwind v4 app with the
rollout stack pre-wired: router, state, data, forms, validation, shadcn/ui,
lint, and format. Choose TypeScript or JavaScript and one of three routers
interactively, or skip the prompts with flags.
what you get
- Vite + React 19 + TypeScript (or JavaScript) with
@/*path alias - Tailwind v4 wired via
@tailwindcss/vite— notailwind.config.js, nopostcss.config.js, just@import "tailwindcss"insrc/index.css - Router — pick one:
tanstack— TanStack Router (file-based, type-safe, vite plugin, generatesrouteTree.gen.ts)react-router— React Router v8 (declarative:BrowserRouter+Link/Route/Routesimported fromreact-router)none— single-page shell
- shadcn/ui (primed) —
components.json,cn()util,Button/Card/Inputcomponents, oklch theme tokens,tsxflag incomponents.jsonflips with language - TanStack Query —
QueryClientProviderwired for the chosen router - Zustand — sample store at
src/lib/store.ts - React Hook Form + Zod — example form at
src/components/ExampleForm.tsx - ESLint — flat config, Vercel/TS style + import sort + tailwind
- Prettier — with
prettier-plugin-tailwindcss - 4 package managers —
npm,pnpm,yarn,bun - Optional
git initand dependency install
usage
interactive
npx create-rv my-appYou'll be prompted for project name, package manager, language, router, and extras. Press Enter at the router prompt to accept the default (React Router v8).
non-interactive (CI / scripts)
npx create-rv my-app \
--pkg pnpm \
--lang ts \
--router react-router \
--with query,zustand,rhf,zod,eslint,prettier,shadcn \
--install \
--yes--yes accepts all defaults and skips prompts.
flags
| flag | values | default |
| --- | --- | --- |
| --pkg | npm | pnpm | yarn | bun | npm |
| --lang | ts | js | ts |
| --router | tanstack | react-router | none | react-router |
| --with | comma-separated extras | all extras |
| --no-git | skip git init | git inits by default |
| --install | run install after scaffold | off |
| -y, --yes | accept all defaults, no prompts | off |
Available extras: query, zustand, rhf, zod, eslint, prettier, shadcn.
after scaffold
cd my-app
pnpm devAdd more shadcn components:
pnpm dlx shadcn@latest add dialogLint and format (if included):
pnpm lint
pnpm formathow it works
- Parse CLI args via
commander. - Prompt for any missing values via
@clack/prompts(skipped if every choice was provided as a flag or--yesis set). - Copy
templates/base/<lang>/(Vite + React skeleton) into the destination, then dotfiles fromtemplates/base/. - Overlay the chosen router from
templates/extras/router-*/and install its package viasrc/installers/router-*.ts. - Install each selected extra by copying its template overlay and adding
deps + scripts to
package.json. - Rewrite to JS if
--lang js: rename.ts→.js,.tsx→.jsx, drop.d.tsandtypescriptdeps, striptscfrombuild. - Optionally
git initand run install. - Render the generated project's
README.mdfrom a__STACK_LIST__/__SCRIPTS_EXTRA__template.
Templates live in templates/base/{ts,js}/ and templates/extras/*/. Each
extra has an installer in src/installers/ that copies its overlay and mutates
package.json.
router notes
- TanStack Router is file-based. Routes live in
src/routes/; the vite plugin generatesrouteTree.gen.tson dev/build.<RouterProvider>wraps the app inmain.tsx. - React Router v8 uses the declarative API:
BrowserRouterwraps<App />inmain.tsx, andApp.tsxuses<Routes>/<Route>/<Link>. Everything is imported fromreact-router(in v8 the declarative API is in the root package;react-router-domis removed).
project layout
src/
cli.ts # commander setup + flag parsing
prompts.ts # clack interactive prompts
scaffold.ts # main scaffold orchestration
options.ts # types + valid value lists
copyDotfiles.ts # walker that copies dotfiles (cpSync skips them on node 25)
installers/ # one file per extra; mutates pkg + copies overlay
templates/
base/{ts,js}/ # Vite+React skeleton
extras/<name>/ # overlay files copied into the project
scripts/
smoke.mjs # scaffolds 4 variants into /tmp and asserts filesdev
pnpm install
pnpm build # tsc -> dist/
node dist/index.js /tmp/demo --pkg npm --with shadcn --no-git --yes
node scripts/smoke.mjs # scaffolds 4 variants, asserts filesSmoke covers 4 variants: ts × tanstack, js × tanstack,
ts × react-router + shadcn, js × react-router + shadcn, plus a
regression check that react-router main.tsx wraps <App /> in
<BrowserRouter> imported from react-router.
