Docs / Project structure
Project structure
The starter follows the Next.js App Router with two route groups: (public) for marketing/content pages and studio for the gated backoffice. The split is the most important convention to internalize.
Top-level layout #
app/
(public)/ — marketing site, anyone can view
layout.tsx — public chrome (SiteHeader, SiteFooter, PageBackground)
page.tsx — homepage
... — other public routes
studio/ — backoffice, auth-gated
layout.tsx — studio chrome + session check
... — admin pages
sign-in/ — auth flow (public, no public chrome)
api/ — route handlers (auth callbacks, etc.)
layout.tsx — root layout (font, html, body)
globals.css
components/
ui/ — generic primitives (Button, Input, …)
section.tsx — Container + Section layout primitives
typography.tsx — Display, Heading, Lede, Eyebrow, Pull
site-header.tsx, site-footer.tsx
...
lib/
auth.ts — better-auth setup shared by client + server
auth-server.ts — server-only auth helpers
auth-client.ts — client-side auth helpers
utils.ts — `cn()` and other tiny helpers
db/ — Drizzle schema + migration output
docker/ — production Dockerfile + nginx
content/ — file-based content (markdown, JSON, etc.)Why two route groups #
Route groups ((public) and studio) let each section have its own layout.tsx — its own chrome, its own background, its own session check — without affecting the URL. The home page is /, not /(public)/. The backoffice is /studio, not /(studio)/.
This matters because the public site and the backoffice are visually different applications but share the same component library. Two route groups + two layouts is how you keep them coherent without duplicating chrome.
Where to add things #
- A new marketing page: create
app/(public)/<your-route>/page.tsx. It inherits the public chrome automatically. - A new backoffice page: create
app/studio/<your-route>/page.tsx. It inherits the studio layout, which checks the session and 401s if you're not signed in. - A new shared component: drop it in
components/. If it's a generic primitive (button, input),components/ui/. - A new server-only helper: drop it in
lib/. Useimport "server-only"at the top if it must never run on the client.
Things that aren't there #
The starter does not include analytics, feature flags, error tracking, a CMS adapter, or a queue worker. Add them when you need them; each is one or two files in lib/ and an env var.