Next.js App Router workflow
Instructions
Step 1: Load context
Read references/area-guide.md and app/AGENTS.md.
Keep page.tsx thin — import content from @dt or @dt-pages.
Step 2: Page checklist
CRITICAL before committing:
- Server component by default;
"use client"only for hooks/events/browser APIs generateMetadataormetadataexport- OG via colocated
opengraph-image.tsx— never hardcodelogo512.pngin metadata - Next.js 16 async params:
const slug = (await params).slug Imagefromnext/image, not raw<img>- CSS Modules: relative import
./Component.module.css
Step 3: OG image (if missing)
Add app/<route>/opengraph-image.tsx using app/lib/og-image-utils.tsx.
Blog posts: hero image when available; generated card as fallback.
Step 4: Verify
npm run dev:next
npm run typecheck && npm run lint && npm test && npm run build:next
Visual check:
npx agent-browser open http://localhost:3000/<route>
npx agent-browser screenshot ./check.png
See docs/AGENT_BROWSER_GUIDE.md.
Examples
Example 1: New static page
User says: "Add a /pricing page"
Actions:
- Create
app/pricing/page.tsx(server component) - Add
app/pricing/opengraph-image.tsx - Import page content from
@dt-pagesor shared patterns - Add EN/FI/SV metadata strings
- Build + visual screenshot
Example 2: Fix Slack preview showing old logo
User says: "OG image is wrong for /contact"
Actions:
- Check page metadata for hardcoded
logo512.png— remove it - Ensure
opengraph-image.tsxexists colocated with route - Rebuild and verify with
curl -Ior social debugger
Troubleshooting
Hydration mismatch on theme/language
Cause: client-only theme/i18n applied before hydration.
Solution: suppressHydrationWarning on root <html> in app/layout.tsx.
params.slug is undefined
Cause: Next.js 16 made route params async.
Solution:
export default async function Page({ params }: Props) {
const slug = (await params).slug;
}
React.lazy / react-router-dom errors
Cause: Vite legacy patterns imported in App Router.
Solution: use next/dynamic and next/link / next/navigation only.
Boundaries
- MUST NOT use
React.lazy(),<Head>, orreact-router-dom - MUST NOT use
import.meta.env.VITE_*