Migrei mais de uma dúzia de sites WordPress para Next.js nos últimos três anos. Alguns correram suavemente. Alguns me fizeram questionar minhas escolhas de carreira às 2 da manhã de uma terça-feira. A diferença entre esses dois resultados quase sempre se resumiu ao planejamento — especificamente, entender o que WordPress estava realmente fazendo pelo site antes de arrancá-lo.

Este guia é tudo que gostaria que alguém tivesse me entregue antes da minha primeira migração. Cobriremos a jornada completa: avaliar se você deveria sequer migrar, escolher seu CMS headless, mover conteúdo, reconstruir templates, lidar com SEO sem perder rankings e fazer deployment no Vercel com uma configuração que não desabará sob picos de tráfego.

Vamos começar.

WordPress to Next.js Migration on Vercel: A 2026 Guide

Índice

Por que migrar do WordPress para Next.js em 2026?

Sejamos honestos — WordPress ainda alimenta aproximadamente 40% da web em 2026. Não vai embora tão cedo. Mas as razões para sair ficaram mais convincentes:

Teto de performance. Mesmo com plugins de cache agressivo (WP Rocket, W3 Total Cache), a maioria dos sites WordPress atinge um limite em torno de 70-80 em scores de performance do Lighthouse. Excesso de plugins, PHP renderizando bloqueios e queries de banco de dados em cada carregamento de página criam overhead que nenhuma quantidade de otimização consegue eliminar completamente.

Superfície de segurança. WordPress teve 149 vulnerabilidades documentadas em 2025 em core e plugins populares. Cada plugin é um vetor de ataque. Cada atualização de tema é um possível break. Se você está rodando WooCommerce, a superfície dobra.

Experiência do desenvolvedor. Se seu time conhece React, construir em templates PHP se sente como escrever com sua mão não-dominante. O App Router do Next.js 15, Server Components e cache integrado oferecem um workflow de desenvolvimento moderno que WordPress não consegue igualar.

Custo em escala. Hospedagem WordPress gerenciada (WP Engine, Kinsta) custa $30-$300/mês para performance decente. O plano Pro do Vercel a $20/usuário/mês com edge functions e scaling automático frequentemente custa menos enquanto perfoma melhor.

Dito isto — não migre apenas porque é tendência. Se seu site é um blog simples com 50 posts e seu cliente o atualiza semanalmente via admin do WordPress, uma migração pode criar mais problemas do que resolve. Os melhores candidatos para migração são:

  • Sites com 500+ páginas que precisam de melhor performance
  • Times que querem desenvolvimento baseado em componentes
  • Sites onde conflitos de plugins estão causando pesadelos de manutenção
  • Sites de e-commerce atingindo limites de performance do WooCommerce
  • Sites de marketing que precisam de A/B testing e personalização na edge

Auditoria pré-migração: O que WordPress está realmente fazendo

É aqui que a maioria das migrações dá errado. As pessoas pensam que estão migrando um blog, mas WordPress está realmente manipulando formulários de contato, redirects, otimização de imagens, busca, comentários, autenticação, cron jobs e quinze outras coisas enterradas em configurações de plugin.

Antes de escrever uma única linha de código Next.js, documente tudo:

Inventário de plugins

Exporte sua lista de plugins e categorize cada um:

wp plugin list --status=active --format=csv > active-plugins.csv

Para cada plugin, responda: O que isto faz, e o que o substitui no ecossistema Next.js?

Plugin WordPress Função Substituição Next.js
Yoast SEO Meta tags, sitemaps, schema next-seo + rota sitemap.xml customizada
WP Rocket Cache, minificação Vercel Edge Cache + Next.js integrado
Contact Form 7 Manipulação de formulário React Hook Form + rota API ou Formspree
Wordfence Segurança Não necessário (sem superfície admin)
WPML Multilíngue next-intl ou roteamento i18n integrado
WooCommerce E-commerce Shopify Storefront API ou Saleor
Advanced Custom Fields Modelos de conteúdo customizado Modelagem de conteúdo do seu CMS headless
Redirection URL redirects next.config.js redirects ou config Vercel
WP Cron Tarefas agendadas Vercel Cron Jobs ou serviço separado
Imagify Otimização de imagem next/image com Vercel Image Optimization

Inventário de conteúdo

Contabilize e categorize seu conteúdo:

SELECT post_type, post_status, COUNT(*) 
FROM wp_posts 
GROUP BY post_type, post_status;

Não esqueça: custom post types, termos de taxonomy, perfis de usuário, estruturas de menu, configurações de widget e valores de option. Aquele site WordPress "simples" provavelmente tem mais tipos de conteúdo do que você pensa.

