For AI agents: fetch the complete documentation index at /llms.txt, the full documentation corpus at /llms-full.txt, the agent skill at /skill.md, or this page as Markdown at /docs/web/first-feature.md (or send Accept: text/markdown).
Build your first feature
An end-to-end walk from schema to shipped screen.
Beginner walkthrough
Prerequisites: Read the two beginner guides below, finish Web local setup, and open this monorepo at its root in your editor. Web settings belong in apps/web/.env.local; Next.js does not read the root .env. /dashboard in the default fake mode is a no-database demo; /auth needs a migrated database.
Quick terms: a workspace is one team's separate users, data, and settings. The server is the trusted backend; keep provider keys out of browser and phone code. A migration is a versioned database change that creates or updates tables. An AI model is the engine that answers; the model catalog is the server's list of available model IDs and prices. A provider is an outside service such as an AI, email, or billing service. An entitlement is a grant that lets a workspace use a feature. A webhook is a message one service sends to another service's URL.
1. Open VS Code at the repository root and ask your coding agent to create apps/web/app/reports/page.tsx and apps/web/src/reports.ts. Give it the report fields and allowed roles; require server-side membership checks and a query filtered by workspaceId.
2. Open /reports as a signed-in member and confirm only that workspace's records appear.
3. Try a signed-out and a second-workspace account; if either can read the records, stop and fix the server check.
For custom code, start with the supplied prompts/README.md and copy the closest prompt: prompts/new-app-screen.md for an app screen or prompts/new-landing-page.md for a public page. For server, database, or provider work, give your coding agent this guide's task and ask it to read AGENTS.md, .ai/PROJECT_CONTEXT.md, and matching .ai/ mirrors; inspect the exact source files named here; list files and checks before editing; preserve workspace ownership and server-only secrets; and show you the diff. Tell it the behavior you want, input and output fields, allowed roles, expected success and errors, and the test account, provider, or device. Use sample values; never paste a real secret.
Create the route
Add a page beneath apps/web/app/. Start from the dashboard or content route so you reuse its session and active-workspace resolution. A route exists when its folder contains page.tsx; use a Server Component for protected reads.
apps/web/app/reports/page.tsx
apps/web/src/reports.ts
.ai/apps/web/app/reports/page.md
.ai/apps/web/src/reports.mdVerify access and recovery
Test anonymous access, a valid member, a member of another workspace, a revoked session, and the required role. Add empty and failed-load states with a recovery action. Keep the route out of the public sitemap and add its prefix to robots disallows.
Model the record
Every product record starts in the database schema. Add a table in packages/db with a workspaceId column, generate the migration, and write the repository helpers that resolve the workspace before reading or writing.
// packages/db/src/schema.ts
export const ideas = pgTable("idea", {
id: uuid().primaryKey(),
workspaceId: uuid().notNull().references(() => workspaces.id),
title: text().notNull(),
createdAt: timestamp().defaultNow(),
});
corepack pnpm --filter @supastack/db db:generateAuthorize the action
Create a server action that resolves the session, the workspace, and the entitlement in that order. Validate input with a zod schema, run the mutation through the repository, and return a typed result the interface can render.
The order matters: a mutation that writes before checking workspace ownership has already crossed the boundary. Derive ownership on the server — never trust a workspace id sent by the browser.
Compose the surface
Build the page from shared components and tokens — the kit's blocks and ui-web primitives cover the common patterns. Handle loading, error, and empty states; a feature that only renders its happy path is not done.