Technical SEO for Next.js: the 2026 reference
Technical SEO for Next.js in 2026: metadata API, structured data, Core Web Vitals, and the streaming metadata issue most articles miss.
What technical SEO for Next.js actually covers in 2026
Technical SEO for Next.js in 2026 is not just metadata, sitemap files, and Core Web Vitals. It is the set of framework-layer decisions that determine whether crawlers, search engines, social scrapers, and AI retrieval systems can read the right HTML, trust the right metadata, and extract the right page structure.
This article is for developers, technical founders, and site owners who have shipped a Next.js site and want to know why it is not ranking, or who are about to launch and want to avoid that outcome. Before digging into the framework checklist, the Free Website Scan gives a page-level read on what crawlers, search engines, and AI answer systems can understand from a live URL.
All code snippets below were tested against Next.js 16.2.6, React 19.2.4, and TypeScript 5.9.3 with strict mode and tsc --noEmit. Last validated May 17, 2026. This is the framework-layer checklist used in Jardine Studio’s custom Next.js web development engagements.
The Next.js metadata API in 60 seconds
The Next.js App Router replaces most manual <Head> work with two server-side primitives: a static metadata object for routes with fixed values, and a generateMetadata async function for routes that need params or fetched data, per the Next.js generateMetadata reference (v16.2.4, April 2026). Both run on the server, both are type-safe, and both compose through nested layouts.
Static metadata, in a layout.tsx or page.tsx:
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL('https://yoursite.com'),
title: {
template: '%s | Your Brand',
default: 'Your Brand',
},
description: 'A clear sentence under 155 characters.',
alternates: { canonical: '/' },
openGraph: {
type: 'website',
siteName: 'Your Brand',
images: ['/og.png'],
},
};
Dynamic metadata, for routes that depend on params or fetched data:
import type { Metadata } from 'next';
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return {
title: post.title,
description: post.excerpt,
openGraph: { images: [post.coverImage] },
};
}
Three rules catch most teams. First, metadataBase is required if you use relative URLs in openGraph.images or alternates.canonical. Without it, the build fails. See Next.js's metadataBase reference for the exact error. Second, generateMetadata is server-only; you cannot use it from a client component. Third, metadata merging is shallow: if a child segment defines openGraph, the entire parent openGraph object is replaced, not extended. To share nested fields, extract them to a separate file and import them on both sides.
Server components and the streaming-metadata trap
Next.js 15.2 introduced streaming metadata, which means generateMetadata no longer blocks the initial UI render. For most browsers and capable crawlers, including Googlebot, this is a perceived-performance win because Time to First Byte drops. For HTML-limited bots that do not execute JavaScript, including Twitterbot, Slackbot, and facebookexternalhit, metadata must remain in <head>, and Next.js detects those bots by User-Agent.
The production trap is the long tail. If you see <title> and <meta> tags rendering in <body> instead of <head> on a dynamic page, this may be expected for non-HTML-limited bots, but it can still cause real regressions when a crawler, social scraper, SEO tool, or AI retrieval bot you care about is not treated the way your workflow expects. Vercel's GitHub discussion of the issue collects the symptoms and the fix.
A 2025 case study documented a production Next.js 15 site that lost roughly two months of social-share visibility because metadata appeared in <body> for several scrapers the team relied on — the December 2025 case study on the streaming-metadata regression walks the full timeline. The fix is one line in next.config.ts:
import type { NextConfig } from 'next';
const config: NextConfig = {
// Force every bot to receive blocking metadata in <head>.
htmlLimitedBots: /.*/,
};
export default config;
This trades a small perceived-performance hit in bot scenarios for guaranteed <head> placement across every crawler, including the long tail of social, AI, and SEO scrapers. Apply it if your site depends on social-share previews, if you rely on niche AI crawlers, or if you have ever had to debug "why is my OG image not showing."
For search-critical sites, Jardine Studio’s default is to test the actual crawlers and preview tools that matter. If metadata placement is not consistent, we set htmlLimitedBots: /.*/ and re-evaluate only if measurement shows the tradeoff is material.