Mapeamento de URL

Exporte toda URL. Toda uma. Use Screaming Frog ou um simples crawl de sitemap:

curl -s https://seusite.com/sitemap_index.xml | \
grep -oP '<loc>\K[^<]+' | \
xargs -I {} curl -s {} | \
grep -oP '<loc>\K[^<]+' > all-urls.txt

Este arquivo é ouro. Você o usará para mapeamento de redirect, preservação de SEO e testes de QA após migração.

WordPress to Next.js Migration on Vercel: A 2026 Guide - architecture

Escolhendo seu backend CMS headless

Você precisa de um lugar para armazenar e gerenciar conteúdo. Os três caminhos mais comuns em 2026:

Opção 1: WordPress como CMS headless

Sim, você pode manter WordPress como o backend e usar Next.js como frontend. WPGraphQL (agora em v2.1) torna isso surpreendentemente viável. Seus editores mantêm a interface admin familiar. Você ganha um frontend moderno.

// lib/wordpress.js
const API_URL = process.env.WORDPRESS_GRAPHQL_URL;

export async function getPostBySlug(slug) {
  const res = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query PostBySlug($slug: ID!) {
          post(id: $slug, idType: SLUG) {
            title
            content
            date
            featuredImage {
              node {
                sourceUrl
                altText
              }
            }
            seo {
              title
              metaDesc
              opengraphImage {
                sourceUrl
              }
            }
          }
        }
      `,
      variables: { slug },
    }),
    next: { revalidate: 60 },
  });
  const json = await res.json();
  return json.data.post;
}

A desvantagem? Você ainda mantém uma instalação WordPress. Atualizações de segurança, gerenciamento de versão PHP, backups de banco de dados — tudo continua em seu prato. E você continua pagando pela hospedagem WordPress.

Opção 2: CMS headless construído para propósito

Isso é o que recomendo para a maioria das migrações. Mova seu conteúdo para um CMS que foi construído do zero para entrega API-first.

CMS Preço (2026) Melhor para Modelagem de conteúdo Tipo API
Sanity Tier grátis, $15/usuário/mês Pro Conteúdo complexo, colaboração em tempo real Excelente, definida em código GROQ + GraphQL
Contentful Tier grátis, $300/mês Team Enterprise, times grandes Boa, definida em UI REST + GraphQL
Storyblok Tier grátis, €106/mês Business Edição visual, componentes Ótima, visual REST + GraphQL
Strapi v5 Grátis (auto-hospedado), Cloud a partir de $29/mês Controle total, open source Flexível, definida em UI REST + GraphQL
Payload CMS 3.0 Grátis (auto-hospedado) Desenvolvedores que querem code-first Excelente, definida em código REST + GraphQL

Se seu time na Social Animal está lidando com a migração, tipicamente recomendamos Sanity ou Payload para desenvolvimento de CMS headless — eles dão aos desenvolvedores o máximo controle sobre modelagem de conteúdo enquanto mantêm editores felizes.

Opção 3: Markdown/MDX no repositório

Para blogs de desenvolvedores e sites de documentação, armazenar conteúdo como arquivos MDX em seu repositório Git é a abordagem mais simples. Nenhum CMS para gerenciar, sem chamadas de API, conteúdo versionado junto com código. Mas isso só funciona se seus editores de conteúdo estão confortáveis com workflows Git.

Estratégia de migração de conteúdo

Exportando do WordPress

A exportação integrada do WordPress (Tools → Export) oferece um arquivo XML. É um começo, mas é bagunçado. Para migração estruturada, uso um script WP-CLI customizado:

// export-content.php
<?php
$posts = get_posts([
    'post_type' => ['post', 'page', 'your_custom_type'],
    'posts_per_page' => -1,
    'post_status' => 'publish',
]);

$export = [];
foreach ($posts as $post) {
    $export[] = [
        'id' => $post->ID,
        'title' => $post->post_title,
        'slug' => $post->post_name,
        'content' => apply_filters('the_content', $post->post_content),
        'excerpt' => $post->post_excerpt,
        'date' => $post->post_date,
        'modified' => $post->post_modified,
        'author' => get_the_author_meta('display_name', $post->post_author),
        'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
        'tags' => wp_get_post_tags($post->ID, ['fields' => 'names']),
        'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
        'acf' => function_exists('get_fields') ? get_fields($post->ID) : [],
        'yoast' => [
            'title' => get_post_meta($post->ID, '_yoast_wpseo_title', true),
            'description' => get_post_meta($post->ID, '_yoast_wpseo_metadesc', true),
        ],
    ];
}

file_put_contents('export.json', json_encode($export, JSON_PRETTY_PRINT));

Transformando conteúdo

Conteúdo WordPress é armazenado como HTML com markup de bloco Gutenberg. Você precisará decidir: manter o HTML e renderizá-lo, ou converter para o formato estruturado do seu CMS?

Para Sanity, uso @sanity/block-tools para converter HTML para Portable Text. Para Contentful, seu CLI de migração manipula conversão de rich text. De qualquer forma, reserve tempo para limpeza de conteúdo — conteúdo WordPress está cheio de [shortcodes], estilos inline e HTML quebrado que precisa ser limpo.

// migrate-to-sanity.js
import { htmlToBlocks } from '@sanity/block-tools';
import { JSDOM } from 'jsdom';
import { Schema } from '@sanity/schema';

const schema = Schema.compile({ /* seu schema */ });
const blockContentType = schema.get('post')
  .fields.find(f => f.name === 'body').type;

function convertPost(wpPost) {
  return {
    _type: 'post',
    title: wpPost.title,
    slug: { current: wpPost.slug },
    publishedAt: wpPost.date,
    body: htmlToBlocks(
      wpPost.content,
      blockContentType,
      { parseHtml: (html) => new JSDOM(html).window.document }
    ),
  };
}

Migração de imagens

Não pule isso. Baixe cada imagem de wp-content/uploads, re-upload para seu CMS ou uma CDN (Cloudinary, Vercel Blob Storage, S3) e atualize todas as referências de conteúdo. Tipicamente escrevo um script que:

  1. Rastreia a exportação JSON para URLs de imagem
  2. Baixa cada imagem
  3. Upload para o novo armazenamento
  4. Cria um arquivo de mapeamento de URL
  5. Executa find-and-replace em todo conteúdo

Reconstruindo seu frontend em Next.js 15

Next.js 15 (estável desde final de 2024, com 15.2 atual em 2026) usa o App Router por padrão. Aqui está a estrutura que uso para sites com muito conteúdo:

app/
├── layout.tsx          # Layout raiz com fontes, analytics
├── page.tsx            # Homepage
├── blog/
│   ├── page.tsx        # Listagem de blog com paginação
│   └── [slug]/
│       └── page.tsx    # Posts de blog individuais
├── [slug]/
│   └── page.tsx        # Páginas genéricas
├── sitemap.ts          # Geração dinâmica de sitemap
├── robots.ts           # robots.txt
└── not-found.tsx       # Custom 404

Geração estática com ISR

Para a maioria das páginas de conteúdo, Incremental Static Regeneration é o ponto ideal — performance estática com freshness dinâmica:

// app/blog/[slug]/page.tsx
import { getPostBySlug, getAllPostSlugs } from '@/lib/cms';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const slugs = await getAllPostSlugs();
  return slugs.map((slug) => ({ slug }));
}

export async function generateMetadata({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) return {};
  return {
    title: post.seo?.title || post.title,
    description: post.seo?.description || post.excerpt,
    openGraph: {
      images: [post.featuredImage?.url],
    },
  };
}

export const revalidate = 3600; // Revalidar a cada hora

export default async function BlogPost({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) notFound();

  return (
    <article className="prose lg:prose-xl">
      <h1>{post.title}</h1>
      <time dateTime={post.date}>{formatDate(post.date)}</time>
      <PostBody content={post.body} />
    </article>
  );
}

Para sites necessitando atualizações em tempo real (notícias, conteúdo ao vivo), use revalidação on-demand via webhooks do seu CMS. A maioria das plataformas CMS headless suportam triggers de webhook na publicação de conteúdo.

Se você está olhando para desenvolvimento Next.js e quer entender melhor os trade-offs de renderização, escrevemos sobre isso separadamente.

Estrutura de URL e preservação de SEO

Isso é inegociável. Se você perder sua estrutura de URL, você perde seus rankings de busca. Período.

Mapa de redirect

WordPress usa padrões de URL como /2024/03/post-slug/ ou /category/term/. Seu site Next.js provavelmente usa /blog/post-slug. Crie um mapa de redirect:

// next.config.js
module.exports = {
  async redirects() {
    return [
      // URLs baseadas em data para slugs limpos
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
      // Arquivos de categoria
      {
        source: '/category/:slug',
        destination: '/blog?category=:slug',
        permanent: true,
      },
      // Paginação
      {
        source: '/page/:num',
        destination: '/blog?page=:num',
        permanent: true,
      },
      // URLs de feed
      {
        source: '/feed',
        destination: '/rss.xml',
        permanent: true,
      },
    ];
  },
};

Para sites grandes (1000+ redirects), use a configuração vercel.json do Vercel ou middleware ao invés — redirects em next.config.js têm um limite suave em torno de 1024 entradas antes dos tempos de build começarem a sofrer.

Dados estruturados

Plugins do WordPress como Yoast geram JSON-LD automaticamente. Você precisa replicar isso:

// components/structured-data.tsx
export function ArticleSchema({ post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.date,
    dateModified: post.modified,
    author: {
      '@type': 'Person',
      name: post.author,
    },
    image: post.featuredImage?.url,
    publisher: {
      '@type': 'Organization',
      name: 'Nome do seu site',
      logo: { '@type': 'ImageObject', url: '/logo.png' },
    },
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

Sitemap XML

Next.js 15 torna sitemaps dinâmicos diretos:

// app/sitemap.ts
import { getAllPosts, getAllPages } from '@/lib/cms';

export default async function sitemap() {
  const posts = await getAllPosts();
  const pages = await getAllPages();

  return [
    { url: 'https://seusite.com', lastModified: new Date() },
    ...pages.map((page) => ({
      url: `https://seusite.com/${page.slug}`,
      lastModified: page.modified,
    })),
    ...posts.map((post) => ({
      url: `https://seusite.com/blog/${post.slug}`,
      lastModified: post.modified,
    })),
  ];
}

