20 minutos. Cinco ficheiros. A create-next-app projeto puxa posts de uma instalação WordPress sobre WPGraphQL, o editor ainda possui cada parágrafo, e next build envia páginas estáticas com revalidate Preparado para o número que quiseres. A passagem abaixo dos pontos em woographqldemo.wpengine.com — trocar no seu próprio domínio no final e aplicar as mesmas seis etapas.
O que está na caixa?
@axistaylor/nextpress é um pacote npm e cerca de 6.000 linhas de TypeScript. Ele carrega três peças que você fio em um app Next.js 16:
withWCR— aNextConfigwrapper que configura o domínio WP reverso-proxy e para frente/wp-content,/wp-admin, e/graphqlatravés da próxima corrida.<Content>— um componente React que toma o HTML WordPress bruto retorna e o torna como nós React reais, além do CSS por bloco que cada um precisa.nextImageParser— um analisador que<img>no conteúdo WP para umanext/imagecomwidth,height, esizesretirado dos atributos do bloco.
Requisitos de infra-estrutura: WordPress com WPGraphQL activo. Qualquer máquina funciona — WP Engine, Pantheon, Bluehost, um VPS autogerido — desde que /graphql responde.
Passo 1 — Bootstrap o aplicativo seguinte
Unidade populacional create-next-app com o modelo TypeScript, em seguida, adicionar @axistaylor/nextpress do npm:
npx create-next-app@latest nextpress-quickstart
cd nextpress-quickstart
npm install @axistaylor/nextpressEtapa 2 — next.config.ts
withWCR leva três argumentos: a base Próxima configuração, a origem WordPress, ea origem frontend voltado para o público. O wrapper reescreve cada URL WordPress absoluta o editor de autoria em um caminho de frontend-relativo para links dentro renderizado conteúdo terra de volta em suas próximas rotas.
import type { NextConfig } from "next";
import { withWCR } from "@axistaylor/nextpress/withWCR";
const wpDomain = "woographqldemo.wpengine.com";
const wpProtocol = "https";
const nextConfig: NextConfig = {
images: {
formats: ["image/avif", "image/webp"],
remotePatterns: [{ protocol: "https", hostname: wpDomain }],
},
env: {
GRAPHQL_ENDPOINT: `${wpProtocol}://${wpDomain}/graphql`,
},
};
export default withWCR(
nextConfig,
{
wpDomain,
wpProtocol,
wpHomeUrl: `${wpProtocol}://${wpDomain}`,
wpSiteUrl: `${wpProtocol}://${wpDomain}`,
},
{
frontendDomain: "localhost:3000",
frontendProtocol: "http",
},
);Etapa 3 — src/proxy.ts
Próxima 16 renomeada middleware.ts para proxy.ts; o papel e o comportamento do fósforo não mudaram. O encarregado faz duas coisas — encaminhar /wp-* caminhos através proxyByWCR para que o navegador nunca toca a origem WordPress, e definir um x-uri cabeçalho em cada requisição não-proxied para componentes do servidor a jusante saber qual URI para pedir a infra- estrutura para scripts e folhas de estilo enqueued:
import { NextResponse, NextRequest } from "next/server";
import { proxyByWCR, isProxiedRoute } from "@axistaylor/nextpress/proxyByWCR";
export const proxy = async (request: NextRequest) => {
const pathname = request.nextUrl.pathname;
if (isProxiedRoute(pathname)) {
return proxyByWCR(request);
}
const headers = new Headers(request.headers);
headers.set("x-uri", pathname);
return NextResponse.next({ request: { headers } });
};
export const config = {
matcher: [
"/atx/:instance/proxiee",
"/atx/:instance/wp",
"/atx/:instance/wc",
"/atx/:instance/wp-internal-assets/:path*",
"/atx/:instance/wp-assets/:path*",
"/atx/:instance/wp-json/:path*",
"/((?!_next|api|favicon.ico|sw.js|.*\\.).*)",
],
};Etapa 4 — src/lib/wp.ts
Três funções async, uma compartilhada fetch Embrulho. fetchPosts alimenta a página de índice, fetchPostBySlug alimenta a página postal, fetchPostSlugs unidades generateStaticParams. next: { revalidate: 60 } dica sobre a fetch é o único controle de cache que você precisa — O próximo lida com o resto:
const ENDPOINT = process.env.GRAPHQL_ENDPOINT!;
async function gql<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
next: { revalidate: 60 },
});
if (!res.ok) throw new Error(`WPGraphQL ${res.status}`);
const { data, errors } = await res.json();
if (errors?.length) throw new Error(errors[0].message);
return data as T;
}
export interface PostSummary {
slug: string;
title: string;
date: string;
excerpt: string | null;
featuredImage: { sourceUrl: string; altText: string } | null;
}
export interface Post extends PostSummary {
content: string;
contentCssClasses: string;
}
export async function fetchPosts(first = 20): Promise<PostSummary[]> {
const data = await gql<{ posts: { nodes: PostSummary[] } }>(`
query Posts($first: Int!) {
posts(first: $first, where: { status: PUBLISH, orderby: { field: DATE, order: DESC } }) {
nodes {
slug title date excerpt
featuredImage { node { sourceUrl altText } }
}
}
}`, { first });
return data.posts.nodes;
}
export async function fetchPostSlugs(): Promise<string[]> {
const data = await gql<{ posts: { nodes: { slug: string }[] } }>(`
{ posts(first: 100, where: { status: PUBLISH }) { nodes { slug } } }`);
return data.posts.nodes.map(n => n.slug);
}
export async function fetchPostBySlug(slug: string): Promise<Post | null> {
const data = await gql<{ post: Post | null }>(`
query Post($slug: ID!) {
post(id: $slug, idType: SLUG) {
slug title date excerpt
content(format: RENDERED)
contentCssClasses
featuredImage { node { sourceUrl altText } }
}
}`, { slug });
return data.post;
}Etapa 5 — /blog índice
Um componente de servidor, um awaitNada de ganchos de clientes. O array de post é apenas JSX:
import Link from "next/link";
import { fetchPosts } from "@/lib/wp";
export const metadata = { title: "Blog" };
export default async function BlogIndex() {
const posts = await fetchPosts(20);
return (
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
<time>{new Date(post.date).toLocaleDateString()}</time>
</li>
))}
</ul>
);
}Etapa 6 — /blog/[slug]
generateStaticParams transforma cada lesma publicada numa rota de construção. A renderização é de duas linhas: um cabeçalho e um <Content>.
import { notFound } from "next/navigation";
import { Content, nextImageParser } from "@axistaylor/nextpress";
import { fetchPostBySlug, fetchPostSlugs } from "@/lib/wp";
export async function generateStaticParams() {
const slugs = await fetchPostSlugs();
return slugs.map((slug) => ({ slug }));
}
export default async function PostPage({ params }: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await fetchPostBySlug(slug);
if (!post) notFound();
return (
<article>
<h1>{post.title}</h1>
<Content
content={post.content}
contentCssClasses={post.contentCssClasses}
parsers={[nextImageParser()]}
/>
</article>
);
}Cinco arquivos, dois domínios, um npx. O editor nunca se move; a camada de entrega muda por baixo dela.

