Medium Publications for AEO: The Honest Tradeoff
App Router's streaming model is a performance win for human users and a citation risk for AI crawlers. The teams getting cited treat Suspense boundaries, server components, and the Metadata API as AEO infrastructure — not rendering details.
In November 2024, the Vercel team announced that Next.js 15 had become the default for new App Router projects, with stable React Server Components, streaming with Suspense, and the Metadata API as first-class primitives. By Q2 2026, more than 65 percent of Vercel-hosted production sites are running on App Router, and the framework has crossed 6 million weekly npm downloads. Next.js is now the default React framework for production B2B sites — which means it is also the default React framework for AI crawler visibility problems.
The problem is not that Next.js cannot serve AI crawlers. It can, beautifully. The problem is that the App Router's default patterns — streaming, Suspense, client components, dynamic data — are optimized for human users with modern browsers, and several of them silently degrade what AI crawlers see. A team that adopted the App Router because it heard "server components are good for SEO" can ship a page that renders perfectly for users, scores green on Lighthouse, and shows blank content to ChatGPT's browsing fetcher.
We have spent the last six months auditing Next.js 15 sites for AEO regressions across SaaS, ecommerce, and content categories. The pattern is consistent: streaming-by-default plus aggressive client component usage equals reduced AI citation visibility, even on sites that look fast and feel modern. The fix is architectural, not cosmetic. This is what the teams winning AI search citations on Next.js — Linear, Cal.com, Cursor, Resend, Vercel's own marketing site — are doing differently from everyone else.
Why App Router Streaming Is an AEO Risk
Streaming was the headline feature of the App Router when it shipped, and it remains the most consequential rendering decision Next.js makes on your behalf. The underlying mechanism is straightforward. When a page contains a Suspense boundary, the server renders everything outside the boundary immediately, sends that HTML in the first chunk of the response, and continues working on the suspended subtree. When the data resolves, Next.js streams a script tag and additional HTML that replaces the fallback. The browser holds the connection open across the entire response and applies the updates progressively.
This is a beautiful pattern for human users on modern browsers. It is also a citation risk for AI crawlers, and the reason is that not all crawlers behave like browsers.
The SSR mandatory analysis we published earlier this year tracked crawler behavior across 14 user agents. The results are uneven. Googlebot is patient and well-behaved — it waits for the full response, executes JavaScript, and indexes the resolved content. Bingbot is similar. Once you leave the major search engine crawlers, the picture changes quickly. OpenAI's GPTBot and ChatGPT-User are HTTP fetchers that read the initial response and disconnect within a few hundred milliseconds. PerplexityBot is similar. Common Crawl's CCBot reads the first chunk and moves on. Anthropic's ClaudeBot is more patient than GPTBot but still less patient than Googlebot.
The result is that a page wrapped in a Suspense boundary around the primary content will look complete to Googlebot, look broken to GPTBot, and look broken to ChatGPT browsing, which sources from CCBot and its own fetcher. The content is technically on the page. It just is not in the bytes the AI crawler read before disconnecting.
We see this most often on three patterns: streaming an entire blog post under a Suspense boundary because the data comes from a headless CMS, streaming a product description from a database query, and streaming the contents of a documentation page from a markdown processor. In all three cases, the user experience is fast and clean, and the AI citation rate is zero.
The Server-Components-First Architecture
The fix is not to abandon streaming. Streaming is genuinely useful for non-primary content. The fix is to make a deliberate decision about what content is allowed to stream and what content must be in the initial response. The teams getting cited in 2026 have converged on a server-components-first architecture that looks like this.
The default rendering mode for every page is fully server-rendered with no Suspense boundaries around primary content. The hero, the article body, the product description, the schema markup, the FAQ block, and the breadcrumb are all server components that block the initial response until their data is ready. The total time-to-first-byte is slightly higher than a fully streamed version, but the resulting HTML contains every piece of content an AI crawler needs.
Streaming is reserved for non-primary content. Recommendations sidebars, related-product widgets, recently-viewed history, comment counts, real-time inventory indicators, personalized banners — these are wrapped in Suspense boundaries and allowed to load progressively. They are not part of the citable content surface. The AI crawler reads the primary content, terminates the connection, and never sees the suspended portion. That is the correct outcome.
Client components are restricted to genuine interactivity. Forms with client-side validation, modals, dropdowns, theme toggles, search inputs, and command palettes are appropriate uses of client components. Static product copy, blog post bodies, FAQ entries, and pricing tables are not. The signal we look for in audits is whether the use-client directive appears in any component that renders content meant for citation. If it does, that content will not exist in the initial HTML, and AI crawlers that do not hydrate will not see it.
Citation-Surface Mapping for Next.js 15
The first step in a Next.js 15 AEO audit is mapping every content surface to its rendering mode. The teams that have done this exercise treat the resulting matrix as a permanent reference document, updated whenever a new feature ships.
| Content surface | Default rendering | Correct rendering for AEO | Common mistake |
|---|---|---|---|
| Article body | Server component | Server component, no Suspense | Streaming entire article via headless CMS fetch |
| Product description | Server component | Server component, no Suspense | Wrapping in Suspense for inventory fetch |
| Pricing table | Server component | Server component, no Suspense | Client component for animated reveal |
| FAQ block | Server component | Server component, no Suspense | Client component for expand-collapse interactivity |
| Schema JSON-LD | Server component | Server component, in layout or page | Generated in client component, invisible to crawlers |
| Breadcrumb | Server component | Server component, no Suspense | Client component for breadcrumb truncation |
| Related items | Server component | Suspense allowed | Blocking initial response on recommender API |
| Comments | Client component | Suspense or client-side, fine to skip | Blocking response on comment count |
| Personalized banner | Client component | Client component, Suspense | Embedded in server component blocking the response |
| Theme toggle | Client component | Client component, leaf node | Wrapping entire layout in client component |
The pattern that emerges from this matrix is that most citable content should be server components without any Suspense wrapper. The cases where streaming is appropriate are also the cases where the content is genuinely supplementary — recommendations, personalization, social proof. The cases where client components are appropriate are also the cases where the content is interactive and not citation-worthy by itself.
Case Study: How Linear Architected Its App Router Migration
Linear's migration from the Pages Router to the App Router happened in late 2024 and was one of the cleanest large-site migrations we have analyzed. The Linear marketing site is heavily editorial — long-form product pages, the Linear Method content site, documentation, a weekly changelog — and citation visibility across ChatGPT, Claude, and Perplexity is one of the company's highest-leverage growth assets.
The architecture they shipped is instructive for any team considering an App Router migration. The root layout is a server component. The page-level components for every editorial surface — blog posts, method essays, documentation pages, changelog entries, product detail pages — are server components. Data fetching happens at the page level in server components using the fetch API with explicit cache settings. The resulting HTML contains every piece of content meant for citation, every JSON-LD block, and every metadata tag. There are no Suspense boundaries around primary content anywhere on the site.
The places where Linear does use streaming are deliberate. The right-rail "more from Linear" widget on blog posts streams. The "recent changelog entries" widget on the homepage streams. The "what's new" banner on the documentation index streams. None of these are citation-bearing content. All of them improve perceived performance without harming AEO.
Linear's client component usage is similarly disciplined. The command palette is a client component. The theme toggle is a client component. The mobile navigation drawer is a client component. The actual product copy, method essays, documentation, and changelog entries are server-rendered HTML. When ChatGPT reads linear.app, it sees the same content Linear's editor wrote, in the same order, with the same emphasis. That is why Linear's citation rate has held steady through the migration when other companies' rates dropped.
The Metadata API and Schema Stack
The Next.js 15 Metadata API is the second AEO-critical primitive in the App Router and the one most teams under-invest in. The API itself is straightforward: every layout and page can export a metadata constant or a generateMetadata async function that returns title, description, Open Graph tags, Twitter card tags, robots directives, alternate URLs, and a handful of other fields. The framework handles assembly, deduplication, and rendering into the HTML head.
The places where teams under-invest are the patterns that compound. Three are worth calling out.
First, generateMetadata should fetch from the same source as the page when the metadata is content-dependent. A blog post should derive its title and description from the post object the page rendered. A product page should derive its OG image from the product image. When the metadata is hardcoded or derived from a separate fetch, it drifts from the content, and AI crawlers extract metadata that does not match what the page actually contains. The fix is to colocate the metadata generation and content fetching, which Next.js's deduping cache makes essentially free.
Second, the metadataBase property should be set in the root layout and the openGraph object should use absolute URLs. The number of production Next.js sites we audit where the OG image URL is relative and resolves to a broken absolute URL when scraped is alarmingly high. The fix is one line of code that sets metadataBase to the production URL.
Third, JSON-LD schema markup belongs in a server component that renders a script tag with the application/ld+json type. The pattern works whether the script is in the page, in a layout, or in a dedicated SchemaProvider component, as long as it renders server-side. The schema markup entity context analysis we published this spring covers the structural reasons schema still matters in 2026, particularly for entity disambiguation. The Next.js 15 implementation is simple — the failure mode is usually that the schema is generated in a client component or pulled from a useEffect, and AI crawlers never see it.
The combination of an accurate Metadata API implementation and server-rendered JSON-LD is what gives a Next.js 15 site a clean metadata surface for AI crawlers. Vercel's own marketing site, Cal.com, and Resend all use this pattern. It is the modern equivalent of the meta-tag hygiene that every SEO consultant in 2014 considered table stakes.
The Audit Playbook
When we run a Next.js 15 AEO audit, the workflow has settled into a repeatable seven-step process. Any team can run this on their own site in an afternoon.
1. Disable JavaScript and crawl the top 50 pages. Use a tool like Screaming Frog with JavaScript disabled, or curl with no rendering, to capture the initial HTML response for each priority page. Compare what you see to what users see in a normal browser. Every difference is a potential AI crawler blindspot. Save the HTML to disk so you can grep for missing content later.
2. Identify every Suspense boundary in your codebase. Grep for the Suspense import from React and document every boundary. For each one, decide whether the wrapped content is primary or supplementary. Primary content under a Suspense boundary is a bug. Supplementary content under a Suspense boundary is correct architecture.
3. Audit your use-client directives. Grep for use client at the top of every component file. For each component, decide whether it is genuine interactivity or whether it could be a server component. The default should be server. The exceptions should be justified.
4. Verify the Metadata API is implemented per page. Every page-level route should export either a static metadata object or a generateMetadata function. Pages that rely on the layout's metadata alone are usually missing per-page context like title, description, and OG image. Check that generateMetadata fetches from the same source as the page itself.
5. Confirm JSON-LD is server-rendered. For every schema type your site uses — Article, Product, FAQPage, Organization, BreadcrumbList — find where the JSON-LD is rendered and confirm it is in a server component. The simplest verification is to view-source on a production URL and search for the script type application/ld+json. If it is missing from the initial HTML, it is invisible to AI crawlers.
6. Test with a real crawler user agent. Fetch your priority pages with the GPTBot, PerplexityBot, and ClaudeBot user agents using curl. Compare the responses to what a normal browser sees. If the AI crawler response is missing content the browser shows, you have a streaming or client-component problem to fix.
7. Track citation rate before and after fixes. Use whatever AI search visibility tracking you have — Profound, Otterly, Ahrefs's AI tracking, or a homegrown script — to measure citation rate for a set of priority queries before the audit and 30 days after the fixes ship. The gap between the two is your AEO ROI for the engineering work.
Profile: Next.js vs Remix vs Astro for AEO
Next.js is the dominant React framework but it is not the only credible choice for an AEO-conscious team. Remix — now consolidated under React Router v7 — and Astro both have meaningful adoption among teams that have made deliberate framework choices for content-heavy sites. The comparison matters because the framework default behaviors differ in ways that affect AI crawler visibility.
Next.js 15 App Router ships server components, streaming, and the Metadata API as defaults. The framework's recommended patterns lean toward streaming, which is the AEO risk we have been describing. With deliberate architecture choices, Next.js produces excellent AEO results. Without those choices, it produces the failure mode we audit most often.
React Router v7, the successor to Remix, defaults to a more traditional server-rendering model with route-level data loaders that block the response by default. Streaming is opt-in via the defer primitive. The default behavior is more conservative than Next.js, which means the default behavior is also more AEO-friendly. Teams that prioritize predictable crawler visibility over rendering flexibility often prefer React Router for content sites. The tradeoff is that the framework's metadata story is less developed than Next.js's, and the ecosystem of plugins and integrations is smaller.
Astro takes the most different approach. Its core model is server-rendered HTML with islands of interactivity. JavaScript is opt-in per component. The result is a framework that produces minimal-JavaScript pages by default, which is structurally good for AI crawlers. Astro's adoption is heaviest in content-first categories — blogs, documentation, marketing sites — where the islands model fits naturally. The tradeoff is that Astro is less suited to highly interactive applications where Next.js's hybrid model works better.
For a B2B SaaS marketing site or documentation portal, all three frameworks can produce excellent AEO results. The right choice depends on what else the site needs to do. Next.js wins on ecosystem and flexibility. React Router wins on simplicity and conservative defaults. Astro wins on content-first sites where minimal JavaScript is the goal.
| Framework | Default rendering | Default crawler safety | Best fit |
|---|---|---|---|
| Next.js 15 App Router | Streaming with Suspense | Requires deliberate architecture | Hybrid apps + marketing |
| React Router v7 | Server-rendered with loaders | Conservative defaults | Content-heavy SaaS |
| Astro | Server-rendered with islands | Best out of the box | Documentation + blogs |
The pattern across all three is that the more conservative the default rendering, the better the out-of-the-box AEO outcomes — and the more flexible the framework, the more the AEO outcomes depend on the team's discipline.
What Vercel Itself Recommends
Vercel publishes guidance on AEO and crawler behavior intermittently, most notably in its 2024 post on AI crawler optimization and in the Next.js documentation on metadata. The current recommendations across both surfaces converge on a few patterns.
Use server components by default. Use Suspense boundaries only for content that is not on the primary citation path. Implement the Metadata API for every page with content-dependent fields. Render JSON-LD server-side. Use absolute URLs for OG images. Configure robots.txt and llms.txt to allow the AI crawler user agents you want to permit. Monitor your edge logs for AI crawler traffic and treat anomalies as signals.
The advice is not novel. It is the same set of patterns that the React SPA audit playbook recommends for any React-based application — render content server-side, ship minimal JavaScript for content surfaces, and treat metadata and schema as first-class infrastructure. What is new is that Next.js 15 makes the implementation easier than any version of the framework has previously, provided the team makes the right architectural choices.
The Common Failure Patterns We See in Audits
Across the audits we have run in the last six months, the failure patterns cluster into a small number of categories. Knowing the patterns lets a team avoid them.
The first pattern is streaming primary content. A team adopts streaming because the framework documentation showcases it as a flagship feature. They wrap their article body, product description, or documentation page in a Suspense boundary and stream the content. The user experience looks fast. The AI citation rate drops to zero. The fix is to remove the Suspense boundary from primary content.
The second pattern is over-using client components. A team migrating from the Pages Router instinctively reaches for client components because the mental model is familiar. They convert components to client components for trivial reasons — a hover state, an animation, a small interaction — and end up with most of the page rendered client-side. The fix is to push client components to leaf nodes and keep the bulk of the page server-rendered.
The third pattern is broken metadata. A team implements the Metadata API but does not set metadataBase, does not generate metadata from the same source as the content, or generates metadata only at the layout level without per-page overrides. The result is missing or wrong OG tags, broken social previews, and degraded AI citation extraction. The fix is to audit every page's metadata and treat it as content infrastructure rather than a configuration detail.
The fourth pattern is invisible schema. A team adds JSON-LD via a third-party library or a client-side helper. The schema renders after hydration. AI crawlers never see it. The fix is to render the script tag with application/ld+json type in a server component, ideally in the page or layout that owns the entity the schema describes.
The fifth pattern is unhandled dynamic functions. A team uses dynamic features like cookies, headers, or searchParams in a server component and accidentally opts the entire page into dynamic rendering. The page now renders per-request, the build no longer prerenders it, and the response time degrades enough that some AI crawlers time out. The fix is to push dynamic functions into Suspense boundaries or to use Partial Prerendering to keep the static shell static.
Each of these patterns is fixable. The hard part is detecting them in a production codebase where the symptoms — a small drop in AI citation rate, a few missing schema validations — are easy to dismiss. The audit playbook above is designed to make detection systematic.
Takeaway: Next.js 15's App Router is the right default for most React teams building production sites in 2026, but the framework's most celebrated features — streaming, Suspense, client components, dynamic rendering — are also its most consequential AEO failure modes when used carelessly. The teams winning AI citations are the ones who treat the App Router as a set of architectural choices rather than a happy path. Server components for primary content. Suspense for supplementary content only. The Metadata API as first-class infrastructure. JSON-LD rendered server-side. A user agent audit that confirms what AI crawlers actually see. The work is not glamorous, and most of it is invisible to users. The compounding effect across thousands of category queries is the difference between being a cited default and being a long-tail mention nobody recommends.
Frequently Asked Questions
Does the Next.js App Router hurt AI crawler visibility compared to the Pages Router?
Not inherently, but the default patterns in the App Router create new failure modes the Pages Router did not have. The biggest is streaming with Suspense. When a page wraps a data-dependent component in a Suspense boundary, Next.js streams the shell first and the fallback HTML for that boundary, then later flushes the resolved component. A modern browser holds the connection open and patches the DOM as chunks arrive. Many AI crawlers — including the lighter HTTP fetchers used by ChatGPT browsing, Perplexity's bot, and Common Crawl — read the initial chunk and disconnect. They see the Suspense fallback, not the resolved content. The fix is not to abandon the App Router. It is to be deliberate about which content is allowed to stream, which content blocks the initial response, and which components render on the server versus the client. The teams getting cited in 2026 made those decisions consciously rather than defaulting to the framework's happy path.
What is the difference between server components and client components for AEO?
Server components render entirely on the server and ship as HTML in the initial response. Client components ship as JavaScript bundles that hydrate in the browser. For AI crawlers, the difference is decisive. AI crawlers that do not execute JavaScript see the full content of server components and see nothing inside client components except whatever placeholder the server rendered before hydration. The practical rule is to default every page-level component and every content-bearing component — articles, product descriptions, FAQ blocks, schema markup, pricing tables — to server components. Use client components only for genuinely interactive surfaces like modals, dropdowns, theme toggles, and forms. Mixing server and client incorrectly is the most common Next.js 15 AEO bug we see in audits. A correctly architected App Router page should ship its primary content as static HTML with stable URLs, and reserve interactivity for the leaf nodes.
How does the Next.js 15 Metadata API affect AI search citations?
The Metadata API in Next.js 15 is the canonical way to generate page metadata — title, description, Open Graph tags, Twitter cards, robots directives, and JSON-LD when paired with the script tag pattern. For AEO, three of its capabilities matter most. First, generateMetadata as an async function lets pages compute titles and descriptions from the same data fetch the page uses, which keeps metadata accurate even when content changes. Second, the openGraph object generates the OG tags that AI crawlers ingest for entity extraction and link previews. Third, the metadataBase property and absolute URL handling fix a class of broken-OG-tag bugs that used to silently kill social and AI citation. Pairing the Metadata API with a JSON-LD injection pattern in the same layout gives AI crawlers a consistent metadata surface across the site. The Vercel-hosted reference sites — Linear, Cal.com, Cursor — all use this pattern in production.
Should I use the App Router or Pages Router if AEO is my top priority?
Use the App Router. The Pages Router still works and is supported, but the App Router is where Next.js 15 and Next.js 16 are receiving new features, including the metadata improvements, Partial Prerendering, and the more granular caching primitives that matter for AI crawler performance. The App Router is also where the React ecosystem is moving — React Server Components are the platform direction for the next decade. The transition cost is real, but the AEO downside of staying on the Pages Router is that you will fall behind on framework features the cited competitors are already using. The decision is not App Router versus Pages Router for AEO. It is App Router versus other frameworks like Remix or Astro, where the underlying rendering models are genuinely different and where the tradeoffs are worth comparing. For most teams running on Vercel or self-hosted Next.js, the App Router is the right answer in 2026.
What is Partial Prerendering and is it good for AI crawlers?
Partial Prerendering, or PPR, is a Next.js 15 rendering mode that combines static and dynamic content on the same page. The page is prerendered at build time with a static shell. When a request arrives, the dynamic portions stream in. The key AEO question is whether the static shell contains enough cited content to serve as a useful response for an AI crawler that does not wait for the stream. If your hero copy, product description, schema markup, and primary content are in the static shell, PPR is fine. If the primary content is inside a dynamic hole, PPR will harm citation rate. The pattern that works in practice is to use PPR for pages where the static portion is the substantive content — blog posts, documentation, product pages — and reserve dynamic holes for personalization, real-time pricing, or other content that genuinely requires per-request data. Treat the static shell as the AEO surface and design accordingly.