Modern web development with Next.js 15: App Router, accessibility and performance
How we build fast, accessible, scalable websites at Página Web Plus: Next.js 15 App Router, React Server Components, URL-based i18n routing, and an accessibility-first approach.
At Página Web Plus we build on Next.js 15 with the App Router because it lets us handle, in a single stack, the three things that matter most to a professional website: real performance, accessibility by default, and solid SEO. Here's the concrete recipe we apply to client projects.
App Router + React Server Components: server-first
The App Router flips the default: components run on the server unless you opt out. Two huge consequences:
- The initial HTML reaches the browser with content already rendered. Google sees the copy without executing JS.
- The client JavaScript shrinks because only interactive components hydrate.
Practical rule: mark 'use client' only when you actually use useState, useEffect, event handlers or browser APIs. Everything else (hero, copy sections, footer, listings) lives on the server. Design pages as islands of interactivity over static HTML.
URL-based i18n routing with next-intl
Having real /es/... and /en/... URLs — not a client-side toggle that swaps text — is critical for multilingual SEO. With next-intl we get there in three pieces:
middleware.tsdetects the locale and rewrites URLs.src/app/[locale]/layout.tsxloads server-side messages and passes them toNextIntlClientProvider.- A custom
Linkcomponent that preserves the locale segment on navigation.
The sitemap.xml emits bilingual entries with hreflang per language; every page declares alternates.languages so Google links the two versions together.
Optimized images and fonts
Two optimizations we always turn on:
next/imagewith modern formats:formats: ['image/avif', 'image/webp'], realisticdeviceSizes, andminimumCacheTTL: 31536000(one year). Above-the-fold images carrypriorityto improve LCP.next/fontwithdisplay: 'swap'andpreload: trueon the primary font. Avoids FOIT and CLS from font substitution.
Accessibility by default
A new site should be born accessible, not remediated later. From day one we apply:
- Semantic HTML:
<main>,<nav>,<article>,<header>,<footer>. ARIA only when HTML can't carry the meaning. - Heading hierarchy with a single
h1per page. - Contrast validated with the brand palette before approving the design.
- Visible focus on every interactive element (usually a properly offset
outlinering, notoutline: none). - Skip link to
<main>for keyboard users. langattribute on<html>reflecting the active locale.- Forms with
<label for>oraria-labelledbyalways wired to the input.
Want to know your site's current state? Run our free scanner against it.
Automated schema markup
We centralize all JSON-LD in a single module (lib/jsonLd.ts) that exports factories for each type: Organization, WebSite, Service, BreadcrumbList, BlogPosting, FAQPage. Each layout or page injects only the relevant schemas. This avoids duplicating copy and keeps the entity graph coherent with cross-referencing @ids.
The blog you're reading, for example, serves BlogPosting + BreadcrumbList per post, plus Organization + WebSite site-wide. Well-implemented schema is one of the best effort-to-benefit levers in technical SEO.
Pre-deploy verification
Before the final npm run build we run:
- TypeScript strict: no
anyunless clearly justified. - ESLint:
next/core-web-vitalsrules enabled. - Lighthouse or WebPageTest on home and two inner pages — target ≥ 95 on Performance, Accessibility, Best Practices and SEO.
- Rich Results Test with the real URL to confirm the schema.
- hreflang validator once in production.
Build new or migrate?
If you're starting fresh, Next.js 15 + App Router gives you SSR/SSG/ISR, native i18n, image optimization, and a solid ecosystem without gluing parts together. If you have a site on WordPress or an older stack, migration typically recovers 30–60 PageSpeed points and resolves most technical accessibility issues.
Want an estimate for a new project or a migration? Let's talk.