Fix Next.js App Router Metadata Not Updating: Root Causes and Minimal Checks

Topic: nextjs-app-router-metadata-not-updatingUpdated 7/22/2026

Quick Answer

  • Root cause: Metadata in the App Router is static by default in Server Components; dynamic metadata requires explicit generateMetadata export or client-side handling.
  • First check: Ensure you are using export const metadata in a Server Component (not a Client Component with 'use client'), or implement generateMetadata for dynamic values.
  • Minimal fix: Replace export const metadata = { ... } with export async function generateMetadata({ params }) { return { ... } } for route segments that depend on dynamic data.
  • Version boundary: This applies to Next.js 13+ (App Router); the Pages Router (getServerSideProps) is not affected.
  • Environment: Works in both development and production; no additional packages required beyond next@latest.

What Problem It Solves

When migrating from the Pages Router to the App Router in Next.js 13/14, developers often find that metadata (title, description, Open Graph tags) does not update when navigating between pages or when data changes. This happens because the App Router changes how metadata is defined and rendered. The Pages Router used next/head or custom _document.js approaches, while the App Router uses a declarative metadata export or generateMetadata function. Understanding this shift is critical for correct behavior.

Root Cause Analysis

The App Router introduces two distinct metadata patterns:

  1. Static metadata: export const metadata = { title: 'My Page' } — works only in Server Components and is evaluated at build time or during static generation. It does not re-run on client-side navigation.

  2. Dynamic metadata: export async function generateMetadata({ params }) { ... } — runs on every request (for dynamic routes) and supports async data fetching. This is required when metadata depends on route parameters, search params, or fetched data.

The most common mistake is using static metadata in a route that depends on dynamic data, or accidentally placing metadata in a Client Component (which ignores the metadata export entirely).

Minimal Working Configuration

Static Metadata (for fixed pages)

TSX
// app/about/page.tsx
export const metadata = {
  title: 'About Us',
  description: 'Learn about our company',
}

export default function AboutPage() {
  return <div>About content</div>
}

Dynamic Metadata (for route segments with params)

TSX
// app/posts/[id]/page.tsx
export async function generateMetadata({ params }: { params: { id: string } }) {
  // Fetch data or compute metadata based on params
  const post = await fetch(`https://api.example.com/posts/${params.id}`).then(res => res.json())
  return {
    title: post.title,
    description: post.excerpt,
  }
}

export default function PostPage({ params }: { params: { id: string } }) {
  return <div>Post {params.id}</div>
}

Common Errors and Fixes

ErrorCauseSolution
Metadata not updating on navigationStatic metadata used in dynamic routeSwitch to generateMetadata
metadata export ignoredComponent has 'use client' directiveMove metadata to a Server Component wrapper, or use useMetadata from a client-side library
getServerSideProps not defined in app directoryUsing Pages Router API in App RouterMigrate data fetching to Server Component or generateMetadata
_app.js and _document.js not usedGlobal styles/head content not migratedMove to app/layout.tsx; wrap Context Providers in Client Component

Error: onLoad handler does not work in Server Components

Solution: Move components using onLoad, onReady, or onError (like <Script>) to a Client Component by adding 'use client' at the top of the file.

Production Notes and Security Checks

  • CSP (Content Security Policy): The App Router does not automatically configure CSP. If you use inline scripts or styles, ensure your next.config.js or server headers include appropriate CSP directives.
  • API route authentication: When migrating API routes from pages/api to app/api (Route Handlers), verify that authentication middleware is correctly applied. The App Router uses a different middleware pattern (middleware.ts at project root).
  • Incremental migration: You can keep pages and app directories side by side. Next.js prioritizes app routes over pages routes for the same path. Remove pages directory only after all routes are migrated.
  • Rollback strategy: Keep a Git branch with the Pages Router version. If migration fails, revert to that branch. Test each migrated route individually before removing the pages equivalent.

FAQ

Q: Can I still use pages/api routes after migrating to App Router?

A: Yes, pages/api routes continue to work during migration. They are not affected by the App Router. After full migration, consider moving API routes to Route Handlers in app/api for consistency.

Q: How do I use React Context in the App Router?

A: React Context only works in Client Components. Create a wrapper component with 'use client' directive, export your Context Provider from it, then import and wrap it in app/layout.tsx. Ensure the Provider wraps all components that need context access.

Q: Will pages and app routes conflict?

A: Yes, if both directories define a route for the same path, the app route takes precedence. To avoid conflicts, migrate pages one at a time and verify no duplicate paths exist. Use next.config.js to control behavior if needed (though experimental.appDir is enabled by default in v13.4+).

Official References

Related Guides