React Server Components themselves are not bad for SEO. They are the default you want for indexable content because they produce HTML on the server. The framing that matters is: if Google should rank it, it must be in the initial HTML response. Client Components, meaning anything in a file marked 'use client', hydrate after the server response, so any content that lives only inside a client component is at risk of being invisible to crawlers that do not execute JavaScript.
Keep navigation, headings, body copy, structured data, internal links, and conversion CTAs in server components. Keep forms, theme toggles, filters, and motion-heavy interactivity in client components. Treat 'use client' as a budget, not a default.
Rendering strategy: SSG, SSR, ISR, and PPR for SEO
Next.js gives you four rendering strategies, and your SEO outcome depends on choosing the right one per route. Static Site Generation (SSG) prerenders pages at build time and serves cached HTML. Server-Side Rendering (SSR) generates HTML per request. Incremental Static Regeneration (ISR) is SSG with periodic background revalidation. Partial Prerendering (PPR) ships a static shell immediately and streams in dynamic regions, per Next.js's next.config reference.
The SEO question is not which rendering mode sounds most advanced. It is which mode gives crawlers stable HTML, low latency, and the least amount of request-time uncertainty.
The decision table, with how each strategy behaves for search crawling and AI-engine retrieval:
Marketing pages, blog posts, docs
- Rendering
- SSG
- Why
- Cached HTML at the CDN edge, fastest LCP, infinite scale
- AI citation suitability
- Best. Deterministic HTML, consistent extraction
Product pages with frequent price updates
- Rendering
- ISR (revalidate: 60-3600)
- Why
- Static performance with controlled freshness
- AI citation suitability
- Excellent. Equivalent to SSG between revalidations
User dashboards, authenticated views
- Rendering
- SSR or PPR
- Why
- Per-user content; usually marked noindex so SEO is not a factor
- AI citation suitability
- Not applicable. Noindex pages are not retrieved or cited
Marketing pages with one personalized region (e.g. recommended posts)
- Rendering
- PPR
- Why
- Static shell prerendered, dynamic region streams in
- AI citation suitability
- Depends. Put indexable content in the static shell; treat the dynamic region as enhancement
Pages with thousands of variants generated from a database
- Rendering
- SSG + generateStaticParams
- Why
- Build-time generation; consider ISR fallback for new entries
- AI citation suitability
- Best. Pre-rendered HTML for every variant
The wrong default is SSR for everything. SSR per-request adds latency that hurts LCP, raises hosting cost, weakens CDN caching, and produces variable response timing that some crawlers and retrieval systems handle inconsistently. The right default for most marketing-and-content Next.js sites is SSG, with ISR for sections that change between deploys and PPR for the few routes that need a personalized region inside otherwise-static content.
Structured data in the App Router (without breaking things)
Structured data in the App Router uses JSON-LD rendered as a <script type="application/ld+json"> tag inside a server component, which means zero runtime cost on the client and the JSON ships in the initial HTML, per Next.js's JSON-LD guide. The canonical pattern:
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await getProduct(id);
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.image,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, '\\u003c'),
}}
/>
<h1>{product.name}</h1>
</>
);
}
Two non-obvious things matter here. First, sanitize the JSON by replacing < with its Unicode escape. Without this, a string in your data that contains a < character can break out of the script tag and execute as HTML. Vercel's own JSON-LD guide flags this as a security concern. Second, structured data should live as close to the data source as possible. Put the JSON-LD in the same server component that fetches the entity, so the markup and the visible content cannot drift apart.
The same pattern applies to Organization schema in the root layout, which gives search engines, crawlers, and retrieval systems a consistent structured handle on the brand:
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
const organizationJsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Your Brand',
url: 'https://yoursite.com',
logo: 'https://yoursite.com/logo.png',
sameAs: ['https://www.linkedin.com/company/yourbrand', 'https://x.com/yourbrand'],
};
return (
<html lang="en">
<body>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(organizationJsonLd).replace(/</g, '\\u003c'),
}}
/>
{children}
</body>
</html>
);
}
FAQPage schema deserves a narrower treatment in 2026. Google restricts FAQ rich results to authoritative government and health sites, so ordinary business sites should not treat FAQPage as a reliable Google feature. The useful part is still the visible Q&A content: render real questions in HTML, keep the answers concise, and only emit FAQPage when there is a deliberate non-Google reason and the schema exactly matches visible content.
Worth shipping when the page actually supports it:
Organizationschema on the root layout (your name, logo, URL, social profiles)WebSiteschema that identifies the site and publisherWebPageschema on important static pages, tools, and service hubsBreadcrumbListon every non-homepage routeArticleorTechArticleon journal posts- Visible FAQ content in HTML, with FAQPage only when there is a deliberate reason beyond ordinary Google FAQ rich results
Producton commerce pages,Serviceon service pages,Eventon event pages
What to skip in 2026: aggressive use of speculative schema types and markup that describes content the page does not visibly contain. Schema is a structural signal for AI and search; it is not a magic feature unlock. Stick to types that describe the page honestly and validate cleanly.
Core Web Vitals targets and how Next.js helps you hit them
Core Web Vitals in 2026 are three metrics: Largest Contentful Paint (LCP) at or under 2.5 seconds, Interaction to Next Paint (INP) at or under 200 milliseconds, and Cumulative Layout Shift (CLS) at or under 0.1, per web.dev's current Core Web Vitals thresholds. INP replaced First Input Delay in March 2024, so any Next.js SEO advice still centered on FID is stale.
Each metric has a Next.js feature that moves it most:
LCP (Largest Contentful Paint)
- Target
- ≤ 2.5s
- Next.js feature that helps
- next/image with priority, next/font with display: swap, static or ISR rendering, edge caching
INP (Interaction to Next Paint)
- Target
- ≤ 200ms
- Next.js feature that helps
- Server Components by default, reduce client JS, defer non-critical scripts with next/script strategy="lazyOnload"
CLS (Cumulative Layout Shift)
- Target
- ≤ 0.1
- Next.js feature that helps
- next/image with fixed width/height or fill mode and an aspect-ratio container, next/font automatic fallback metrics
The practical rule for Next.js 15 is to default to server components and treat interactive islands as expensive until proven otherwise. Every island of client interactivity has an INP cost. Audit your 'use client' declarations the same way you would audit any production budget.
To measure in production, wire up useReportWebVitals and pipe the values to your analytics:
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function WebVitals() {
useReportWebVitals((metric) => {
// Send to your analytics endpoint.
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
headers: { 'Content-Type': 'application/json' },
});
});
return null;
}
Render <WebVitals /> once in the root layout. The hook gives you real-user data, not just lab data from PageSpeed Insights. If you host on Vercel, Speed Insights covers the same measurement layer without writing the hook yourself.

