Acme Store Docs
Getting Started

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.

CapabilityTurns on when you setDefault behavior without it
Stripe paymentsSTRIPE_SECRET_KEY (+ STRIPE_WEBHOOK_SECRET for order recording)Quote-request checkout only; no Checkout button
Transactional emailRESEND_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.

PackageVersionRole
next14.2.4React framework — App Router, server components, route handlers, next/image
react / react-dom^18UI runtime
typescript^5Static typing across the whole codebase (tsc --noEmit in CI)
tailwindcss^3.4.1Utility-first CSS; brand tokens registered in tailwind.config.ts
tailwindcss-animate^1.0.7Tailwind plugin powering component animations (registered as a plugin in tailwind.config.ts)
tailwind-merge^2.3.0Merges conflicting Tailwind classes; backs the cn() utility
clsx^2.1.1Conditional class-name construction
class-variance-authority^0.7.0Typed component variants for the UI kit
zod^3.23.8Runtime schema validation (forms, API payloads, email schemas)
react-hook-form^7.51.5Form state and validation
@hookform/resolvers^3.6.0Bridges Zod schemas into react-hook-form
framer-motion^11.2.10Animations across the storefront
lucide-react^0.394.0Icon set
date-fns^3.6.0Date 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:

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 packageVersion
@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:

PackageVersionRole
vaul^0.9.1Drawer component (mobile sheets)
react-day-picker^8.10.1Calendar / date picker
react-input-mask^2.0.4Masked 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:

FamilySourceCSS variableTailwind utility
Bricolage Grotesquenext/font/google--font-displayfont-display
Geist Sansgeist/font/sans (geist ^1.7.2)--font-sansfont-sans (body default)
Geist Monogeist/font/mono (geist ^1.7.2)--font-monofont-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.

PackageVersionRole
firebase^10.12.2Client SDK — Auth (Google + passwordless email-link), Firestore, Storage
firebase-admin^14.1.0Server SDK — verifies ID tokens, records orders, powers the seed script
react-firebase-hooks^5.1.1React 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.

PackageVersionRole
@prisma/client^7.8.0Prisma runtime client (Postgres adapter)
prisma^7.8.0Prisma CLI / schema tooling (dev dependency)
@prisma/adapter-pg^7.8.0Prisma driver adapter for pg
pg^8.22.0PostgreSQL driver
@supabase/supabase-js^2.109.0Supabase (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

PackageVersionRole
stripe^22.3.0Server-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

PackageVersionRole
resend^3.3.0Sends transactional email
react-email^2.1.4Local email dev/preview tooling
@react-email/components^0.0.19React 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.

PackageVersionRole
fumadocs-ui13.4.10Docs UI shell and components
fumadocs-core13.4.10Docs routing / search primitives
fumadocs-mdx10.0.2MDX content pipeline (postinstall hook)
@types/mdx^2.0.14MDX type definitions

Testing — Vitest

PackageVersionRole
vitest^4.1.9Unit 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:

Terminal
npm test          # CI mode (vitest run)
npm run test:watch

Tooling scripts

The scripts block in package.json ties the stack together:

CommandRunsPurpose
npm run devnext devDevelopment server
npm run buildnext buildProduction build
npm run startnext startServe the production build
npm run lintnext lintESLint (next/core-web-vitals)
npm run typechecktsc --noEmitType-check without emitting
npm testvitest runUnit suite (CI)
npm run test:watchvitestWatch-mode tests
npm run seednode scripts/seed.mjsSeed demo catalog data
npm run prisma:generateprisma generateGenerate the Prisma client (Postgres backend)
npm run prisma:pushprisma db pushPush the Prisma schema to DATABASE_URL

Linting uses eslint ^8 with eslint-config-next 14.2.4; PostCSS (^8) drives the Tailwind build.

Next steps

On this page