New: Plug-and-Play Next.js Integration for Dynamic SEO
The Dynamic SEO Next.js App Router integration is a three-step setup that works from your layout alone. No per-page modifications, no build-time data fetching — just server-rendered SEO metadata that stays in sync with your dashboard.
Managing SEO metadata in Next.js has always required trade-offs. Either you configure each page individually using generateMetadata, or you install a package that still requires per-route setup. Both approaches tie SEO configuration to your codebase, meaning every metadata change requires a code change, a review, and a deployment.
Dynamic SEO now offers a different path. Our new Next.js App Router integration lets you manage all SEO metadata from the Dynamic SEO dashboard and have it rendered server-side on your pages — without modifying individual routes. The setup takes three steps, all in existing files, and the result is production-grade SEO that your marketing team can update without filing a pull request.
This post explains how the integration works, what it does under the hood, and how to set it up.
Early access: Dynamic SEO is currently in early access. The Next.js integration described here is available to early-access teams — join the waitlist to get access.
The Challenge: SEO in Next.js App Router
Next.js App Router introduced a fundamentally different rendering model. Server Components run on the server and stream HTML to the client. Layouts wrap pages but do not re-render on navigation. Middleware runs at the edge before rendering begins but cannot modify response bodies.
This architecture is excellent for performance. It is less accommodating for SEO tooling that needs to intercept and modify HTML responses.
Why Traditional Approaches Fall Short
The most common pattern for dynamic SEO in Next.js is the generateMetadata function, exported from each page or layout file. It works well for a handful of pages with predictable metadata. It does not scale when you have hundreds of URLs managed by a non-technical team.
// The standard approach: one function per route
export async function generateMetadata({ params }) {
// You need this in every page file
// Changes require code changes and redeployment
const seo = await fetchSEOData(params.slug)
return {
title: seo.title,
description: seo.description,
}
}
Packages like next-seo reduce boilerplate but still require per-page configuration objects. The fundamental constraint remains: metadata is defined in code, deployed with code, and governed by your development workflow.
For teams where SEO is managed by marketers or content strategists, this creates a bottleneck. Every title tag adjustment becomes a Jira ticket.
Why Middleware Alone Is Not Enough
In Express or Fastify, you can write middleware that modifies the response body before it reaches the client. Next.js middleware deliberately does not support this. It can rewrite URLs, set headers, and redirect requests, but it cannot alter the HTML that gets streamed to the browser.
This is a conscious design decision by the Next.js team — streaming responses cannot be buffered and modified without sacrificing the performance benefits of streaming itself. Any SEO integration for Next.js must work within this constraint, not against it.
Our Solution: DynamicSEOHead
The Dynamic SEO Next.js integration uses a Server Component called DynamicSEOHead that renders meta tags directly in your layout. Next.js automatically hoists any <title>, <meta>, and <link> elements rendered by Server Components into the document <head>, regardless of where they appear in the component tree.
This means a single component in your root layout can control all SEO metadata for every page it wraps — without touching individual page files.
The component fetches SEO configuration from the Dynamic SEO API at render time, resolves the correct metadata for the current URL, and returns the appropriate tags. Because it runs as a Server Component, the metadata is included in the initial HTML response. Search engines see fully rendered meta tags on the first request, with zero client-side JavaScript involved.
Three-Step Setup
The entire integration requires changes to three files, two of which are single-line additions.
Step 1: Install the Package
The package is not on npm. Download it from the Next.js guide on the Integrate page of your dashboard, commit it in your repository, and install it from that path:
npm install ./vendor/dynamic-seo-nextjs.tgz
It ships TypeScript source, so tell Next to compile it. This is the one line every consumer needs in next.config.ts:
const nextConfig = { transpilePackages: ['@dynamic-seo/middleware'] }
export default nextConfig
Step 2: Add the Proxy
Next 16 renamed the middleware file convention to proxy. Create proxy.ts at the project root (on Next 15 and earlier, middleware.ts, exported as middleware):
// proxy.ts
export { dynamicSEOMiddleware as proxy } from '@dynamic-seo/middleware/nextjs'
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|api/).*)'],
}
The proxy sets one request header, the current path, so the layout component knows which page it is rendering. If you already have a proxy, wrap it with withDynamicSEO instead of exporting ours.
It adds negligible latency. It is a lightweight pass-through that performs no external requests.
Step 3: Add DynamicSEOHead to Your Layout
// app/layout.tsx (or a marketing-specific layout)
import { DynamicSEOHead } from '@dynamic-seo/middleware/nextjs'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<DynamicSEOHead
apiUrl={process.env.DYNAMIC_SEO_WORKER_URL!}
domain={process.env.DYNAMIC_SEO_DOMAIN!}
siteKey={process.env.DYNAMIC_SEO_SITE_KEY}
/>
</body>
</html>
)
}
The three environment variables are the edge worker's URL, your domain as registered in Dynamic SEO, and the site key from the Integrate page. The guide prints them filled in for your site.
That is the complete setup. Every page rendered within this layout will have its SEO metadata fetched from Dynamic SEO and rendered as server-side HTML.
How It Works
The integration works in the background during server-side rendering. When a page is requested, the layout component asks the edge worker for the published SEO of that path and renders it as part of the HTML response. Next.js automatically includes the tags in the document head. When nothing is published for the path, the component renders nothing, and your own tags stay in place.
The fetch adds negligible latency to the render. The worker answers from its edge cache, and Next caches the fetch for the revalidate window you configure, so subsequent requests for the same URL make no call at all.
Component Props and Configuration
DynamicSEOHead accepts several props for controlling its behavior:
<DynamicSEOHead
apiUrl="https://seo.dynamicseo.com" // Required. The edge worker.
domain="example.com" // Required. The domain as registered in Dynamic SEO.
siteKey="sk_..." // Your site key, sent as X-Site-Key. Set it in production.
revalidate={300} // Optional. Fetch revalidation in seconds. Default: 300.
timeout={2000} // Optional. Fetch timeout in milliseconds. Default: 2000.
skipPaths={['/admin', '/api']} // Optional. Paths to exclude from SEO injection.
/>
Caching
The revalidate prop controls how long Next caches the fetch. The default, 300, means the SEO for a given URL is re-fetched at most once every five minutes. The worker in front of it keeps its own edge cache, which publishing purges, so a change you publish reaches the next uncached render.
Path Exclusion
The skipPaths prop accepts an array of path prefixes. Any URL starting with a listed prefix is skipped entirely — no API call is made and no metadata is injected. This is useful for admin panels, API routes, authentication pages, and any route where you want full control over the metadata in code.
Automatic Security
All metadata values are automatically sanitized before rendering. This is not optional or configurable — it runs on every response.
Advanced Usage
While the layout-level setup covers the majority of use cases, the package also exports utilities for teams that need per-page control.
Per-Page Override with generateMetadata
If a specific page needs to merge Dynamic SEO data with local metadata, use createDynamicSEO. Its metadata method fetches the published SEO for a path and merges it over the fallback you pass, which is what the page keeps when nothing is published:
// app/products/[slug]/page.tsx
import { createDynamicSEO } from '@dynamic-seo/middleware/nextjs'
const dseo = createDynamicSEO({
apiUrl: process.env.DYNAMIC_SEO_WORKER_URL!,
domain: process.env.DYNAMIC_SEO_DOMAIN!,
siteKey: process.env.DYNAMIC_SEO_SITE_KEY,
})
export async function generateMetadata({ params }) {
const { slug } = await params
return dseo.metadata(`/products/${slug}`, {
title: 'Products',
openGraph: { images: [{ url: `/og/products/${slug}.png` }] },
})
}
When using createDynamicSEO at the page level, ensure you remove DynamicSEOHead from the layout wrapping that page, or use skipPaths to exclude it. Duplicate metadata tags degrade SEO rather than improve it.
Merging Metadata from Multiple Sources
The mergeMetadata helper combines metadata objects with predictable precedence:
import { mergeMetadata } from '@dynamic-seo/middleware/nextjs'
export async function generateMetadata({ params }) {
const { slug } = await params
const seo = await dseo.fetchSEO(`/products/${slug}`)
const localMeta = {
title: `${slug} | Products`,
robots: { index: true, follow: true },
}
// the published SEO takes precedence; localMeta fills gaps, and is all there is until something is published
return seo ? mergeMetadata(seo, localMeta) : localMeta
}
Dynamic SEO metadata integrates seamlessly with any existing Next.js metadata you have defined. The Dynamic SEO dashboard is the source of truth, and local values serve as fallbacks.
Why No Per-Page Modifications Are Needed
This is the central design decision and worth explaining in detail.
Traditional Next.js SEO workflows require touching every page file because metadata generation is co-located with the page component. The App Router reinforced this pattern with generateMetadata — a clean API, but one that distributes SEO configuration across your entire route tree.
Dynamic SEO takes the position that SEO configuration should be centralized. Your development team should not need to know what the title tag for /products/hiking-boots says. They should know that SEO metadata comes from the dashboard, and the integration handles delivery.
By placing DynamicSEOHead in the layout, you establish a single integration point. Adding new pages to your application automatically inherits SEO management — no additional wiring required. Removing pages removes them from management automatically at the next crawl cycle. The SEO layer tracks the site, not the code.
This also eliminates a class of bugs where a developer forgets to add generateMetadata to a new page, or copies metadata from another page and forgets to update it. If the page exists and the layout wraps it, SEO is handled.
Comparison with Other Approaches
next-seo
The next-seo package provides React components for setting meta tags. It requires you to add <NextSeo> to each page with a configuration object. Metadata lives in your component files, changes require deployments, and there is no external management interface. It is a good library for developer-controlled SEO on smaller sites. It does not address the workflow problem of non-technical teams managing metadata at scale.
Manual generateMetadata with a CMS
Some teams fetch SEO metadata from their CMS in generateMetadata. This works but creates a tight coupling between your CMS content model and Next.js metadata format. You need transformation logic in every page file, error handling for missing CMS entries, and a content model that maps to the full range of meta tags, Open Graph properties, and structured data types that SEO requires. Dynamic SEO handles this mapping internally, with a purpose-built interface for SEO rather than a general-purpose CMS.
Head management libraries
Libraries like react-helmet (for Pages Router) or manual <head> manipulation are not compatible with App Router's streaming architecture. They rely on client-side DOM manipulation, which means search engine crawlers may not see the metadata on the initial render. Server Components solve this at the framework level, and DynamicSEOHead builds on that foundation.
Deployment Compatibility
The middleware and DynamicSEOHead component are runtime-agnostic. They run on any Node.js or edge runtime that supports the Next.js App Router — managed hosting platforms, edge networks, or a self-hosted Node server. ISR is supported via the standard Next.js revalidation API; edge-runtime targets can swap the cache adapter included in the package.
The component itself is a standard React Server Component that makes a fetch call. Any platform that supports Next.js App Router supports the integration.
What the Integration Does Not Do
Transparency about scope prevents mismatched expectations.
The integration manages metadata — title tags, meta descriptions, canonical URLs, Open Graph tags, Twitter cards, structured data (JSON-LD), and custom meta tags. It does not modify page content, inject visible elements, alter your CSS, or add client-side JavaScript.
It does not replace your sitemap. Dynamic SEO has separate tooling for sitemap generation, but the Next.js integration focuses exclusively on metadata delivery.
It does not handle redirects. Next.js has its own redirect system in next.config.js and middleware. Mixing redirect logic with SEO metadata injection would create debugging complexity without meaningful benefit.
What Comes Next
React Server Components make this integration pattern possible. The ability to run server-side data fetching inside a component that renders to the initial HTML stream — without client-side hydration — is what allows a single layout component to handle SEO for an entire site.
We expect this pattern to become more common across the React ecosystem. Remix already has similar primitives through its loader system. SvelteKit and Nuxt have their own equivalents. As frameworks converge on server-first rendering, the case for centralized, external SEO management becomes stronger.
On our side, the roadmap includes A/B testing for metadata (serve different titles to different segments and measure click-through rates), real-time preview in the dashboard showing exactly how your pages will appear in search results after a metadata change, and tighter integration with the Dynamic SEO crawl engine to automatically detect new pages and suggest metadata rules.
The Next.js integration is available now. Download the package from the Integrate page, add three lines of configuration, and move SEO management out of your codebase and into the hands of the people who manage it.