Project Structure
How the Next.js template is organized.
The template is a Next.js 16 App Router project. Routes live under
src/app/[locale], grouped by audience, with API handlers in src/app/api.
Everything else (components, config, libraries, i18n, content) sits beside it in
src/.
Top-level layout
Route groups
Every page is nested under src/app/[locale] so the active locale is available
to the whole tree (see Internationalization).
Inside it, route groups ((name) folders that don't add a URL segment)
split the app by audience and share a layout:
| Group | URL prefix | Contains |
|---|---|---|
(marketing) | / | Landing, blog, legal, about, contact, AI demo, pay-success |
(dashboard) | /dashboard, /admin, /setting, /account | Authenticated app surfaces |
(docs) | /docs | Fumadocs-rendered documentation |
auth | /auth/* | Login, register, forgot/reset password, confirm |
auth is a plain folder, not a route group, so its pages live at
/auth/login, /auth/register, and so on. Only (marketing),
(dashboard), and (docs) use the parenthesised group syntax.
Dynamic content lives behind catch-all and dynamic segments: docs render through
(docs)/docs/[[...slug]], and blog/legal posts resolve their slugs from MDX in
content/.
API routes
Server handlers live in src/app/api, grouped by concern:
| Route | Purpose |
|---|---|
api/auth/[...all] | Better Auth catch-all handler |
api/user/{profile,settings,subscription,billing} | Authenticated user data |
api/admin/users | Role-guarded admin user management |
api/payment/{create,query,notify/creem} | Creem checkout + signed webhook |
api/storage/upload-image | Authenticated image upload |
api/ai/{image,audio,video} (+ /query) | Replicate generation + polling |
api/chat | OpenAI streaming chat (Vercel AI SDK) |
api/contact | Contact form email |
Auth, validation, and response shapes are applied per route, not enforced
globally — patterns vary between handlers. Read the specific route before
relying on a guard. For example, the /admin/users API checks the admin
role, but page-level protection in proxy.ts only verifies a session cookie.
Supporting directories
| Directory | Role |
|---|---|
src/components | UI primitives (ui/), feature, layout, and motion components |
src/config | App configuration (site, footer, routes, pricing, sidebars) |
src/lib | Server logic: auth, db, email, payment, ai, source, utils |
src/i18n | next-intl routing, request, navigation |
messages | Translation catalogs (en.json, zh.json) |
content | MDX for docs, blog, and legal pages |
public | Static assets (and runtime uploads/, see Storage) |
Request flow: src/proxy.ts
The template uses Next.js 16's proxy.ts (the renamed middleware.ts) for two
jobs on every non-API request:
- Locale handling — runs next-intl's middleware to detect and prefix the locale.
- Protected-path redirect — for
/dashboard,/admin,/setting, and/account, it checks for a Better Auth session cookie and redirects to/auth/login?callbackUrl=…when it's missing.
const protectedPaths = ["/dashboard", "/admin", "/setting", "/account"];
// ...
export const config = {
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};The cookie check is a redirect for unauthenticated traffic, not an authorization boundary. It does not validate the session or the user's role — always re-check the session (and role) inside the API route or server component that returns sensitive data.
Content & generated source
Documentation, blog, and legal content are MDX collections defined in
source.config.ts (Fumadocs MDX) and loaded through src/lib/source.ts.
Fumadocs generates a .source/ directory at build time — it is regenerated from
content/, so don't edit it by hand.
Next.js 16 conventions to know
- Server Components by default. Files are RSC unless they start with
"use client". Keep data fetching and secrets on the server. - Async request APIs.
params,searchParams,headers(), andcookies()are awaited (e.g.const { locale } = await params). - React Compiler is enabled, so manual
useMemo/useCallbackare rarely needed.
