<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hossam</title>
    <description>The latest articles on DEV Community by Hossam (@hossam_hamdy).</description>
    <link>https://dev.to/hossam_hamdy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4040860%2F0468d1cd-2c74-4bd1-8fc9-cb6c59cd7a41.jpg</url>
      <title>DEV Community: Hossam</title>
      <link>https://dev.to/hossam_hamdy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hossam_hamdy"/>
    <language>en</language>
    <item>
      <title>How to Migrate WordPress to Next.js Without Losing Your SEO</title>
      <dc:creator>Hossam</dc:creator>
      <pubDate>Wed, 22 Jul 2026 00:03:32 +0000</pubDate>
      <link>https://dev.to/hossam_hamdy/how-to-migrate-wordpress-to-nextjs-without-losing-your-seo-55bg</link>
      <guid>https://dev.to/hossam_hamdy/how-to-migrate-wordpress-to-nextjs-without-losing-your-seo-55bg</guid>
      <description>&lt;p&gt;Most “WordPress to Next.js” tutorials show you how to fetch posts from the WP REST API and render them in the App Router. That’s the easy 20%. The 80% that actually decides whether your organic traffic survives is everything &lt;em&gt;around&lt;/em&gt; the content: your URLs, your redirects, your metadata, your sitemap, and your images. Get those wrong and you’ll watch impressions fall off a cliff two weeks after launch, right when everyone assumes the migration “went fine.”&lt;/p&gt;

&lt;p&gt;This guide is the checklist I wish every team ran &lt;strong&gt;before&lt;/strong&gt; flipping DNS. It’s framework-accurate for the Next.js App Router, and it works whether your new backend is headless WordPress, a headless CMS, or flat files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule that saves rankings
&lt;/h2&gt;

&lt;p&gt;Every decision in a migration comes back to a single principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nothing about how Google already sees your pages should change, except the parts you deliberately improve.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google ranks specific URLs based on their content, their metadata, and the links pointing to them. A migration is dangerous precisely because it’s tempting to change all three at once: new URLs, a “cleaner” content structure, redesigned templates. Do that and you’ve thrown away the signals every ranking is built on. The safe path is boring: same URLs, same content, same meta, just a faster, modern frontend underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Inventory everything before you touch anything
&lt;/h2&gt;

&lt;p&gt;You cannot preserve what you haven’t captured. Before writing a line of Next.js, you need a complete, structured snapshot of the live site: every published URL, its rendered content, its SEO metadata, its images, and its internal links. This inventory becomes the source of truth for your redirect map, your &lt;code&gt;generateMetadata&lt;/code&gt;, and your sitemap.&lt;/p&gt;

&lt;p&gt;This is the step most guides wave away with “export your content from WordPress.” In reality it’s where migrations break, because the default WordPress export (WXR) gives you raw post content, not the &lt;em&gt;rendered&lt;/em&gt; HTML your page builder actually outputs, and it drops most of the SEO fields you need. If your site runs Elementor, WPBakery, Divi, Bricks, or Oxygen, a raw export is close to useless: the shortcodes never render.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is exactly what Migratik was built for&lt;/strong&gt; (Migratik packs an entire WordPress site into one clean JSON file) the fully &lt;em&gt;rendered&lt;/em&gt; &lt;code&gt;content_html&lt;/code&gt; (builder shortcodes executed), the theme-rendered &lt;code&gt;document_title&lt;/code&gt;, the &lt;code&gt;permalink&lt;/code&gt; of every item, SEO meta from Yoast / Rank Math / AIOSEO, featured &amp;amp; in-content images, internal links, taxonomies, and custom fields. It’s builder-agnostic and read-only, so it changes nothing on your live site. That single file is the inventory this entire guide runs on. &lt;a href="https://wordpress.org/plugins/migratik/" rel="noopener noreferrer"&gt;Install Migratik free on WordPress.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However you capture it, make sure your inventory contains, for every URL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The permalink&lt;/strong&gt;, the live, canonical URL. This drives your redirect map.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rendered HTML&lt;/strong&gt;, what visitors actually see, builders executed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The document title &amp;amp; meta description&lt;/strong&gt;, the exact &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and description Google indexed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical, Open Graph, and structured data&lt;/strong&gt;: anything that shaped the search snippet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every image URL&lt;/strong&gt;, featured and in-content, with alt text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal links&lt;/strong&gt;, so you can verify none 404 after launch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Keep your URL structure
&lt;/h2&gt;

