Tech Stack
The frameworks, libraries, and services the boilerplate is built on and why.
This boilerplate is a modular, quote-based ecommerce starter built on Next.js 14 with the App Router. Everything you need to browse products, build a cart, and submit quote requests works out of the box against Firebase — and every heavier dependency (SQL databases, Stripe payments, transactional email) is optional and env-gated, so you only pay for the complexity you actually turn on.
All versions below are copied verbatim from package.json. Treat that file as the source of truth if a version ever drifts from this page.
The optional / env-gated philosophy
The store's default path needs no payment processor, no SQL database, and no email provider. Those integrations activate only when you set their environment variables:
Leave a service's env vars unset and that service is invisible — the code paths that depend on it are skipped, not broken. This keeps the "afternoon rebrand" promise intact: clone, add Firebase credentials, and you have a working storefront.
| Capability | Turns on when you set | Default behavior without it |
|---|---|---|
| Stripe payments | STRIPE_SECRET_KEY (+ STRIPE_WEBHOOK_SECRET for order recording) | Quote-request checkout only; no Checkout button |
| Transactional email | RESEND_API_KEY (+ RESEND_FROM_EMAIL) | Quotes submit normally; emails are skipped |
| SQL backend (Supabase / Postgres) | COMMERCE_BACKEND + NEXT_PUBLIC_COMMERCE_BACKEND (+ DATABASE_URL) | Firebase is the zero-config default — no Prisma account, no DATABASE_URL needed |
The store is quote-based by default: browse → cart → submit a quote request, no payment required. Stripe, Resend, and the SQL adapters are additive layers on top of that core. See environment variables for the full list.
Core stack
These are the load-bearing dependencies every install uses.
| Package | Version | Role |
|---|---|---|
next | 14.2.4 | React framework — App Router, server components, route handlers, next/image |
react / react-dom | ^18 | UI runtime |
typescript | ^5 | Static typing across the whole codebase (tsc --noEmit in CI) |
tailwindcss | ^3.4.1 | Utility-first CSS; brand tokens registered in tailwind.config.ts |
tailwindcss-animate | ^1.0.7 | Tailwind plugin powering component animations (registered as a plugin in tailwind.config.ts) |
tailwind-merge | ^2.3.0 | Merges conflicting Tailwind classes; backs the cn() utility |
clsx | ^2.1.1 | Conditional class-name construction |
class-variance-authority | ^0.7.0 | Typed component variants for the UI kit |
zod | ^3.23.8 | Runtime schema validation (forms, API payloads, email schemas) |
react-hook-form | ^7.51.5 | Form state and validation |
@hookform/resolvers | ^3.6.0 | Bridges Zod schemas into react-hook-form |
framer-motion | ^11.2.10 | Animations across the storefront |
lucide-react | ^0.394.0 | Icon set |
date-fns | ^3.6.0 | Date formatting and math |
Why Next.js 14 + App Router
The App Router lets server components fetch catalog data and resolve locale, currency, and theme server-side on first paint (no flash), while route handlers under src/app/api/* provide the Public Store API, the checkout endpoint, and the commerce RPC bridge. Server-rendered pages also make the SEO pack (sitemap, robots, JSON-LD) straightforward.
UI kit — shadcn/ui + Radix UI
The interface is built from shadcn/ui components — 30+ primitives copied into src/components/ui/ — which wrap Radix UI for accessible, unstyled behavior and style it with Tailwind. Because the components live in your repo (not node_modules), you own and can edit every one.
The shadcn configuration lives in components.json:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}Radix packages are pulled in per primitive. A representative selection from package.json:
| Radix package | Version |
|---|---|
@radix-ui/react-dialog | ^1.0.5 |
@radix-ui/react-dropdown-menu | ^2.0.6 |
@radix-ui/react-navigation-menu | ^1.1.4 |
@radix-ui/react-select | ^2.0.0 |
@radix-ui/react-toast | ^1.1.5 |
@radix-ui/react-tabs | ^1.0.4 |
@radix-ui/react-popover | ^1.0.7 |
@radix-ui/react-tooltip | ^1.0.7 |
The full set (accordion, alert-dialog, aspect-ratio, avatar, checkbox, collapsible, label, menubar, radio-group, scroll-area, separator, slot, switch, and more) is listed in package.json. Two related UI helpers ship alongside them:
| Package | Version | Role |
|---|---|---|
vaul | ^0.9.1 | Drawer component (mobile sheets) |
react-day-picker | ^8.10.1 | Calendar / date picker |
react-input-mask | ^2.0.4 | Masked inputs (e.g. phone) |
Fonts
Typography combines two families, wired through src/app/[locale]/layout.tsx and exposed as CSS variables the Tailwind fontFamily config reads:
| Family | Source | CSS variable | Tailwind utility |
|---|---|---|---|
| Bricolage Grotesque | next/font/google | --font-display | font-display |
| Geist Sans | geist/font/sans (geist ^1.7.2) | --font-sans | font-sans (body default) |
| Geist Mono | geist/font/mono (geist ^1.7.2) | --font-mono | font-mono |
Geist ships as the official geist npm package (it is not in this Next version's Google-font manifest); globals.css aliases its --font-geist-sans / --font-geist-mono variables onto the --font-sans / --font-mono design tokens.
Firebase — the default backend
Firebase is the zero-config default: it provides authentication, the Firestore document database, and file storage, with realtime carts and an all-client data flow. No SQL setup, no Prisma account.
| Package | Version | Role |
|---|---|---|
firebase | ^10.12.2 | Client SDK — Auth (Google + passwordless email-link), Firestore, Storage |
firebase-admin | ^14.1.0 | Server SDK — verifies ID tokens, records orders, powers the seed script |
react-firebase-hooks | ^5.1.1 | React bindings for auth/Firestore state |
Firebase Auth is the identity provider regardless of which database you choose — swapping backends never changes how users sign in. See choosing a backend and the Firestore data model for details.
Optional SQL backends
The commerce layer (src/lib/commerce) is backend-agnostic: Firebase is one adapter, and two SQL adapters ship behind the same interface. These are server-side only and activate via COMMERCE_BACKEND / NEXT_PUBLIC_COMMERCE_BACKEND.
| Package | Version | Role |
|---|---|---|
@prisma/client | ^7.8.0 | Prisma runtime client (Postgres adapter) |
prisma | ^7.8.0 | Prisma CLI / schema tooling (dev dependency) |
@prisma/adapter-pg | ^7.8.0 | Prisma driver adapter for pg |
pg | ^8.22.0 | PostgreSQL driver |
@supabase/supabase-js | ^2.109.0 | Supabase (Postgres) client |
You never install or configure any of this on the default path. If a deploy prompts you for a Prisma / Prisma Postgres API key, you accidentally triggered Prisma's hosted-database onboarding — set a plain postgresql:// DATABASE_URL, or simply don't run prisma generate.
Prisma tooling is exposed through two npm scripts: npm run prisma:generate and npm run prisma:push.
Optional payments — Stripe
| Package | Version | Role |
|---|---|---|
stripe | ^22.3.0 | Server-side Stripe SDK for Checkout Sessions and webhook verification |
With STRIPE_SECRET_KEY set, carts where every item has a price gain a Checkout button beside the quote flow. POST /api/checkout re-prices items server-side (client prices are never trusted) and creates a variant-aware Checkout Session; POST /api/webhooks/stripe (needs STRIPE_WEBHOOK_SECRET) records the paid order. See payments.
Optional email — Resend + react-email
| Package | Version | Role |
|---|---|---|
resend | ^3.3.0 | Sends transactional email |
react-email | ^2.1.4 | Local email dev/preview tooling |
@react-email/components | ^0.0.19 | React components for building email templates |
With no RESEND_API_KEY, the store runs normally and skips email. When enabled, quote submissions notify your team and confirm receipt to the customer; the template lives in src/components/emailing/QuoteEmailTemplate.tsx. See email notifications.
Documentation — Fumadocs
These docs (the page you are reading) are built with Fumadocs, wired via a postinstall step that runs fumadocs-mdx.
| Package | Version | Role |
|---|---|---|
fumadocs-ui | 13.4.10 | Docs UI shell and components |
fumadocs-core | 13.4.10 | Docs routing / search primitives |
fumadocs-mdx | 10.0.2 | MDX content pipeline (postinstall hook) |
@types/mdx | ^2.0.14 | MDX type definitions |
Testing — Vitest
| Package | Version | Role |
|---|---|---|
vitest | ^4.1.9 | Unit test runner |
Tests run in a plain Node environment — no browser, emulators, or Firebase credentials required — and live in tests/, covering the pure seams of the app (rate limiter, email schemas, Public Store API helpers, cart merging, the commerce surface, and the cn utility). Globals are off, so tests import their APIs explicitly (import { describe, it, expect } from "vitest").
Run them with:
npm test # CI mode (vitest run)
npm run test:watchTooling scripts
The scripts block in package.json ties the stack together:
| Command | Runs | Purpose |
|---|---|---|
npm run dev | next dev | Development server |
npm run build | next build | Production build |
npm run start | next start | Serve the production build |
npm run lint | next lint | ESLint (next/core-web-vitals) |
npm run typecheck | tsc --noEmit | Type-check without emitting |
npm test | vitest run | Unit suite (CI) |
npm run test:watch | vitest | Watch-mode tests |
npm run seed | node scripts/seed.mjs | Seed demo catalog data |
npm run prisma:generate | prisma generate | Generate the Prisma client (Postgres backend) |
npm run prisma:push | prisma db push | Push the Prisma schema to DATABASE_URL |
Linting uses eslint ^8 with eslint-config-next 14.2.4; PostCSS (^8) drives the Tailwind build.