Skip to content
WooGraphQL
  • Support
  • Blog
Get ProSign in
Get Pro
WooGraphQL

The original WPGraphQL extension for WooCommerce. Maintained by the open-source community and AxisTaylor.

Product
WooGraphQL ProCreate WooNext AppPricingNextPress
Developers
DocumentationPlaygroundSchema ExplorerGitHub
Company
BlogSupportConsultancy
Legal
AboutEditorial StandardsPrivacyTermsContact
© 2021–2026 WPGraphQL for WooCommerce. GPLv3 licensed.·Built byAxisTaylor
[Valid RSS]

Plugin + JS packageServer-rendered Gutenberg, no escape hatches

Render WordPress Gutenberg in Next.js — 1:1

NextPress is a WPGraphQL extension plus a Next.js package that brings full Gutenberg fidelity to your headless app. theme.json typography, layered stylesheets, block-supports CSS, the Interactivity API — all rendered server-side.

$composer require axistaylor/nextpress-wordpress

$npm install @axistaylor/nextpress

localhost:3000/about Acme About Docs Shop Contact ⌖ rendered by NextPress About Acme Family-owned shop in Portland, Oregon — since 2014. Our story We started in a garage. Twelve years later, we still hand-pack every order. The site you’re looking at? Edited in WordPress, served by Next.js. Same blocks, same theme, same fonts. No double-build. Hand-packed in Portland Every order ships within 48 hours. Carbon-neutral postage included. Read more From the journal → How we source our linen → Five years of mended hems → Notes from the workshop, Spring ’26

// what NextPress does

Full Gutenberg, no compromises.

Theme.json, asset dependencies, block-supports CSS, the Interactivity API — NextPress handles the whole stack so your headless pages match the editor preview exactly.

Server-rendered Gutenberg

Async server component. WordPress’s block HTML lands in your Next.js tree at request time with no client hydration tax.

Theme.json fidelity

Palettes, font sizes, spacing, fluid typography — the same tokens the editor uses, threaded through your headless page as CSS variables.

Style isolation

@scope ([data-rendered]) keeps WP’s reset and block styles inside the rendered area, so your app shell stays untouched.

Interactivity API

Script modules + import map for type="module" Gutenberg interactivity blocks — cart drawers, navigation, accordions all work natively.

// four steps

Wire it in, render the page.

— 01 / Install

Install the plugin

Activates templateByUri, globalStyles, and the asset-bridge queries on your WPGraphQL endpoint.

shell
wp plugin install axistaylor-nextpress --activate
# ✓ NextPress activated · GraphQL extensions registered

— 02 / Proxy

Configure the proxy

Forward proxied WP asset URLs through Next.js and thread the cart session into rendered pages.

proxy.ts
import { NextRequest, NextResponse } from 'next/server'
import { proxyByWCR, isProxiedRoute } from '@axistaylor/nextpress/proxyByWCR'

export const proxy = async (request: NextRequest) => {
  const { pathname } = request.nextUrl
  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/wp-assets/:path*',
    '/atx/:instance/wp-internal-assets/:path*',
    '/atx/:instance/wp-json/:path*',
    '/atx/:instance/wp',
    '/atx/:instance/wc',
    '/((?!_next|api|favicon.ico|.*\\.).*)',
  ],
}

— 03 / Wire

Wire the layout

Drop the head and footer slots into your root layout. NextPress emits the stylesheets, scripts, and import map.

app/layout.tsx
import { WPHead, WPFooter } from '@axistaylor/nextpress'
import { fetchAssets, fetchGlobalStyles } from '@/lib/utils'
import { headers } from 'next/headers'

export default async function RootLayout({ children }) {
  const uri = (await headers()).get('x-uri') ?? '/'
  const [{ stylesheets, scripts, importMap }, globalStyles] =
    await Promise.all([fetchAssets(uri), fetchGlobalStyles()])

  return (
    <html>
      <head>
        <WPHead
          scripts={scripts}
          stylesheets={stylesheets}
          globalStyles={globalStyles}
          importMap={importMap}
          pathname={uri}
          criticalHandles={['theme', 'wp-block-library']}
        />
      </head>
      <body>
        {children}
        <WPFooter scripts={scripts} pathname={uri} />
      </body>
    </html>
  )
}

— 04 / Render

Render the page

Your [...uri] catch-all becomes a one-liner. NextPress resolves the template, fetches the block tree, returns the matching React.

app/[…uri]/page.tsx
import { Content, nextImageParser } from '@axistaylor/nextpress'
import { fetchContentByUri } from '@/lib/utils'

export default async function CatchAllPage({
  params,
}: {
  params: Promise<{ uri?: string[] }>
}) {
  const { uri } = await params
  const path = '/' + (uri ?? []).join('/')
  const data = await fetchContentByUri(path)
  if (!data) return null

  return (
    <Content
      content={data.content}
      contentCssClasses={data.contentCssClasses}
      parsers={[nextImageParser]}
    />
  )
}

// coverage

If WordPress can render it, NextPress can too.

NextPress doesn’t care about block names. It walks the block tree, applies the same supports CSS WordPress would, and emits matching React. Custom blocks work out of the box.

theme.json palettesglobalStyles tokenscore blocksblock-supports CSSscript modulesInteractivity APIcustom blocksclassic-editor HTMLreusable blockstemplate partsblock patternsasset dependency order

// with WooGraphQL

Bring the WooCommerce blocks with you.

NextPress renders any Gutenberg block, including the WooCommerce ones. Paired with WooGraphQL, the headless session threads into the rendered block context — so the WooCommerce Cart, Checkout, My Account, and product blocks read the same session your Next.js app does. No double sign-in, no proxy hacks.

  • WC Cart / Checkout / My Account blocks render against the user’s real session.
  • Cart token shared between the Next app and the rendered block via @woographql/session-utils.
  • Product blocks, filters, mini-cart — same WPGraphQL endpoint, no parallel REST surface.
  • Block-level mutations (add to cart, apply coupon) round-trip through WooGraphQL.
app/[…uri]/page.tsx
import { Content, nextImageParser } from '@axistaylor/nextpress'
import { fetchContentByUri } from '@/lib/utils'
import { NextPressLink } from '@/components/NextPressLink'

// proxyByWCR (NextPress middleware) forwards the cart-token
// cookie to WordPress for matching URIs, so the WP renderer
// reads the same session as your Next app. WC Cart, Checkout,
// My Account, and product blocks all render against the user's
// real cart — no withSession wrapper required.
export default async function Page({
  params,
}: {
  params: Promise<{ uri?: string[] }>
}) {
  const { uri } = await params
  const path = '/' + (uri ?? []).join('/')
  const { content, contentCssClasses } = await fetchContentByUri(path)

  return (
    <Content
      content={content}
      contentCssClasses={contentCssClasses}
      instance="shop"
      linksAs={NextPressLink}
      parsers={[nextImageParser]}
    />
  )
}

Build with NextPress

Installation, configuration, the asset-bridging API, and the full catalogue of blocks NextPress understands — all in the docs.

Read the docs
View on GitHub