next/image, next/font, and the LCP win most teams miss
The highest-impact technical SEO change on many Next.js sites is correct image handling. next/image serves modern formats, generates a responsive srcset, and lazy-loads below-the-fold images. On image-heavy pages, getting the LCP image right can be the difference between a middling mobile score and a page that feels instantly available.
The non-negotiable pattern for any hero image (the typical LCP element):
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="A specific, descriptive alt text"
width={1600}
height={900}
priority
sizes="(min-width: 1024px) 1024px, 100vw"
/>;
The priority prop tells Next.js to preload this image, which moves LCP earlier. The sizes attribute is the part most teams get wrong. The default sizes="100vw" makes the browser download the largest available variant on every viewport, which wastes bandwidth and hurts LCP. The right sizes value mirrors the image's actual rendered width at each breakpoint.
For fonts, next/font self-hosts the font files at build time and inlines metric-matching fallback fonts so CLS stays near zero, per Next.js's next/font reference. Using Google Fonts directly through a <link> tag is one of the most common SEO regressions on Next.js sites, because Google Fonts blocks render and adds a DNS lookup. The fix:
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.variable}>
<body>{children}</body>
</html>
);
}
This removes the external font stylesheet, avoids FOIT, and keeps fallback metrics stable while the web font loads. On a recent Jardine rebuild, switching from <link rel="stylesheet"> Google Fonts to next/font dropped CLS from 0.18 to 0.02 with no other changes.
Sitemap, robots, and the file conventions Next.js generates for you
The App Router includes file conventions that generate sitemap.xml and robots.txt from a single TypeScript or JavaScript file each, per Next.js's metadata file conventions. A dynamic sitemap that reads your content directory or database:
// app/sitemap.ts
import type { MetadataRoute } from 'next';
import { getAllPosts } from '@/lib/content';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts();
const base = 'https://yoursite.com';
return [
{ url: base, lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
{ url: `${base}/about`, lastModified: new Date(), priority: 0.8 },
...posts.map((post) => ({
url: `${base}/journal/${post.slug}`,
lastModified: post.updatedAt,
changeFrequency: 'monthly' as const,
priority: 0.7,
})),
];
}
A robots.ts that allows everything important and blocks operational paths:
// app/robots.ts
import type { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: [{ userAgent: '*', allow: '/', disallow: ['/api/', '/thanks/', '/og/'] }],
sitemap: 'https://yoursite.com/sitemap.xml',
host: 'https://yoursite.com',
};
}
Both files render at /sitemap.xml and /robots.txt automatically. You do not need a build step, a third-party package, or a manual update process. The same convention applies to manifest.webmanifest (via app/manifest.ts) and to favicon files (placed directly in /app).
A sitemap is a promise to crawlers: every URL inside is a page you intend to be discovered and indexed. Mixing in noindex URLs breaks that promise. Two practical rules: do not include noindex URLs in the sitemap, and split the sitemap into an index if it grows beyond 50,000 URLs or 50 MB, per Next.js's sitemap reference.
What changed in Next.js 15 and 16 (and what to verify)
Next.js 15 and 16 introduced several changes that affect technical SEO. The most important ones to verify before a production deploy:
- Streaming metadata (15.2.0).
generateMetadatano longer blocks the initial render for most browsers. Side effect: metadata may appear in<body>for non-HTML-limited bots. Verify with view-source on a dynamic page and apply thehtmlLimitedBotsfix above if you see metadata outside<head>for a crawler you care about. - Async
paramsandsearchParams. As of v15, route params and search params are Promises. You mustawaitthem. Existing pages that destructure params synchronously will throw at runtime. - Cache Components (
'use cache'directive). Introduced in v15, stabilized in v16. Lets you mark expensive computations as cacheable. ForgenerateMetadata, this means metadata that depends on external data but not request-time data can be cached cleanly. - Partial Prerendering (PPR) stabilization. Now the recommended pattern for pages that mix static and dynamic regions.
- React 19 baseline. Concurrent rendering primitives,
<form>actions, and theusehook. Verify all client components still work. - Turbopack default in development. Faster local builds. Production builds still use webpack unless explicitly opted in to Turbopack.
Before a production deploy on a Next.js 15 or 16 upgrade, verify the things crawlers actually touch: open a dynamic route in production and view source to confirm <title> is in <head>; check Google Search Console URL Inspection on a representative page; run Lighthouse on the mobile preset; and re-test social previews on Twitter, LinkedIn, Slack, and Facebook.
What it looks like in practice: a Jardine case
The Black Salt Room build shipped on Next.js 15 with React Server Components by default, next/image and next/font throughout, automatic sitemap and robots generation, JSON-LD for LocalBusiness and Service on every relevant page, and htmlLimitedBots: /.*/ set in next.config.ts.
The site launched with Lighthouse 100 across Performance, Accessibility, Best Practices, and SEO on first run. The technical foundation is now in place to compound as content and links accrue. Without that foundation, future content work would have had a lower ceiling.
The Next.js technical SEO checklist
A single scannable list, in execution order. Each item maps to a section above for the full explanation.
Set
metadataBasein the root layoutPoint it to the production URL so relative paths in
openGraphandalternates.canonicalresolve correctly. Without it, the build fails.Define
title.templatein the root layoutEvery child route inherits a consistent title pattern without restating the brand name in every page.
Define
description,openGraph, andtwitterdefaultsSet them once in the root layout. Override per-route where the page has unique content. Remember that nested
openGraphis replaced, not merged.Use
generateMetadatafor routes with fetched dataWrap shared fetch logic in React's
cache()so the same data is not re-requested betweengenerateMetadataand the page component.Add
htmlLimitedBots: /.*/tonext.config.tsGuarantees
<head>placement of metadata for every crawler, not just the ones on Next.js’s built-in HTML-limited list. The streaming-metadata fix most articles miss.Default new components to Server Components
Use
'use client'only where genuinely needed: forms, theme toggles, motion-heavy interactivity. Treat client-side islands as a production budget.Set Partial Prerendering as the default for marketing routes
Static shell ships immediately; the small dynamic region streams in. Best fit for pages that mix evergreen content with one personalized region.
Replace
<link rel="stylesheet">Google Fonts withnext/fontSelf-hosts the fonts, removes the blocking external request, and inlines metric-matching fallbacks so CLS stays near zero.
Replace
<img>withnext/imageAdd
priorityto hero LCP images. Set an explicitsizesattribute matching the actual rendered width at each breakpoint, not the default100vw.Add
app/sitemap.tsthat reads from your content sourceFilter
noindexURLs out of the sitemap so it does not contradict your robots rules. Search Console treats the contradiction as a low-quality signal.Add
app/robots.tsAllow production paths and disallow
/api/,/thanks/,/og/, and any other operational routes. The same file convention generates/robots.txtat build time.Add JSON-LD via a server-rendered
<script>tagPlace it close to the data source it describes. Sanitize the JSON with
.replace(/</g, '\\u003c')to prevent script-tag breakout.Add
Article,WebPage, andBreadcrumbListschemasUse them where they match the visible page. Use
TechArticlefor technical references; it acceptsproficiencyLevelanddependenciesfields.Wire
useReportWebVitalsto your analyticsCaptures real-user CWV data, not just lab data from PageSpeed Insights. Vercel Speed Insights covers this without writing the hook yourself.
Submit
sitemap.xmlto Search Console and Bing Webmaster ToolsForces both engines to crawl on demand, surfaces indexing errors you would otherwise miss, and accelerates time-to-first-crawl on a fresh article.
How to verify your fix actually worked
Use five checks, each measuring a different failure mode:
- Google Search Console URL Inspection. Run on a representative page after deploy. The "Crawled as" tab shows exactly what Googlebot sees, including the rendered HTML. If your metadata is missing or in the wrong place, this is where you catch it.
- PageSpeed Insights (PSI). Real-user data from the Chrome User Experience Report plus a lab test. Use the mobile tab; mobile thresholds are stricter and represent the majority of traffic.
- Lighthouse in Chrome DevTools. Use the mobile preset with throttling before deploy. Treat local Lighthouse as an engineering smoke test, then confirm production behavior with field data.
- Vercel Speed Insights or analytics endpoint. Real-user CWV captured via
useReportWebVitals. Catches regressions that lab tools miss. - Bing Webmaster Tools. Submit the same sitemap. Bing's crawl is slower than Google's but Bing's index feeds the AI engines that route through Microsoft (Copilot, ChatGPT web search in some configurations).
Schedule a Lighthouse CI run on every pull request. The configuration that matches Jardine's standard:
// .github/workflows/lighthouse.yml (excerpt of the assertions block)
{
"assertions": {
"categories:performance": ["error", { "minScore": 0.95 }],
"categories:seo": ["error", { "minScore": 0.95 }],
"categories:accessibility": ["error", { "minScore": 0.95 }]
}
}
A failed Lighthouse run should block the merge until the regression is understood. Most obvious CWV regressions are caught here before they reach production.
The five mistakes I see on most Next.js site audits
In Jardine Studio's audit work, the same mistakes account for most Next.js SEO regressions. Each one has a named fix and a section above for the full context. If your site is underperforming, start here.
-
Indexable content lives inside
'use client'components. A team marks a hero section as a client component because it has an entrance animation, and the H1 and body copy live inside it. Crawlers that do not execute JavaScript see an empty page. Fix: extract the static parts (H1, body copy, schema, internal links) to the parent server component. Keep only the animation logic inside'use client'. -
metadataBaseis missing or set to the wrong URL. WithoutmetadataBasein the root layout, any relative URL inopenGraph.imagesoralternates.canonicaleither errors at build time or silently produces broken absolute URLs in production. Fix: setmetadataBase: new URL('https://yoursite.com')inapp/layout.tsxonce. Every relative metadata URL in the app then resolves against it correctly. -
<img>tags instead ofnext/image, ornext/imagewithoutsizes. Plain<img>skips AVIF/WebP conversion and lazy-loading;next/imagewithoutsizesdownloads the largest variant on every viewport. Both crater mobile LCP. Fix: every<img>becomesnext/image. Every hero getspriority. Every image gets asizesattribute matching its actual rendered width at each breakpoint. -
The sitemap includes
noindexURLs. A/thankspage or a/previewroute ends up insitemap.xmleven though the page itself is markednoindex. This sends contradictory crawl signals and makes the sitemap less trustworthy. Fix: explicitly filter noindex routes out ofapp/sitemap.ts. Cross-check Search Console > Pages > Excluded for "Indexed, though blocked by robots.txt" warnings. -
SSR is the default for everything. A team rebuilds on the App Router, marks every page
dynamic = 'force-dynamic'because "we might need real-time data later," and now every request hits the origin. LCP suffers, hosting cost rises, CDN caching is wasted, and crawlers get inconsistent response times. Fix: default to SSG. Add ISR (export const revalidate = 60or longer) only where content changes between deploys. Use SSR only for authenticated pages that are noindex anyway.
The pattern across all five mistakes is the same: a Next.js feature was used in a way that overrode the SEO-friendly default. The framework gives you the right defaults; the audits we run mostly find places where teams have opted out of them without noticing.
When this is not enough
Technical SEO is necessary but not sufficient. A site that is technically clean but has no inbound links, thin content, and no topical authority will still struggle to rank. The technical layer raises the ceiling; content, links, and brand do the work of reaching it. Google's May 15, 2026 guidance makes the same point: "From Google Search's perspective, optimizing for generative AI search is optimizing for the search experience, and thus still SEO" (Google for Developers, 2026). The framework matters less than whether the page deserves to rank for what it claims.
If you have shipped a Next.js site that meets every item in the checklist above and it still is not ranking, the technical layer is probably not the constraint. The constraint is usually one of four things: not enough referring domains, content that does not match search intent, missing topical depth across the site, or a brand that has not earned enough trust in the topic cluster.
Those problems are solved with SEO engagements, content strategy, and outreach, not framework configuration. When the framework layer itself is the work, the studio runs technical SEO as a standalone audit, focused engagement, or the technical layer of a new build. For full rebuilds where rankings need to survive the migration, the studio runs dedicated SEO migration projects. If you want passage-level discipline applied to the writing itself, passage optimization covers the writing side of the same problem.
FAQ
What is technical SEO for Next.js?
Is Next.js good for SEO?
Does the Next.js App Router hurt SEO?
Why is my Next.js metadata showing in body instead of head?
Should I use SSR or SSG in Next.js for SEO?
Are React Server Components good for SEO?
Do I need next-seo in 2026?
Does Next.js automatically generate sitemap and robots files?
References (12)
- Next.js docs (v16.2.4, updated April 15, 2026). generateMetadata API reference. Vercel. https://nextjs.org/docs/app/api-reference/functions/generate-metadata
- Next.js docs (v16.2.4, updated April 15, 2026). Metadata and OG images. Vercel. https://nextjs.org/docs/app/getting-started/metadata-and-og-images
- Next.js docs (updated May 11, 2026). Guides: JSON-LD. Vercel. https://nextjs.org/docs/app/guides/json-ld
- Next.js docs. File conventions: metadata (sitemap, robots, opengraph-image, favicon). Vercel. https://nextjs.org/docs/app/api-reference/file-conventions/metadata
- Next.js docs. useReportWebVitals hook reference. Vercel. https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals
- Vercel GitHub Discussion #84518. Next.js 15 App Router: SEO meta tags showing in body instead of head. https://github.com/vercel/next.js/discussions/84518
- JavaScript in Plain English. (December 10, 2025). Next.js 15 App Router Killed Our SEO for 2 Months (And How We Fixed It). https://javascript.plainenglish.io/
- web.dev. (current 2026 thresholds). Web Vitals: LCP, INP, CLS. Google Chrome team. https://web.dev/articles/vitals
- Google for Developers. (updated May 15, 2026). Optimizing your website for generative AI features on Google Search. https://developers.google.com/search/docs/fundamentals/ai-optimization-guide
- Search Engine Land (Leigh McKenzie). (February 11, 2026). Generative engine optimization (GEO): How to win AI mentions. https://searchengineland.com/what-is-generative-engine-optimization-geo-444418
- Nwachukwu, E.. (2025). Optimizing Core Web Vitals with Next.js 15. Medium
- Adeel Imran. (December 9, 2025). Next.js SEO: Complete Implementation Guide for 2026. https://www.adeelhere.com/
Want to talk about your site?
Tell Jardine Studio what is moving and what is stuck. We reply within one business day.
