How to Convert HTML to Next.js Without Losing SEO
A step-by-step guide that keeps your URLs, metadata, and rankings intact when moving a static HTML site to Next.js, built around a real industrial migration.

The safest way to convert HTML to Next.js without losing SEO starts with a ruthless inventory of every page on your existing site, then carrying that structure forward into reusable components that keep URLs identical and metadata in place. Here's the process we follow, using a real industrial migration as the reference.—We moved the C&A Millwrights marketing site from a static HTML stack to Next.js while preserving service pages, location pages, and project entries down to the last URL. The site had to sell to plant managers and recruit skilled millwrights, so the information architecture had to survive the migration untouched. We'll walk through the exact steps that kept SEO intact, then point you toward deeper support.
Why convert HTML to Next.js without breaking your SEO
An HTML‑to‑Next.js migration isn't a redesign — it's a component‑level translation of the existing information architecture. When done right, you gain server‑side rendering, cleaner code, and a layout system that scales, while search engines never notice a difference. Next.js allows you to:
- Serve the same URL paths with identical content
- Port legacy metadata (titles, descriptions, Open Graph tags) using the Metadata API
- Generate a matching sitemap and structured data programmatically
- Deliver content as static HTML at build time (SSG) or on demand (SSR), both highly crawlable
On the C&A Millwrights project, every plant‑facing service page and every location variant had to rank for terms like millwright services Nebraska. That meant the URL structure, heading hierarchy, and internal linking couldn't shift by even a trailing slash.
Step 1: Crawl every URL and document your current site
Start with a complete mirror of what exists. Use a crawler like Screaming Frog or Sitebulb to export:
- Every public URL
- Status codes (200, 301, 302, 404)
- Page titles and meta descriptions
- Canonical tags
- H1 and H2 headers
- Internal links per page
Output a CSV file that becomes your migration spec. For C&A Millwrights, that inventory covered the service index (/services), individual millwright services (/services/equipment-installation), project portfolio entries (/projects/pump-base-fabrication), location pages (/locations/grand-island-ne), and the recruitment path (/careers). Every line in that spreadsheet would later map to a Next.js route.
Step 2: Map pages to a shared Next.js layout and reusable components
Extract the common shell — header, footer, navigation — into a root layout. Then create a component tree that mirrors your content types.
For C&A Millwrights, the homepage split into two calls‑to‑action: one for industrial buyers and one for potential hires. That became a single RootLayout with a navigation that includes a prominent “Join our team” link, plus two distinct content sections handled by page components.
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>
<nav>
<a href="/services">Services</a>
<a href="/projects">Projects</a>
<a href="/careers">Join Our Team</a>
</nav>
</header>
{children}
<footer>{/* full footer ported verbatim */}</footer>
</body>
</html>
);
}Service pages, location pages, and projects each became a dedicated directory with a page.js file that pulls their content from local data or a CMS. The old HTML for a service card is replaced by a <ServiceCard /> component that outputs the same HTML structure and schema.org markup. This approach, similar to how the Next.js migration from Create React App introduces a root layout, keeps every page uniform and maintainable.
You can also stage isolated legacy pages inside the public/ folder if they rely on scripts that aren't ready for React. Just set Next.js to serve them as static assets while the rest of the site runs through the App Router.
Step 3: Keep URLs identical and redirects intact
The cardinal rule: a migration must not change any public URL that is indexed.
Replicate your old path structure exactly using Next.js file‑based routing:
Old HTML URL | Next.js route |
|---|---|
| |
| |
| |
If your old site used .html extensions, you can either strip them (with 301 redirects from the old pattern) or keep them using a custom server or next.config.js rewrites. For C&A Millwrights, URLs were already clean (/services, /projects, /careers), so the file‑based router matched them out of the box.
Any existing 301 redirects from the old site must be carried over. Next.js handles this inside next.config.js:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/old-about-us.html',
destination: '/about',
permanent: true,
},
// copy every redirect from your .htaccess / server config
];
},
};This cross‑walk ensures old backlinks and bookmark traffic land on the right page without a URL change.
Step 4: Port metadata, sitemap and structured data in bulk
Search engines read every scrap of metadata. Your migration script should:
- Titles & meta descriptions – Copy them verbatim from the old HTML
<title>and<meta name="description">into Next.js’smetadataexport for each page. For dynamic pages (like projects), usegenerateMetadatato pull from the same source of truth. - Canonical tags – If your old pages had self‑referencing canonicals, replicate them to prevent default behaviour from introducing a trailing slash or other variation.
- Open Graph / Twitter cards – Place them inside the same
metadataobject to keep social sharing looking identical. - Sitemap – Generate a current
sitemap.xmldynamically withgenerateSitemaps. A utilities script that reads all routes and outputs the same URLs andlastmoddates will ensure search engines see no discontinuity. - Structured data – If your service pages carried
LocalBusinessorServiceschema, embed the same JSON‑LD in the page output. For C&A Millwrights, the service index and location pages usedLocalBusinessmarkup; we moved it to a<script type="application/ld+json">inside the layout or page component.
This step mirrors the detailed migration log from Create React App that emphasizes keeping meta tags identical. There's no “next‑specific” metadata penalty—Google sees the same strings, so nothing changes.
Step 5: Verify Core Web Vitals and organic rankings after cutover
After deployment, treat the first few days as an observation period. Core Web Vitals often improve simply because Next.js ships lean HTML and can statically generate pages. Compare your new Lighthouse scores to a snapshot of the old site:
- Check Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), Interaction to Next Paint (INP) in Search Console or PageSpeed Insights for the same key URLs.
- Monitor crawl stats in Google Search Console for any spike in 404s or soft 404s. That reveals gaps in your URL mapping.
- Watch organic impressions and clicks for the top‑20 queries that drove traffic before migration. A stable or slight upward trend confirms you've preserved ranking signals.
For C&A Millwrights, the migration was successful because every service page and location page returned the same content in the same HTML structure, just served faster through Vercel’s edge. The client’s dual‑audience paths — plant manager and millwright recruit — continued to convert, and the site held its ground for local industrial searches.
When you need deeper hands‑on help, a specialist Next.js development company can handle the URL mapping, component architecture, and post‑migration monitoring for you. If you're facing a large, multi‑service site like C&A Millwrights, our structured HTML‑to‑Next.js migration service includes the full crawl‑map‑validate workflow.
FAQ
Will migrating from HTML to Next.js hurt my SEO?
No, provided you preserve URL paths, port all metadata exactly, handle redirects carefully, and verify Core Web Vitals post‑launch. A well‑planned migration often improves performance scores, which can boost rankings.
How do I keep my old URL structure exactly the same?
You can preserve identical URLs by using Next.js file‑based routing (e.g., app/services/page.js for /services). Purely static pages can live in the public/ folder or be rendered as React Server Components that output the same HTML structure.
How do I handle forms and dynamic content when migrating?
Forms and dynamic content benefit from React state, but you can keep existing endpoints: either POST to your legacy backend or migrate logic to API routes. For static forms that rely on a third‑party action, use the same action attribute inside a React component wrapped in a client boundary.