Connect to multiple WordPress backends from a single Next.js application for microservices, multi-tenant, and migration scenarios.
NextPress supports connecting to multiple WordPress backends from a single Next.js application. This is useful for:
Configure multiple instances in your Next.js config:
// next.config.mjs import { withWCR } from '@axistaylor/nextpress/withWCR'; const nextConfig = {}; export default withWCR(nextConfig, { instances: { default: { wpDomain: 'main.example.com', wpProtocol: 'https', }, blog: { wpDomain: 'blog.example.com', wpProtocol: 'https', }, shop: { wpDomain: 'shop.example.com', wpProtocol: 'https', wpSiteUrl: 'https://shop.example.com/wp', // Bedrock setup }, }, frontendDomain: 'app.example.com', frontendProtocol: 'https', });
Each instance accepts the same options as single-instance configuration:
| Option | Type | Required | Description |
|---|---|---|---|
wpDomain | string | Yes | WordPress domain |
wpProtocol | 'http' | 'https' | Yes | Protocol |
wpHomeUrl | string | No | Home URL (defaults to protocol://domain) |
wpSiteUrl | string | No | Site URL (defaults to wpHomeUrl) |
Pass the instance prop to specify which WordPress backend to use:
import { Content, WPHead, WPFooter } from '@axistaylor/nextpress'; // Use the blog instance <Content content={content} instance="blog" /> <WPHead scripts={scripts} stylesheets={stylesheets} globalStyles={globalStyles} importMap={importMap} instance="blog" pathname={uri} /> <WPFooter scripts={scripts} instance="blog" pathname={uri} />
If no instance prop is provided, components use 'default':
// These are equivalent <Content content={content} /> <Content content={content} instance="default" />
Organize your app with route groups for each WordPress instance:
app/
├── (main)/ # Default WordPress instance
│ ├── layout.tsx
│ └── [[...uri]]/
│ └── page.tsx
├── (blog)/ # Blog WordPress instance
│ ├── layout.tsx
│ └── [[...uri]]/
│ └── page.tsx
└── (shop)/ # Shop WordPress instance
├── layout.tsx
└── [[...uri]]/
└── page.tsx
// app/(blog)/layout.tsx import { WPHead, WPFooter } from '@axistaylor/nextpress'; import { headers } from 'next/headers'; import { fetchAssets, fetchGlobalStyles } from '@/lib/wordpress'; const INSTANCE = 'blog'; export default async function BlogLayout({ children, }: { children: React.ReactNode; }) { const headersList = await headers(); const uri = headersList.get('x-uri') || '/'; const [{ scripts, stylesheets, importMap }, globalStyles] = await Promise.all([ fetchAssets(uri, INSTANCE), fetchGlobalStyles(INSTANCE), ]); return ( <html lang="en"> <head> <WPHead scripts={scripts} stylesheets={stylesheets} globalStyles={globalStyles} importMap={importMap} instance={INSTANCE} pathname={uri} /> </head> <body> {children} <WPFooter scripts={scripts} instance={INSTANCE} pathname={uri} /> </body> </html> ); }
// lib/wordpress.ts const GRAPHQL_ENDPOINTS: Record<string, string> = { default: 'https://main.example.com/graphql', blog: 'https://blog.example.com/graphql', shop: 'https://shop.example.com/graphql', }; export async function fetchAssets(uri: string, instance: string = 'default') { const endpoint = GRAPHQL_ENDPOINTS[instance]; const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: ` query GetAssets($uri: String!) { uriAssets(uri: $uri) { scripts { ... } stylesheets { ... } } } `, variables: { uri }, }), }); const { data } = await response.json(); return { scripts: data?.uriAssets?.scripts || [], stylesheets: data?.uriAssets?.stylesheets || [], }; }
The proxy automatically routes requests based on the instance slug in the URL:
/atx/default/wp-json/wp/v2/posts → main.example.com
/atx/blog/wp-json/wp/v2/posts → blog.example.com
/atx/shop/wc?wc-ajax=add_to_cart → shop.example.com
No additional proxy configuration is needed - just ensure your proxy.ts matcher includes the instance pattern:
export const config = { matcher: [ '/atx/:instance/wp', '/atx/:instance/wc', '/atx/:instance/wp-json/:path*', '/atx/:instance/wp-assets/:path*', // ... ], };
Assets are automatically rewritten to use the correct proxy path:
// Original WordPress URL
https://blog.example.com/wp-content/uploads/image.jpg
// Rewritten for proxy
/atx/blog/wp-assets/wp-content/uploads/image.jpg
This happens automatically when you pass the instance prop to components.
For different instances per environment:
// next.config.mjs const instances = { default: { wpDomain: process.env.WP_DOMAIN_DEFAULT, wpProtocol: process.env.WP_PROTOCOL || 'https', }, blog: { wpDomain: process.env.WP_DOMAIN_BLOG, wpProtocol: process.env.WP_PROTOCOL || 'https', }, }; export default withWCR(nextConfig, { instances, frontendDomain: process.env.FRONTEND_DOMAIN, frontendProtocol: process.env.FRONTEND_PROTOCOL || 'https', });
import { getWPInstance } from '@axistaylor/nextpress'; const instance = getWPInstance('blog'); // { wpDomain, wpProtocol, wpHomeUrl, wpSiteUrl }
Instance configuration is available through the proxy URL pattern - client components don't need direct access to configuration.
Use consistent instance names - Keep names short and descriptive (blog, shop, docs)
Organize by route groups - Use Next.js route groups to separate instances
Share utilities - Create shared GraphQL utilities that accept instance parameter
Handle errors gracefully - Check for valid instance before making requests
Consider caching - Each instance may have different caching requirements