&lt;p&gt;The single highest-leverage decision you’ll make: &lt;strong&gt;keep your existing permalink structure&lt;/strong&gt;. If a post lives at &lt;code&gt;/blog/corneal-cross-linking/&lt;/code&gt; today, it should live at the same path on Next.js. Same slug, same trailing-slash convention, same casing. When URLs don’t change, the vast majority of your ranking signal transfers for free.&lt;/p&gt;

&lt;p&gt;In the App Router, mirror your WordPress paths with the file-system router and dynamic segments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/blog/[slug]/page.tsx  →  /blog/corneal-cross-linking&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateStaticParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAllContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// from your inventory JSON&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One detail that silently causes duplicate-content problems: &lt;strong&gt;trailing slashes&lt;/strong&gt;. WordPress serves &lt;code&gt;/blog/post/&lt;/code&gt; (with a slash); Next.js defaults to no slash. Pick one and enforce it site-wide with &lt;code&gt;trailingSlash&lt;/code&gt; in your config, and make sure your redirects and canonical tags agree with the choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;trailingSlash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;  &lt;span class="c1"&gt;// match WordPress's default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: 301 anything that moves
&lt;/h2&gt;

&lt;p&gt;Some URLs will change, maybe you’re dropping &lt;code&gt;/category/&lt;/code&gt; bases or consolidating thin pages. Every one of those &lt;strong&gt;must&lt;/strong&gt; return a &lt;strong&gt;301 (permanent) redirect&lt;/strong&gt; to its new home. A 301 passes the old URL’s ranking authority to the new one; a 302 (temporary) or a soft 404 throws it away.&lt;/p&gt;

&lt;p&gt;Because your inventory already holds every old permalink, generating the redirect map is mechanical. For a handful of redirects, put them in &lt;code&gt;next.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;redirects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/category/:slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/topics/:slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// emits a 301&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/old-about-us&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For hundreds or thousands of one-off redirects, don’t hand-write them, generate the list from your inventory and match them in &lt;code&gt;middleware.ts&lt;/code&gt; against a lookup map, or use your host’s redirect layer (Vercel, Netlify, Cloudflare). The key is that &lt;code&gt;permanent: true&lt;/code&gt; emits an HTTP 301.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Watch out&lt;/strong&gt;, Never chain redirects (old → interim → final) and never redirect everything to the homepage. A redirect to an irrelevant page is treated by Google as a soft 404 and loses the ranking entirely. Map each old URL to its &lt;em&gt;true&lt;/em&gt; equivalent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Carry over your metadata, exactly
&lt;/h2&gt;

&lt;p&gt;Your &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and meta description are what earn the click in search results. Port them &lt;strong&gt;verbatim&lt;/strong&gt; from the inventory: don’t “rewrite while you’re at it.” The Next.js App Router exposes the Metadata API for precisely this, and &lt;code&gt;generateMetadata&lt;/code&gt; lets you populate it per-page from your captured data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/blog/[slug]/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;document_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// exact indexed &amp;lt;title&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alternates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permalink&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;openGraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;document_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featured_image&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things people forget here: set an explicit &lt;strong&gt;canonical&lt;/strong&gt; for every page (with your trailing-slash convention), and carry over your &lt;strong&gt;structured data&lt;/strong&gt; (JSON-LD). If your posts had Article or FAQ schema on WordPress, render the same JSON-LD in Next.js, losing it can shrink your rich results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Rebuild your sitemap and robots
&lt;/h2&gt;

&lt;p&gt;A fresh site needs a fresh XML sitemap listing every live URL, so Search Console can re-discover your pages fast. The App Router generates one from a single file, again, straight from your inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/sitemap.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAllContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/robots.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;robots&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Robots&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yoursite.com/sitemap.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The classic launch-day disaster&lt;/strong&gt;, A staging site almost always ships with &lt;code&gt;Disallow: /&lt;/code&gt; or a site-wide &lt;code&gt;noindex&lt;/code&gt;. If that config reaches production, Google will &lt;em&gt;deindex your entire site&lt;/em&gt;. Before launch, confirm production returns &lt;code&gt;index,follow&lt;/code&gt; and an &lt;code&gt;allow: /&lt;/code&gt; robots rule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 6: Don’t lose your images
&lt;/h2&gt;

