Documentation
Project Structure
How Motoko Base is organized.
Open inChatGPT (opens in a new tab)Claude (opens in a new tab)Cursor (opens in a new tab)How Motoko Base is organized.
Motoko Base separates routes from product code. Routes in src/app/ stay thin; everything you build lives in src/features/.
Directory overview
src/
├── app/ # Next.js routes — layouts, pages, API handlers
├── features/ # Product features (UI, actions, queries, schemas)
├── components/ # Shared UI — shadcn primitives + dashboard shell
├── lib/ # Shared infrastructure (auth, db, billing, email, …)
└── assets/ # Static images and logos| Folder | Purpose |
|---|---|
app/ | URL structure only. Fetch data, check auth, render a feature component. |
features/ | Where your SaaS logic lives — one folder per feature. |
components/ui/ | Reusable shadcn/ui primitives (Button, Card, Dialog, …). |
components/layout/ | Dashboard shell, sidebar, headers — shared across pages. |
lib/ | Server-only infrastructure. Features call into lib/, not the other way around. |
features/dashboard/config/ | Dashboard sidebar and settings navigation (nav.ts, settings-nav.ts). |
assets/ | SVG logos, images — imported where needed. |
app/ — routes only
Pages should compose feature code, not contain business logic:
// src/app/dashboard/links/page.tsx
import { LinksPage } from "@/features/links/components/links-page";
import { listLinksForUser } from "@/features/links/queries";
import { getCachedSession } from "@/lib/auth/session";
export default async function Page() {
const session = await getCachedSession();
const result = await listLinksForUser(session!.user!.id);
return <LinksPage initialLinks={result.items} />;
}Use Route Handlers (app/api/...) for auth catch-all, webhooks, and streaming endpoints. Use Server Actions inside features for CRUD.
features/ — your product
Each feature is a self-contained folder. Look at links, storage, or settings for real examples.
Typical layout:
src/features/my-feature/
├── components/ # Page UI and sub-components
├── actions.ts # Server Actions ("use server")
├── queries.ts # Database reads
├── schemas.ts # Zod validation
├── config.ts # Constants, limits, feature flags
└── lib/ # Feature-specific helpers (optional)lib/ — shared infrastructure
| Module | What it provides |
|---|---|
lib/auth/ | Better Auth, session helpers |
lib/db/ | Drizzle client and table schemas |
lib/billing/ | Provider-agnostic billing API |
lib/email/ | Resend, templates |
lib/analytics/ | PostHog events |
lib/monitoring/ | Sentry |
Database tables live in lib/db/schema/. Export new tables from lib/db/schema/schema.ts.
components/ and dashboard config
- Add a shadcn component →
components/ui/ - Add a nav item →
features/dashboard/config/nav.ts - Do not put feature-specific UI here — keep it in the feature folder.
Rules of thumb
- Server-first — auth and database access stay on the server
- Ownership — every mutation uses
id + userId - Validation — Zod at the server boundary (actions and route handlers)
- Soft-fail — optional integrations (billing, R2, AI) should not crash unrelated pages
- Copy, don't abstract — duplicate a demo feature and edit it; no framework layers
For agent-oriented rules, see AGENTS.md in the repository root.
Next steps
Installation — if you have not set up the project yet.
Introduction — overview of what ships in the starter.