rlc-stack
v1.0.0
Published
Personal Next.js 15 scaffolder — Prisma+tRPC or Convex, both with Clerk threaded into the right provider context.
Maintainers
Readme
rlc-stack
Personal Next.js 15 scaffolder. Pick Prisma + tRPC or Convex — Clerk is already threaded into the right provider context either way. Optional add-ons (shadcn/ui, Sentry, Stripe, UploadThing, PostHog, Resend, next-intl) are toggleable per project.
Usage
# interactive
npx rlc-stack
# zero-prompt
npx rlc-stack my-app --db prisma --prisma-flavor pg --addons shadcn,sentry --pm bun -yOutputs a Next.js project in ./my-app, runs <pm> install, runs
prisma generate (Prisma track), inits a git repo with an initial
commit, prints a next-steps banner, and opens .env.local in $EDITOR.
Flags
| flag | default | description |
| --- | --- | --- |
| --name <kebab> | (prompt) | project name; first positional arg also works |
| --db <prisma\|convex> | (prompt) | database / backend |
| --prisma-flavor <standard\|pg> | (prompt, Prisma only) | standard = new PrismaClient(); pg = @prisma/adapter-pg for Neon/Vercel serverless |
| --addons <a,b,c> | (prompt) | comma-separated, see addon matrix below |
| --pm <bun\|pnpm\|npm> | (auto-detect) | package manager |
| --skip-install | off | don't run <pm> install |
| --skip-git | off | don't git init |
| --skip-banner | off | suppress next-steps output |
| --skip-edit | off | don't open .env.local in $EDITOR |
| -y, --yes | off | accept defaults; minimize prompting |
| -h, --help | — | print help |
| -v, --version | — | print version |
Addon matrix
| addon | Prisma | Convex | adds |
| --- | :---: | :---: | --- |
| shadcn | ✓ | ✓ | shadcn/ui v3 + tailwindcss-animate + base Button/Input/cn |
| sentry | ✓ | ✓ | @sentry/nextjs + client/server/edge configs + instrumentation hook + withSentryConfig |
| stripe | ✓ | ✓ | stripe SDK + @stripe/stripe-js + signed webhook route at /api/webhooks/stripe |
| uploadthing | ✓ | — | uploadthing + @uploadthing/react + Clerk-gated file router. Hidden on Convex — Convex has native file storage. |
| posthog | ✓ | ✓ | posthog-js + <PostHogProvider> with Clerk identity sync + soft-nav pageview tracking |
| resend | ✓ | ✓ | resend SDK + @react-email/components + sample welcome email |
| i18n | ✓ | ✓ | next-intl + routing.ts + request.ts + messages/{en,es,fr}.json (manual [locale] adoption — see addon docs) |
What you get
Common (both tracks)
- Next.js 15.4 App Router + React 19 + TypeScript strict
- Tailwind 3.4 + base globals +
@/*path alias - Clerk (
<ClerkProvider>+clerkMiddleware+/sign-in&/sign-upcatchall routes) - Sonner toaster
- ESLint via
eslint-config-next tsconfig.json,_gitignore→.gitignore,.env.example+.env.localcomposed from each chosen module's env fragment
Prisma track
<ClerkProvider> ← Clerk session
<TRPCProvider> ← tRPC + TanStack Query
{children}// trpc/init.ts
export const createTRPCContext = cache(async () => {
const { userId } = await auth(); // from @clerk/nextjs/server
return { prisma, userId, ip }; // ← Clerk threaded into context
});privateProcedure enforces non-null userId and narrows the context type.
A sample whoami procedure ships in trpc/routers/_app.ts.
--prisma-flavor pg swaps src/lib/prisma.ts to use the @prisma/adapter-pg
adapter (designed for Neon / Vercel serverless) and adds previewFeatures =
["driverAdapters"] to the Prisma schema.
Convex track
<ConvexClientProvider> (src/components/providers/)
<ClerkProvider> ← Clerk session
<ConvexProviderWithClerk ← Convex receives Clerk's useAuth
client={convex}
useAuth={useAuth}>
{children}// convex/auth.ts
export async function requireAuth(ctx) {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new ConvexError({ code: "UNAUTHENTICATED" });
return { userId: identity.subject };
}convex/auth.config.ts declares Clerk as the JWT issuer. Native file
storage ships by default (convex/files.ts + src/app/upload-demo/page.tsx)
— ctx.storage.generateUploadUrl() → POST → saveFileRef mutation. No
UploadThing needed.
How the templating works
Per-project combinatorics (2 DBs × 7 addons = 128 combinations) are handled by fragment composition, not pre-baked combined templates:
- Copy
templates/base/files/→ project dir. - Copy
templates/nextjs-{prisma,convex}/files/→ project dir. - (Prisma + pg) Overlay
templates/prisma-adapter-pg/files/. - For each addon: overlay
templates/addons/<addon>/files/. - Compose
package.jsonfrom each chosen module'spackage.fragment.json(deep-merged, dep keys sorted, project name injected). - Compose
.env.example+.env.localfrom each module'senv.fragment. - Rename
_gitignore→.gitignore.
Conflict policy: later fragments win for conflicting dep versions.
Versions are pinned in fragments (no latest) for reproducibility.
Project layout
rlc-stack/
├── bin/index.js # CLI entry (shebang + ES module)
├── src/
│ ├── prompts.js # Inquirer prompt + flag parsing
│ ├── scaffold.js # File-tree composition orchestrator
│ ├── package-merge.js # Fragment merge + env composition
│ ├── post-scaffold.js # install / prisma generate / git / banner / editor
│ └── utils.js # parseArgs, validateProjectName, runCommand, etc.
├── templates/
│ ├── base/ # DB-agnostic Next.js + Tailwind + Clerk shell
│ ├── nextjs-prisma/ # Prisma + tRPC + Clerk threading
│ ├── nextjs-convex/ # Convex + Clerk threading + native file storage
│ ├── prisma-adapter-pg/ # PrismaPg overlay
│ └── addons/{shadcn,sentry,stripe,uploadthing,posthog,resend,i18n}/
└── test/ # node:test unit + integration testsDevelopment
git clone … rlc-stack
cd rlc-stack
npm install
npm test # full suite (39 tests, ~2s)
# Smoke scaffold a maxed-out Prisma project:
npm run smoke:prisma # scaffolds ./smoke-prisma
# ...or manually:
node ./bin/index.js my-test --db convex --pm npm --skip-install -y
npm run smoke:clean # remove smoke output dirs
# Local install for testing the bin:
npm link # then `rlc-stack` works from anywhereTroubleshooting
prisma generate failed during scaffold install
You probably haven't set DATABASE_URL yet. Set it in .env.local,
then run <pm> exec prisma generate. The scaffolder warns rather than
hard-failing in this case.
Convex project — TypeScript errors about convex/_generated/...
The scaffold ships stub files in convex/_generated/ so the project
typechecks before the first convex dev run. Once you run
<pm> convex dev, those stubs are overwritten with the real generated
types.
STRIPE_SECRET_KEY is not set; Stripe will fail at runtime
That warning is expected during next build if you haven't filled in
Stripe keys. The Stripe SDK is initialised at module-load with a fallback
key so the static build phase doesn't crash.
Sentry experimental.instrumentationHook warning
Removed — instrumentation is stable in Next.js 15. The instrumentation
file at src/instrumentation.ts is auto-loaded.
next lint is deprecated warning
Next.js 16 is dropping next lint. Migrate via:
npx @next/codemod@canary next-lint-to-eslint-cli .Adding more shadcn/ui components
bunx shadcn@latest add dialogcomponents.json is pre-configured.
License
MIT
