Monorepo starter with Strapi v5 CMS and Next.js 16 frontend. Uses pnpm workspaces with Turborepo.
- Save any screenshots or tmp files to tmp/
- When updating skills ALWAYS update skills in
.agents/folder, no the ones in agent specific folders like.claude/
- Always use
interfacefor prop types withextend ComponentProps<"div">where applicable - Never use one-line if, if-else or else branches, always wrap its content in curly braces
| Path | Description |
|---|---|
apps/ui |
Next.js 16 (App Router, React 19, TailwindCSS v4, Shadcn/ui) |
apps/strapi |
Strapi v5 CMS (PostgreSQL via Docker) |
packages/strapi-types |
Auto-generated TypeScript types from Strapi schemas |
packages/design-system |
Shared TailwindCSS tokens and styles |
packages/shared-data |
Shared constants and types |
qa/tests/playwright |
E2E and accessibility tests |
pnpm dev # Start both apps (Docker required for DB)
pnpm build # Build all
pnpm lint # ESLint all packages
pnpm typecheck # Typecheck (run from apps/ui)See docs/commands.md for full command reference.
After ANY Strapi schema change, run this immediately and autonomously — do not ask the user first:
cd apps/strapi && pnpm generate:typesThis updates @repo/strapi-types. Forgetting causes silent type mismatches between apps.
generate:types is a one-shot script, not a long-running server — it is safe to run at any time.
If the command fails, block the current task and ask the user to restart Strapi, then re-run it.
- Commands Reference — All pnpm commands
- Architecture — System design and patterns
- Page Builder — Component registry and rendering
- Strapi API Client — Fetching content from Strapi
- Pages Hierarchy — URL structure and redirects
- Strapi Schemas — Schema attributes, localization, lifecycle hooks
- Strapi Types — Type utilities and usage patterns
Never launch dev servers (pnpm dev, strapi develop, next dev) in the background. These spawn long-running processes that are hard to kill from within the agent.
Strapi PUT requests replace the entire field value. Always GET first, append, then PUT.
// WRONG — wipes existing content
PUT { "data": { "content": [{ "__component": "sections.new", ... }] } }
// RIGHT — preserves existing
GET → content = [{ existing1 }, { existing2 }]
PUT { "data": { "content": [{ existing1 }, { existing2 }, { "__component": "sections.new", ... }] } }
Uses conventional commits enforced by Husky + commitlint.
pnpm commitOr write manually: type(scope): subject