Every paid Next.js starter says it "uses shadcn/ui". Almost none of them tell you what happens when you run npx shadcn@latest add inside their repo six months later. This page answers that, then argues honestly against the free official starter — the one most people should probably use.
SaaS Starter does not generate its UI with the shadcn CLI. The ten components in src/components/ui/ are hand-written on shadcn's token contract and use shadcn's exact cn() helper. Because the repo ships a components.json, the shadcn CLI, the registry and the MCP all work against it, and anything you pull in inherits the theme without edits. There is no Radix and no class-variance-authority in the box — Radix arrives only when you add a component that needs it.
Search for a Next.js SaaS starter with shadcn and you get lists. Which kits use shadcn, how many stars they have, what they cost. Fine. But that is not the thing you are actually nervous about.
The real worry is narrower. You chose shadcn deliberately — you wanted to own the components, paste them from a registry, and let your editor's MCP pull new ones on demand. Then you consider paying $149 for a starter, and the obvious risk is that the kit has quietly built its own UI layer on top, so the registry, the CLI and the MCP stop being useful the day after you buy. That is why the free official starter feels like the safe default: it is canonical shadcn, and nothing sits between you and the registry.
That fear is reasonable. It is also testable in about sixty seconds, which is what the rest of this page is about. We ran the test on our own repo and published what came out, including the part that did not work at first.
Let's be exact, because this is the kind of claim buyers verify thirty seconds after purchase and request a refund over. Three separate statements, all true:
1. Our components were not generated by shadcn.
button.tsx, card.tsx, input.tsx, modal.tsx, badge.tsx, avatar.tsx, skeleton.tsx, spinner.tsx, textarea.tsx and toggle-theme.tsx were written by hand. They use a plain object for variants, not cva, and they wrap native elements, not Radix primitives. If you came here believing you were buying vendored shadcn source, you are not, and we would rather you know now.
2. They sit on shadcn's token contract.
src/lib/utils.ts exports shadcn's exact cn() — twMerge(clsx(inputs)), character for character. Our buttons read --muted, --accent-foreground, --destructive and friends. Those are shadcn's canonical variable names, not ours.
3. The CLI, registry and MCP work against the repo.
components.json ships in the root, pointing at src/styles/globals.css and the @/lib/utils alias. That file is the thing the CLI, the registry and the shadcn MCP all read. Without it, none of them can see your project.
Here is the whole proof. Clone the repo, then:
$ npx shadcn@latest add dialog ✔ Checking registry. ✔ Installing dependencies. ✔ Created 1 file: - src/components/ui/dialog.tsx
The interesting part is not that it succeeded. It is the top of the file it wrote:
import { Dialog as DialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
A stock registry component resolved our hand-rolled cn() and composed our hand-rolled Button. No edits. That is the whole argument for the token contract: the boundary between "our code" and "registry code" is just CSS variables, so the two sides compose instead of fighting.
We also checked that the theme genuinely lands rather than assuming it. Compiling the stylesheet and grepping the output, all seventeen utilities the dialog relies on resolve to our variables:
.bg-popover { background-color: var(--popover); }
.bg-destructive { background-color: var(--destructive); }
The part that did not work first time
Running that test is how we found a real hole. The generated dialog uses animate-in, fade-in-0 and zoom-in-95. Tailwind v4 core does not ship those keyframes, so the dialog mounted correctly themed but completely unanimated. The fix is one dependency (tw-animate-css) and one @import line in globals.css, and both are already in the repo as of 16 July 2026 — so shadcn add now works for animated components too. We are documenting it because a "zero edits" claim that quietly needed one edit is exactly the kind of thing that earns a refund.
One honest consequence. Because we do not vendor Radix, adding a registry component that needs it will pull radix-ui into your dependency tree at that moment. We think that is the right default — you get primitives when you ask for them, not before. If you would rather every primitive be present on day one, the official starter already does that, and that is a legitimate reason to prefer it.
Most comparison tables on paid-starter sites quietly omit nextjs/saas-starter, which is the single most likely thing you are comparing against. Ours used to as well. It is here now, in the first column, because a comparison that hides the obvious alternative is not a comparison.
The last row is the one no competitor publishes: when each kit last shipped a commit. For closed-source kits that number is simply not verifiable from outside, and we say so rather than guessing.
| nextjs/saas-starter (official, free) | SaaS Starter ($149) | Typical paid kit ($199–$299) | |
|---|---|---|---|
| Price / licence | $0 — MIT | $149 — commercial, unlimited projects at $299 | $199–$299 — commercial |
| GitHub stars | 15,957 | Private repo — not comparable | Private repos — not comparable |
| Next.js / React | 15.6.0-canary.59 / 19.1.0 | 15.5.14 / 19.2.4 (both stable) | Varies — check package.json |
| Database / ORM | Postgres + Drizzle | Postgres + Prisma | Prisma or Supabase |
| Auth | Hand-rolled JWT cookie | Clerk (hosted) | NextAuth or Supabase |
| shadcn/ui depth | Canonical — generated by the CLI, Radix + cva vendored in | Compatible — hand-rolled on the token contract, components.json ships, CLI/registry/MCP work, no Radix until you add one | Usually canonical, sometimes wrapped |
| Payments | Stripe + Customer Portal | Stripe + webhooks + Customer Portal | Stripe |
| Admin panel + MRR | No | Yes — users, metrics, MRR | Rare or basic |
| Transactional email | No | Resend + React Email templates | Usually |
| Blog | No | MDX built-in | Sometimes |
| Tests | No test script | 39 Vitest tests, all green | Rare |
| i18n | No | next-intl, 8 locales | Rare |
| Teams / RBAC | Owner & Member roles, activity log | User & Admin roles only — no multi-tenant teams | Varies |
| Docs | README | 6 web docs pages + AI prompt library | README or docs site |
| Last commit | 11 December 2025 (7 months ago) | 15 July 2026 | Closed source — unverifiable |
Official starter figures read from the GitHub API and package.json on main, 16 July 2026. Stars and commit dates move; re-check them before you decide. The "typical paid kit" column is deliberately vague because we will not publish specific numbers about closed-source competitors we cannot verify.
The free starter's README does not oversell itself. It says, verbatim:
"While this template is intentionally minimal and to be used as a learning resource, there are other paid versions in the community which are more full-featured."
— nextjs/saas-starter README
We are not going to argue with that. It is an excellent piece of code and it does exactly what it says. It gets you auth, Stripe, Drizzle and real shadcn/ui for nothing. The honest pitch for paying us is not that it is bad — it is that it deliberately stops at the point where the boring six weeks begin. Here is that delta, with the time it actually took us to build each piece:
| What you'd build yourself | Our build time | The part that eats the hours |
|---|---|---|
| Admin panel + MRR metrics | ~14 h | Not the tables — reconciling MRR against Stripe when subscriptions change mid-period |
| Transactional email (Resend + React Email) | ~8 h | Templates are fast; making them survive Outlook and dark mode is not |
| MDX blog | ~6 h | Frontmatter, draft handling, RSS, and sane typography defaults |
| Vitest suite (39 tests) | ~10 h | Mocking Stripe and Clerk so tests run without network or keys |
| Dark mode (next-themes) | ~3 h | Killing the flash of wrong theme on first paint |
| i18n (next-intl, 8 locales) | ~16 h | Routing and hreflang, not translation |
| Docs site + AI prompt library | ~12 h | Writing, mostly. There is no shortcut |
That is roughly 69 hours, and that is our time on a stack we wrote and know by heart. Your number will be higher on unfamiliar code — assume something like 90 to 100 hours, and more if you have not wired Stripe webhooks before. At any freelance rate you would recognise, that is the entire argument. If your hourly rate is above about $2, $149 is cheaper than building it.
The inverse is also true and worth saying plainly: if those seven rows are things you do not need, the free starter is the correct choice and you should close this tab. A blog and eight locales are not worth $149 to someone validating an idea over a weekend.
We run Next.js 15.5.14 and React 19.2.4. Not Next.js 16. If a comparison page tells you the free official starter is on Next.js 16 and we are behind, check its package.json yourself — it pins next@15.6.0-canary.59. Both kits are on Next.js 15. Theirs is a canary build, ours is a stable release, and neither of those facts is worth much marketing noise.
Our position on Next.js 16: we will move when the ecosystem does, not before. A starter's job is to be boring on release day. Being one stable minor behind and fully green beats being on a canary that breaks Clerk or Prisma in a way you get to debug on our behalf.
The freshness number that actually matters is the last row of the matrix above. The free starter's most recent commit, as of 16 July 2026, is from 11 December 2025 — and it was a security bump (CVE-2025-55182). That is not an attack on it; unmaintained is the normal state of a repo whose stated job is teaching. But if you adopt it, understand that keeping it current is now your job, and that is a real cost you should price in next to $149.
Every kit in this search result lists what it has. None list what they lack, which is the cheaper and more useful list. Ours:
No multi-tenant teams
Roles are User and Admin. No organisations, no invitations, no per-seat billing. The free official starter actually beats us here with Owner/Member and an activity log.
Auth is Clerk, not yours
A third-party dependency with its own pricing beyond the free tier. If self-hosted auth is a requirement, do not buy this.
Prisma, not Drizzle
If you have already standardised on Drizzle or Supabase, swapping the ORM will cost you more than the kit saves.
Components are not vendored shadcn
Covered above. If you specifically want Radix primitives and cva already sitting in your repo, the official starter gives you that for free.
No usage-based or metered billing
Flat subscription tiers only. Metered billing is yours to build.
No public star count
It is a private repo, so you cannot audit the code before paying. That is a genuine disadvantage against a 15,957-star MIT repo, and the 14-day refund exists because of it.
If you are learning how auth and billing work, do not buy this — you would be paying to skip the education you are after. Clone the free one. This kit is for people who have already built this plumbing at least once, know exactly how long it takes, and would rather not do it a third time.
Not literally. The ten components in src/components/ui/ were written by hand, not generated by the CLI. They are built on shadcn's token contract and use shadcn's exact cn(), and the repo ships a components.json, so the CLI, registry and MCP all work against it. Registry components you add inherit the theme with no edits.
Yes, and you should test it before trusting us. npx shadcn@latest add dialog writes src/components/ui/dialog.tsx and it renders themed immediately, because globals.css maps our variables onto the utility names shadcn components expect.
It can, if the kit wraps or vendors its UI behind custom abstractions. Ours does not. There is no Radix and no class-variance-authority in package.json, so nothing intercepts a registry component. Radix enters your tree only when you add something that needs it.
For learning, yes — its own README says it is "intentionally minimal and to be used as a learning resource". It ships Postgres, Drizzle, Stripe and real shadcn/ui under MIT. It has no admin panel, no MRR metrics, no emails, no blog, no tests and no i18n, and as of 16 July 2026 its last commit was 11 December 2025.
No. It runs Next.js 15.5.14 and React 19.2.4, both stable. The free official starter is not on Next.js 16 either — it pins next@15.6.0-canary.59. Anyone claiming a Next.js 16 gap between the two is reading marketing copy rather than package.json.
Anyone who wants self-hosted auth instead of Clerk, anyone committed to Drizzle or Supabase, anyone who needs multi-tenant teams with per-seat billing, and anyone whose goal is to learn how auth and billing work rather than skip building them. In those cases the free official starter or a from-scratch build is the better call.
Then clone the free one — genuinely. If not, $149 gets you the Starter licence for one project, $299 covers unlimited projects, and both include 14-day refunds precisely because you cannot read the code first.
One-time payment · 14-day refund · Instant GitHub access · Read the docs before buying