Summary
Frontend decisions dominate SEO: CSR apps are crawlable but Google may delay rendering by days. SSR and SSG give crawlers HTML immediately. The Next.js Metadata API generates `<head>` tags at build or request time. JSON-LD structured data adds rich results. Core Web Vitals are a direct ranking signal for Google Search.
Jump to the interview angleRendering strategy vs crawlability
| Strategy | First-fetch HTML | Google indexing speed | Use when | |
|---|---|---|---|---|
| SSG | Full HTML in CDN response | Immediate — no render queue | Content is the same for all users | |
| SSR | Full HTML per request | Immediate — no render queue | Content varies by user or URL | |
| CSR | Empty shell; JS builds DOM | Delayed — JS render queue | Auth-gated pages with no public SEO need | |
| Partial prerendering | Static shell + streamed dynamic slots | Shell indexed immediately | Mostly static pages with a few dynamic sections |
Next.js Metadata API + JSON-LD structured data
Next.js App Router exports metadata (static) or generateMetadata (dynamic, async). Add JSON-LD in the same file — Googlebot reads it from the initial HTML without executing extra JS.
import type { Metadata } from "next";
// generateMetadata runs on the server at request time.
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> },
): Promise<Metadata> {
const { slug } = await params;
const post = await fetchPost(slug); // your data layer
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: `https://example.com/blog/${slug}` },
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: post.ogImage, width: 1200, height: 630 }],
},
};
}
// JSON-LD lives in the RSC layer → present in initial HTML.
export default async function BlogPost(
{ params }: { params: Promise<{ slug: string }> },
) {
const { slug } = await params;
const post = await fetchPost(slug);
const schema = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
datePublished: post.publishedAt,
author: { "@type": "Person", name: post.author },
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
<article>{/* … */}</article>
</>
);
}generateMetadata and the page component share one fetchPost call — Next.js deduplicates it via fetch memoisation, so there is no double request.
Structural SEO checklist
- Every public page needs a unique `<title>` and `<meta name="description">` — duplicates dilute ranking.
- Set `<link rel="canonical">` on paginated, filtered, or UTM-tagged URLs to consolidate link equity.
- Submit an XML sitemap to Google Search Console; exclude `noindex` URLs from it.
- Keep `robots.txt` permissive for CSS, JS, and images — block only auth-gated paths.
- Use semantic HTML (`<article>`, `<nav>`, `<main>`, `<h1>` hierarchy) — crawlers parse structure, not visual layout.
CWV are a ranking signal — INP is now included
Google's Page Experience signal includes all three Core Web Vitals: LCP (≤2.5 s), INP (≤200 ms), and CLS (≤0.1). INP replaced FID in March 2024. A poor INP from a heavy third-party script or a long event handler directly affects Search ranking for that URL. Measure with CrUX field data, not Lighthouse lab scores.
Interview angle
Expect questions on CSR vs SSR for crawlability, how Googlebot handles JS, and which CWV affect ranking. Soundbite: "Googlebot renders JS but queues it — SSR guarantees crawlers see your content on first fetch."
Key terms
- SSR
- Server-side rendering — HTML generated per request, visible to crawlers on first fetch.
- JSON-LD
- Script tag embedding schema.org structured data; enables rich results in Google Search.
- canonical URL
- The `<link rel="canonical">` tag that signals the authoritative URL for duplicate content.
- Core Web Vitals
- LCP, INP, CLS — Google ranking signals measuring load, responsiveness, and layout stability.