┌─────────────────────────────────────────────────────────────────────────────┐
│ Strapi CMS (apps/strapi) │
│ - Content management │
│ - REST API with document middleware │
│ - PostgreSQL (Docker) │
└────────────────────────────────────┬────────────────────────────────────────┘
│ REST API
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Next.js Frontend (apps/ui) │
│ - App Router with catch-all routing │
│ - Server/client Strapi clients with proxy │
│ - Page builder component registry │
└─────────────────────────────────────────────────────────────────────────────┘
Strapi component UIDs map to React components via a central ContentComponents registry. Three content types use dynamic zones:
- Page (collection type):
sections,forms,planscomponents - Header (single type):
navigationcomponents - Footer (single type):
footercomponents
All are rendered by a shared DynamicZoneRenderer.
- Strapi schemas:
src/components/{category}/ - Populate configs:
src/populateDynamicZone/{category}/ - Page components:
components/page-builder/components/{category}/ - Footer components:
components/page-builder/single-types/footer/ - Registry:
components/page-builder/index.tsx
See Page Builder for details.
PublicStrapiClient handles content fetching using API key authentication.
Client-side requests use a proxy route (/api/public-proxy) to hide the Strapi URL.
See Strapi API Client for details.
- Catch-all
[locale]/[[...rest]]renders Strapi-managed pages - Locale extracted from URL, passed to API queries
Dual i18n system:
| System | Purpose | Location |
|---|---|---|
| next-intl | UI strings | src/locales/{locale}.json |
| Strapi i18n | Content | locale query parameter |
Validated via @t3-oss/env-nextjs. Access through getEnvVar().
Files bootstrapped from .example files on pnpm install.
Pages use parent-child relations for URL structure. fullPath auto-generated from slug chain.
See Pages Hierarchy for workflow.