Deployment no Vercel: Configuração que realmente funciona

Configuração de projeto

npx create-next-app@latest my-migrated-site --typescript --tailwind --app
cd my-migrated-site
vercel link

Variáveis de ambiente

Defina estas no dashboard do Vercel, não em arquivos .env commitados ao Git:

CMS_API_URL=https://seu-endpoint-api-cms
CMS_API_TOKEN=seu-token-somente-leitura
REVALIDATION_SECRET=uma-string-aleatória-para-autenticacao-webhook
SITE_URL=https://seusite.com

Configuração Vercel

// vercel.json
{
  "crons": [
    {
      "path": "/api/revalidate-sitemap",
      "schedule": "0 */6 * * *"
    }
  ],
  "headers": [
    {
      "source": "/fonts/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ]
}

Webhook de revalidação on-demand

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-revalidation-secret');
  if (secret !== process.env.REVALIDATION_SECRET) {
    return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
  }

  const body = await request.json();
  const { slug, type } = body;

  if (type === 'post') {
    revalidatePath(`/blog/${slug}`);
    revalidatePath('/blog'); // Revalidar listagem também
  } else {
    revalidatePath(`/${slug}`);
  }

  return NextResponse.json({ revalidated: true });
}

Aponte seu webhook CMS para https://seusite.com/api/revalidate com o header secreto. Agora atualizações de conteúdo aparecem em segundos sem rebuilds completos.

Benchmarks de performance: Antes e depois

Esses são números reais de migrações que completamos na Social Animal em 2025-2026:

Métrica WordPress (WP Engine) Next.js (Vercel) Melhoria
Lighthouse Performance 62-78 95-100 +30-40%
Largest Contentful Paint 2.8-4.2s 0.8-1.4s 60-70% mais rápido
Time to First Byte 800ms-1.5s 50-120ms 90%+ mais rápido
Cumulative Layout Shift 0.12-0.25 0.01-0.05 ~80% redução
Custo de hospedagem mensal $115/mês média $20-40/mês 60-80% economia
Tempo de build (500 páginas) N/A (dinâmico) 45-90 segundos N/A
Páginas/segundo (ISR) 15-30 req/s 10,000+ da edge Ordens de magnitude

A melhoria de TTFB sozinha vale a migração. WordPress gera cada página através de PHP e MySQL. Vercel entrega páginas pré-renderizadas de nós de edge em 300+ locais mundialmente.

Armadilhas comuns de migração

Armadilha 1: Esquecer feeds RSS do WordPress. Se pessoas se inscrevem no seu feed, redirecione /feed/ e /rss/ para um novo endpoint RSS. Next.js não gera feeds por padrão — você precisa de uma rota customizada.

Armadilha 2: Perder shortcodes do WordPress. Conteúdo exportado do WordPress está repleto de [gallery], [embed] e shortcodes específicos de plugin. Se você não parsear e converter esses, eles renderizam como texto simples. Escreva transformers para cada tipo de shortcode que seu conteúdo usa.

Armadilha 3: Ignorar dados de comentários do WordPress. Se você tem threads de comentários valiosos, migre-os para um serviço como Disqus ou construa um sistema de comentários customizado. A maioria das pessoas apenas abandona comentários, mas verifique com stakeholders primeiro.

Armadilha 4: Não testar links internos. Conteúdo WordPress está cheio de links internos usando URLs absolutas (https://seusite.com/old-path/). Esses precisam ser atualizados para caminhos relativos ou sua nova estrutura de URL. Uma simples regex find-and-replace durante migração manipula a maioria dos casos.

Armadilha 5: Esquecer referências wp-content/uploads. Mesmo após migrar imagens, conteúdo antigo pode referenciar caminhos /wp-content/uploads/2024/03/image.jpg. Configure um redirect catch-all ou proxy esses para sua nova CDN de imagens.

Se isso parece avassalador, isso é honestamente normal. Uma migração apropriada leva 4-12 semanas dependendo da complexidade do site. Confira nossos preços ou entre em contato direto se você quer mãos experientes no projeto.

FAQ

Quanto tempo leva uma migração de WordPress para Next.js? Para um site com 100-500 páginas, espere 4-8 semanas com um desenvolvedor dedicado. Sites maiores com custom post types, e-commerce ou conteúdo multilíngue podem levar 8-16 semanas. A migração de conteúdo em si é geralmente 20% do trabalho — os outros 80% são reconstruir templates, manipular edge cases e testes de QA em cada URL.

Vou perder meus rankings do Google durante migração? Não se manipular redirects apropriadamente. Implemente 301 redirects para cada URL que muda, preserve seus títulos meta e descrições, submeta seu novo sitemap ao Google Search Console e use a ferramenta Change of Address se seu domínio mudar. Espere uma pequena flutuação de ranking por 2-4 semanas, depois recuperação ou melhoria conforme Google reconhece melhor Core Web Vitals.

Posso manter usando WordPress como meu CMS com Next.js? Absolutamente. Isso é chamado "WordPress headless" e é uma abordagem popular. Use WPGraphQL para expor seu conteúdo como uma API, então o consuma do Next.js. Seus editores mantêm o admin do WordPress que conhecem. A principal desvantagem é que você ainda mantém uma instalação WordPress — atualizações de segurança, hospedagem, toda stack.

Quanto custa migrar do WordPress para Next.js? DIY com um desenvolvedor: 100-300 horas de trabalho. Migração de agência: tipicamente $15.000-$75.000 dependendo da complexidade. Custos de hospedagem contínuos geralmente diminuem — Vercel Pro a $20/usuário/mês versus hospedagem WordPress gerenciada a $50-$300/mês. O ROI vem de custos de hospedagem reduzidos, melhor performance (que melhora taxas de conversão) e menor overhead de manutenção.

Devo usar Pages Router ou App Router em Next.js 15? App Router, sem dúvida. Em 2026, o App Router é o padrão estável com Server Components, streaming e rotas paralelas. O Pages Router ainda funciona e não está deprecado, mas novos features e otimizações são App Router-first. Toda migração que fazemos na Social Animal usa o App Router exclusivamente. Confira nossas capacidades de desenvolvimento Next.js para mais sobre nossa abordagem.

E quanto a formulários e funcionalidade dinâmica? Rotas API do Next.js (ou Server Actions no App Router) manipulam submissões de formulário, busca, autenticação e qualquer lógica server-side. Para formulários de contato, você pode usar Server Actions com um serviço como Resend para entrega de email. Para busca, considere Algolia ou Meilisearch. Para autenticação, NextAuth.js (agora Auth.js v5) cobre a maioria dos casos de uso.

Vercel é a única opção para hospedar Next.js? Não. Você pode fazer deployment de Next.js no Netlify, AWS Amplify, Cloudflare Pages ou auto-hospedar com Node.js. No entanto, Vercel é construído pelo time do Next.js, e a integração se mostra — ISR, edge middleware, otimização de imagem e analytics todos funcionam melhor no Vercel. A lacuna de DX entre Vercel e alternativas reduziu em 2026, mas Vercel continua o caminho de menor resistência.

E se eu precisar migrar uma loja WooCommerce? Este é um projeto maior. A maioria dos times migra a storefront para Next.js enquanto move o backend de comércio para Shopify (usando Storefront API), Medusa.js ou Saleor. Framework Hydrogen do Shopify é outra opção, mas se você quer controle total sobre o frontend, Next.js com a API do Shopify é o caminho mais provado. Espere a migração de e-commerce adicionar 4-8 semanas à sua timeline.