Guides Next.js development for a marketing website using App Router, TypeScript, Tailwind CSS, headless CMS (Sanity or Payload), and self-hosted deployment on Hostinger. Use when creating pages, components, routes, layouts, CMS integration, SEO metadata, image optimization, static export, or deployment configuration for this project.
Build a performant, SEO-optimized marketing website using Next.js App Router with TypeScript, Tailwind CSS, and a headless CMS. Deploy to Hostinger via static export or Node.js hosting.
app/
├── layout.tsx # Root layout with fonts, metadata, analytics
├── page.tsx # Homepage
├── loading.tsx # Global loading skeleton
├── error.tsx # Global error boundary
├── not-found.tsx # Custom 404
├── sitemap.ts # Dynamic sitemap generation
├── robots.ts # Robots.txt configuration
├── opengraph-image.tsx # Default OG image
├── (marketing)/ # Route group for marketing pages
│ ├── about/page.tsx
│ ├── services/page.tsx
│ ├── contact/page.tsx
│ └── pricing/page.tsx
├── blog/
│ ├── page.tsx # Blog listing
│ └── [slug]/
│ ├── page.tsx # Blog post (SSG with generateStaticParams)
│ └── opengraph-image.tsx
├── api/
│ ├── health/route.ts # Health check endpoint
│ ├── revalidate/route.ts # On-demand revalidation webhook
│ └── draft/route.ts # CMS preview/draft mode
lib/
├── cms/ # CMS client and queries
│ ├── client.ts
│ ├── queries.ts
│ └── types.ts
├── utils.ts # cn() helper, formatting utilities
└── constants.ts # Site config, navigation, social links
components/
├── ui/ # Shadcn/Radix primitives
├── layout/ # Header, Footer, Navigation
├── sections/ # Hero, CTA, Features, Testimonials
└── shared/ # Reusable: Button, Card, Image wrapper
public/
├── fonts/ # Self-hosted fonts
├── images/ # Static images
└── favicon.ico
Default to Server Components. Only add 'use client' for interactive elements (modals, forms, carousels, mobile nav toggles).
Use generateStaticParams for all CMS-driven pages. Marketing pages should be statically generated at build time for maximum performance.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts()
return posts.map((post) => ({ slug: post.slug }))
}
Use Incremental Static Regeneration for pages that update via CMS:
export const revalidate = 3600 // Revalidate every hour
Combine with on-demand revalidation via CMS webhooks for instant updates.
Every page must export metadata. Use title templates in root layout:
// app/layout.tsx
export const metadata: Metadata = {
title: { default: 'C4Flow', template: '%s | C4Flow' },
description: 'Your site description',
metadataBase: new URL('https://yourdomain.com'),
}
Always use next/image. For Hostinger static export, use unoptimized: true or a custom loader (Cloudinary, Imgix). For Node.js hosting, built-in optimization works.
import Image from 'next/image'
<Image
src="/images/hero.jpg"
alt="Descriptive alt text"
width={1200}
height={630}
priority // For above-the-fold (LCP) images
sizes="100vw"
/>
Two options depending on Hostinger plan:
Option A: Static Export (simpler, works with basic hosting)
output: 'export' in next.config.tsout/ directory to HostingerOption B: Node.js on VPS (full Next.js features)
output: 'standalone' in next.config.tsFor detailed deployment instructions, see hosting-deployment.md.
Both Sanity and Payload support headless operation with Next.js:
generateStaticParams for static pathsFor detailed CMS setup, see cms-integration.md.
async in 'use client' components'use server') are the exception: they can be passed to client components.toISOString() before passing to client'use server'Promise.all() for parallel fetches or Suspense boundariescache() when same data is needed in metadata and pageerror.tsx (client component) in route segments for error boundariesglobal-error.tsx catches root layout errors (must include <html> and <body>)redirect(), notFound(), forbidden() in try-catchunstable_rethrow() if navigation APIs are inside catch blockspriority on LCP images (hero, above-the-fold)sizes attribute to responsive imagesloading.tsx for route-level loading statesnext/font for zero layout shift