&lt;p&gt;Images carry ranking weight through image search, alt text, and Core Web Vitals. Two traps: broken image URLs after you move hosts, and dropped alt text. Your inventory should already include every image URL and its alt attribute, bundle the actual files, rewrite the URLs to their new home, and keep the alt text attached to each image.&lt;/p&gt;

&lt;p&gt;If you’re moving off the WordPress uploads directory entirely, &lt;strong&gt;Migratik Pro’s one-click media bundle&lt;/strong&gt; does this in a single step: it downloads every referenced image, mirrors the folder structure, and rewrites the URLs in your content to the bundled copies, so nothing 404s when the old &lt;code&gt;/wp-content/uploads/&lt;/code&gt; path disappears. Then serve them through &lt;code&gt;next/image&lt;/code&gt; for automatic optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Launch, then verify like your traffic depends on it
&lt;/h2&gt;

&lt;p&gt;It does. Ship, then work this checklist in the first 48 hours:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Submit the new sitemap&lt;/strong&gt; in Google Search Console and request indexing for your top pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot-check 301s&lt;/strong&gt;: hit a sample of old URLs and confirm each lands on the right page with a single 301 hop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crawl the new site&lt;/strong&gt; (Screaming Frog or similar) and diff the URL list against your inventory, anything missing is a gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify metadata&lt;/strong&gt; on live pages: titles, descriptions, canonicals, and JSON-LD all present.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch Coverage &amp;amp; 404s&lt;/strong&gt; in Search Console daily for two weeks. A spike in “Not found” means a redirect you missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the old site reachable&lt;/strong&gt; for a short window if you can, so nothing 404s while DNS and caches settle.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The mistakes that actually tank rankings
&lt;/h2&gt;

&lt;p&gt;Almost every post-migration traffic drop traces back to one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Changing URLs without 301s&lt;/strong&gt;, the number-one killer. Every orphaned URL is a ranking deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incomplete content migration&lt;/strong&gt;, pages that render empty because a builder’s shortcodes never executed in the export.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dropped metadata&lt;/strong&gt;, new templates that forget titles, descriptions, or canonicals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leftover &lt;code&gt;noindex&lt;/code&gt; / &lt;code&gt;Disallow&lt;/code&gt;&lt;/strong&gt; from staging reaching production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken images&lt;/strong&gt; when the old uploads path disappears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redirecting everything to the homepage&lt;/strong&gt; instead of true equivalents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice the pattern: every one is a &lt;em&gt;preservation&lt;/em&gt; failure, and every one is preventable with a complete inventory captured before you start. Get Step 1 right and the rest is mechanical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Will migrating from WordPress to Next.js hurt my SEO?
&lt;/h3&gt;

&lt;p&gt;Not if you preserve what Google already trusts. Keep your URLs identical, 301-redirect anything that changes, carry over your titles, descriptions and canonicals, and rebuild your sitemap. Rankings drop when those signals are lost, not because the frontend changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I keep my WordPress URLs in Next.js?
&lt;/h3&gt;

&lt;p&gt;Mirror your permalink structure with the App Router’s dynamic segments and generate one page per slug with &lt;code&gt;generateStaticParams()&lt;/code&gt;, using your existing slugs. Match WordPress’s trailing-slash convention with &lt;code&gt;trailingSlash&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set up 301 redirects in Next.js?
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;redirects()&lt;/code&gt; function in &lt;code&gt;next.config.js&lt;/code&gt; with &lt;code&gt;permanent: true&lt;/code&gt; for a handful of rules, or match a generated redirect map in &lt;code&gt;middleware.ts&lt;/code&gt; (or your host’s redirect layer) for large lists.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I export my WordPress content for Next.js?
&lt;/h3&gt;

&lt;p&gt;Export it as clean JSON or Markdown with the rendered content and SEO metadata intact, a tool like &lt;a href="https://hossamhamdy99.github.io/migratik-site/index.html" rel="noopener noreferrer"&gt;Migratik&lt;/a&gt; packs your whole site into one file that powers your routes, metadata and redirects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Start with a clean inventory&lt;/strong&gt; (Migratik exports your whole WordPress site) rendered content, SEO metadata, permalinks, images, and links, into one file that powers your redirects, metadata, and sitemap. Free to install, read-only, and safe on a live site. &lt;a href="https://wordpress.org/plugins/migratik/" rel="noopener noreferrer"&gt;Install Migratik free on WordPress.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>wordpress</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>seo</category>
    </item>
  </channel>
</rss>
