Site template
How the Next.js site template that publishes Journey AI content is structured, configured, and turns a request into a page.
This page covers what the site template is, how the project is laid out, which environment variables it needs, and how a request becomes a page.
Journey AI is a headless CMS. Editors work in the panel, and the API serves the content as JSON. The site visitors actually see is rendered by the site template: a Next.js project in the Cms-Landing-Core repository. Every new site starts as a copy of this template.
Tech stack
| Area | Used |
|---|---|
| Framework | Next.js 16 (App Router), React 19, TypeScript |
| Styling | Tailwind CSS v4, shadcn/ui, lucide-react |
| Forms | react-hook-form, zod |
| Content | react-markdown, remark-gfm |
| Package manager | pnpm only |
| Output | output: "standalone", Docker and Node 22 |
Quick start
git clone <Cms-Landing-Core> my-site && cd my-site
cp .env.example .env.local # fill in NEXT_PUBLIC_API_URL and NEXT_PUBLIC_SITE_URL
pnpm install
pnpm dev # http://localhost:3000
pnpm exec tsc --noEmit && pnpm lintNew site projects are created with the repository's scripts/create-landing-project.sh script. Node 22 or later is required; use pnpm only as the package manager.
The README is out of date
The repository's README refers to folders that no longer exist (templates/next-core/, examples/proxy.ts). Follow this page and the repository's .env.example file when setting up.
Project structure
Per-project configuration
| File | What you set |
|---|---|
src/lib/template-config.ts | Home page slug (HOME_SLUG), default language (DEFAULT_LANGUAGE), HTML_LANG, OG_LOCALE, header and footer slugs, paths excluded from the sitemap, form messages |
src/lib/middleware/languages.ts | Languages enabled on the site (TEMPLATE_LANGUAGES) |
src/lib/middleware/config.ts | Language prefix behavior, proxy settings |
src/app/globals.css | Tailwind v4 @theme and shadcn color variables |
next.config.ts | Remote image sources, panel domains allowed to frame the site |
Environment variables
Fill in values following the comments in .env.example. Never commit secret values.
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_URL | Required. CMS API root, for example https://api.example.com. |
PRIVATE_API_ADDRESS | Optional. Internal CMS address used on the server side. |
NEXT_PUBLIC_SITE_URL | Root for canonical, Open Graph, and share URLs. |
NEXT_PUBLIC_SITE_NAME | Site name used in the title template. |
PREVIEW_MODE | Server-only. When true, the deployment runs as the preview site. |
NEXT_PUBLIC_INSPECTOR_ENABLED | Panel inspector. Enable on the preview deployment only. |
SITE_PROXY_SECRET | Shared key for forwarding visitor details to the CMS on form submissions. |
REVALIDATE_ALLOWED_ORIGINS | Origins allowed to call the revalidation endpoint. |
REVALIDATE_NODES | Other server instances that revalidation is forwarded to. |
NEXT_PUBLIC_BUILD_IMG_SERVER_ADDRESS, NEXT_PUBLIC_RUNTIME_IMG_SERVER_ADDRESS | Preconnect targets for the image CDN. |
Without NEXT_PUBLIC_API_URL, development shows a setup screen. In production, pages return 404.
A single content route
The site has no hand-written pages. All content goes through one catch-all route: src/app/[[...slug]]/page.tsx. The address /kurumsal/ekip maps to the slug kurumsal/ekip, and / maps to HOME_SLUG.
How a request becomes a page
The proxy runs
src/proxy.ts skips static files, cleans up malformed URL leftovers, applies the redirect map from the CMS, 301-redirects uppercase paths to lowercase, and resolves the language prefix.
The page is fetched from the CMS
The route calls GET /api/v1/page/detail/{language}?path={slug}. The call is wrapped in react.cache, so metadata and the page body share a single request.
The component tree is rendered
Each top-level item in components[] is turned into a React component by DynamicComponent. See Components.
Metadata is generated
buildMetadata builds the title, description, canonical, Open Graph, Twitter, and robots tags from the SEO fields. PageSchemas prints the JSON-LD blocks.
The shell is added
The header, footer, and navigation come from the template fetched on the server in the root layout, so they are part of the first HTML response.
The result is cached
The page is cached with 300-second ISR. See Caching and preview.
Code conventions
- Write server components by default. Add
"use client"only when you need state or a browser API. - Stay on the Tailwind scale:
text-lg, nottext-[17px]. - Use semantic tokens instead of raw colors:
bg-background,text-muted-foreground. - Icons come from
lucide-reactonly. Load GSAP lazily throughsrc/lib/gsap.ts, never at module level.
Next steps
Related pages
Components
How CMS component types map to React components, and how to add a new block to a site.
Caching and preview
The site template's ISR and cache layers, panel-triggered revalidation, the preview site, redirects, language prefixes, and the sitemap.
Developer documentation
Guides to building sites and technical integrations with Journey AI.