Building SEO-First Web Apps with Next.js and TypeScript

5/1/2026Forgeora Team
Building SEO-First Web Apps with Next.js and TypeScript

A practical guide to structuring Next.js apps for search engines, improving Core Web Vitals, and using TypeScript for robust codebases.

Search engine optimisation (SEO) is not an afterthought—it is a design discipline that begins at the architecture stage and flows through every layer of implementation. In this comprehensive guide, we walk through the concrete steps required to build Next.js applications that rank well, load fast, and remain maintainable as teams grow. ## Why SEO Starts at Architecture Modern search engines are sophisticated crawlers that evaluate not just keyword density but page structure, load performance, semantic correctness, and user experience signals. If you bolt SEO onto an existing application, you typically fight your own codebase. Starting with SEO in mind means choosing rendering strategies intentionally, writing accessible HTML, and integrating measurement from day one. Next.js is uniquely positioned for SEO work because it supports multiple rendering modes—static generation (SSG), server-side rendering (SSR), and incremental static regeneration (ISR)—within a single framework. TypeScript adds the compile-time safety that prevents silent regressions when APIs evolve. ## Rendering Strategy Selection **Static Generation (SSG)** is ideal for content that doesn't change frequently: marketing pages, blog posts, product pages with infrequent updates. Pages are prebuilt at deploy time and served as HTML directly from a CDN edge, giving the fastest possible Time to First Byte (TTFB) and making them trivially crawlable. **Incremental Static Regeneration (ISR)** extends SSG by allowing pages to be regenerated in the background on a schedule or on-demand. For a product catalog with thousands of pages, ISR means you get CDN speed while keeping content fresh without full rebuilds. Set `revalidate` carefully—too short and you lose cache efficiency; too long and crawlers see stale prices. **Server-Side Rendering (SSR)** is appropriate when content is highly personalized or must always be fresh—think dashboards, live feeds, or search results. SSR returns crawlable HTML, but your TTFB is bounded by server response time, so optimize your data layer accordingly. **Client-Side Rendering (CSR)** is generally harmful for SEO. Googlebot does render JavaScript, but there are crawl budget and timing constraints. Reserve CSR for authenticated sections, interactive tools, and experiences where search indexing is irrelevant. ## Semantic HTML and ARIA A page's heading hierarchy tells crawlers and assistive technologies what the content hierarchy is. Every page must have exactly one `<h1>`, which should include the primary keyword. Subheadings `<h2>` through `<h6>` should follow logical nesting. Avoid using heading tags for styling purposes—use CSS classes instead. Use semantic elements: `<article>` for standalone content, `<section>` for grouped topics, `<nav>` for navigation, `<aside>` for tangentially related content, `<main>` for the primary page body. These signal document structure to parsers. ARIA attributes supplement semantics when native HTML is insufficient. Use `aria-label` on icon-only buttons, `aria-describedby` to link form inputs to error messages, and `role="region"` with `aria-labelledby` for landmark regions. Accessible pages tend to perform better because they are structurally clearer. ## Metadata Management with Next.js Next.js 14+ introduced the Metadata API as a first-class way to define `<title>`, `<meta>` descriptions, Open Graph tags, and canonical URLs. Use the `generateMetadata` async function in your route files to derive metadata from fetched data: ```typescript import { Metadata } from 'next'; export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> { const post = await getPost(params.slug); return { title: `${post.title} | Forgeora Blog`, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [{ url: post.ogImage, width: 1200, height: 630 }], }, alternates: { canonical: `https://forgeora.com/blog/${params.slug}`, }, }; } ``` Always specify canonical URLs to prevent duplicate content penalties from pagination, filters, or query parameters. ## Structured Data with JSON-LD Structured data helps search engines extract rich information for enhanced search results (rich snippets). For blog posts, implement `Article` schema; for products, `Product` schema; for FAQs, `FAQPage` schema. In Next.js, inject JSON-LD via a `<Script>` component with `strategy="beforeInteractive"` or directly in the `<head>` through the Metadata API: ```typescript const jsonLd = { '@context': 'https://schema.org', '@type': 'Article', headline: post.title, author: { '@type': 'Organization', name: 'Forgeora' }, datePublished: post.date, image: post.ogImage, }; ``` Test structured data with Google's Rich Results Test before deploying. ## TypeScript Patterns for Resilient Codebases Strong typing prevents the silent bugs that erode code quality over time. Define shared types in a central `types/` directory and import them across client and server code: ```typescript // types/blog.ts export interface BlogPost { id: string; title: string; slug: string; excerpt: string; content: string; publishedAt: Date; author: Author; tags: string[]; ogImage: string; } ``` Use `zod` for runtime validation at API boundaries—parse external data defensively and surface errors early rather than letting malformed data propagate: ```typescript import { z } from 'zod'; const BlogPostSchema = z.object({ id: z.string(), title: z.string().min(1).max(200), slug: z.string().regex(/^[a-z0-9-]+$/), publishedAt: z.coerce.date(), }); ``` ## Core Web Vitals Optimization Core Web Vitals—LCP, INP, and CLS—are ranking signals. For LCP, preload the hero image with `<link rel="preload">` and use `next/image` with the `priority` prop. For INP, break long JavaScript tasks with `scheduler.yield()` or web workers. For CLS, always specify `width` and `height` on images and reserve space for dynamic elements with CSS. ## CI Checklist Integrate SEO hygiene into your CI pipeline: run Lighthouse with a budget config that fails builds when performance scores drop below thresholds; lint for missing alt text with `eslint-plugin-jsx-a11y`; validate JSON-LD with automated schema tests; and check for broken internal links with a site crawler. Treating SEO as a continuous quality gate keeps the codebase from drifting toward poor practices over time.