O quê? <Content> faz para você
O corpo do post é uma cadeia de HTML bloco WordPress. Colando-o em um dangerouslySetInnerHTML mas perderíamos três coisas no processo. <Content> Controla-os:
- Processa o HTML em uma árvore de Reagir. Cada
<p>,<figure>,<blockquote>é um nó real React que você pode substituir com um analisador — é assimnextImageParserswaps<img>em veznext/imagecom zero de marcação muda a montante. - Puxa o CSS de cada bloco para a resposta. Os suportes de bloco em linha regras, tokens paleta theme.json, folhas injetadas por plugins — eles enviam em linha com a página para que não haja mudança de layout após a hidratação.
- Manter
<script type="application/json">manchas intactas. É assim que os blocos interativos (motion engines, acordeons, qualquer coisa com um view-script) de mão estado para o seu cliente-side código sem uma viagem de volta API separada.
Próximos movimentos
Os mesmos seis arquivos se estendem em algumas direções óbvias. Categorias e etiquetas são mais uma WPGraphQL where argumento sobre fetchPosts. Comentários e passeio pagination junto na mesma consulta. A pesquisa atinge o search argumento e você envia um /search?q=... rota daqui a 20 minutos.
Fonte completa ligada GitHub. Documentos de embalagem em /docs/nextpress. Se você quer o motivo arquitetônico por trás de manter WordPress e adicionar Next.js como uma camada de entrega, essa peça está chegando.

Leave a Reply