Next.js Page Router SDK
Looking for the latest Next.js SDK?
The Next.js App Router SDK v2 is the latest Uniform SDK for Next.js. It requires Next.js 16 and works exclusively with the App Router. If you are using the Page Router with Next.js 13--15, this Page Router SDK is the right choice and remains fully supported.
The Uniform SDK for Next.js Page Router provides a full-featured integration between Uniform's DXP and the Next.js Pages Router. It handles route resolution, component rendering, personalization, A/B testing, and visual editing -- with support for both Server-Side Rendering (SSR) and Static Site Generation (SSG) with Incremental Static Regeneration (ISR).
Packages overview#
The Page Router integration is split across several packages, each with a focused responsibility:
| Package | Purpose |
|---|---|
@uniformdev/canvas | Core composition data types, route client, and state constants |
@uniformdev/canvas-next | Next.js Page Router helpers: route resolution (getServerSideProps / getStaticProps / getStaticPaths), preview handler, and UniformRichText |
@uniformdev/canvas-react | React components and hooks: UniformComposition, UniformSlot, UniformText, registerUniformComponent, and contextual editing support |
@uniformdev/context | Uniform Context engine: visitor scoring, personalization manifest, quirks, and plugins |
@uniformdev/context-react | React bindings for Context: UniformContext provider, useQuirks, useScores, Personalize, Test, and Track components |
@uniformdev/context-next | Next.js-specific Context utilities: NextCookieTransitionDataStore for SSR cookie-based score persistence, enableNextSsr, and UniformAppProps type |
@uniformdev/project-map | Project map client for fetching site tree nodes for navigation, sitemaps, and static path generation |
How it works#
Understanding the request lifecycle helps you reason about configuration choices and debug issues effectively.
SSR mode (Server-Side Rendering)#
getServerSidePropsresolves the route -- When a visitor requests a page (e.g.,/about), the Uniform SDK helperwithUniformGetServerSidePropscalls the Uniform Route API, resolves the matching composition, and returns it as page props.The page component receives the composition -- The page component receives the full
RootComponentInstanceas props and passes it toUniformComposition.Components render via the component registry --
UniformCompositionwalks the composition tree and renders each component using the registered component resolver.Visual editing works out of the box -- When previewing in the Uniform dashboard, the SDK automatically enables contextual editing via Next.js preview mode.
SSG mode (Static Site Generation)#
In SSG mode, pages are pre-rendered at build time. getStaticPaths fetches all available paths from the Uniform Project Map, and getStaticProps resolves each composition. Pages can be incrementally regenerated using ISR. See Incremental Static Regeneration (ISR) for details.
Getting started#
Prerequisites#
- Next.js 13+ (Pages Router)
- Node.js 18+
- A Uniform project with compositions set up
Quick start options#
The fastest way to get up and running is to start from a working project rather than integrating from scratch.
Run this in your favorite terminal and select Next.js (Page Router):
This scaffolds a new project with all required content and files pre-configured.
You can also clone the starter manually from uniformdev/examples/nextjs-starter and follow the repo readme to set up manually.
Alternatively, if you prefer to add Uniform to an existing project manually, follow the steps below.
Install packages#
For CLI operations (sync, manifest download), also install:
Environment variables#
Create a .env.local file in your project root:
note
You can find these values in your Uniform project under Settings > API Keys. See the API keys guide for more information.
Project setup#
The Uniform SDK requires a specific set of files to wire up routing, rendering, and preview. Here is the minimal file structure:
The following sections walk through each file.
Step 1: Next.js configuration#
The Page Router integration does not require a custom Next.js config wrapper. A standard next.config.js works:
Step 2: Uniform Context factory#
Create lib/uniform/uniformContext.ts. This factory creates a Context instance that drives personalization, scoring, and quirks. In the Page Router, the manifest is downloaded locally as a JSON file (unlike the App Router, which fetches it at runtime):
Key points:
NextCookieTransitionDataStorepersists visitor scores and quirks in cookies, enabling SSR-compatible score transfer between client and server.- Pass
serverContextwhen creating the context on the server (in_document.tsxwithenableNextSsr) so the store can read cookies from the incoming request. - The
contextManifest.jsonfile is generated by the CLI and must be downloaded before builds (see Manifest management).
Step 3: App wrapper (_app.tsx)#
Create pages/_app.tsx. The UniformContext provider wraps your entire app and supplies the context engine to all components:
note
serverUniformContext is provided when enableNextSsr() is used in _document.tsx (see Server-side personalization below). If it is not provided, the client-side context is used as the fallback.
Step 4: Catch-all page route#
Create pages/[[...slug]].tsx. This is the main route handler that resolves compositions from the Uniform Route API. You can use either SSR or SSG mode.
SSR mode (recommended for getting started)#
SSG mode#
note
The param option in withUniformGetStaticProps must match your dynamic route segment name. For [[...slug]].tsx, use param: "slug".
Step 5: Page composition component#
Create components/PageComposition.tsx. This component receives the composition data from getServerSideProps or getStaticProps and renders it:
UniformComposition walks the composition tree and renders each component using the registered component resolver (see Component registration below).
Step 6: Preview handler#
Create pages/api/preview.ts to handle visual editing preview requests from the Uniform dashboard:
The preview handler:
- Validates the preview secret from the query string.
- Sets Next.js preview mode cookies (enabling draft content fetching).
- Redirects to the composition path or the playground page.
- Handles both
GET(preview entry) andPOST(live composition data from the editor) requests.
Step 7: Playground route#
Create pages/playground.tsx for visual editing and previewing individual compositions from the Uniform dashboard:
Step 8: Component registration#
Create components/canvasComponents.ts. This file imports all your Canvas-managed components so they are registered with the component store:
This file must be imported in _app.tsx (as a side-effect import) so all components are registered before any composition is rendered.
Building components#
Components in the Page Router SDK use the registerUniformComponent pattern. Each component registers itself with the global component store, which UniformComposition uses to resolve component types to React components at render time.
Component props#
Every component receives props through ComponentProps<TProps>:
| Prop | Type | Description |
|---|---|---|
component | ComponentInstance | The full component instance data (type, slots, parameters, _id, etc.) |
...parameters | TProps | Flattened parameter values spread directly onto props |
In the Page Router SDK, parameter values are flattened -- the raw value of each parameter is spread directly onto the component props. This differs from the App Router SDK, which wraps parameters in ComponentParameter<T>.
Basic component with parameters#
note
registerUniformComponent is a side-effect registration. The component registers itself when its module is imported. Make sure all component modules are imported in canvasComponents.ts.
Accessing raw parameter values#
Access parameter values directly from the component prop for non-visual values (URLs, boolean flags, etc.):
UniformText#
UniformText renders text parameters with built-in support for inline editing in the Uniform visual editor:
warning
In the Page Router SDK, UniformText uses parameterId (a string matching the parameter name in your component definition). This differs from the App Router SDK, which passes the parameter object directly.
UniformRichText#
UniformRichText renders rich text parameters with full formatting support. It is imported from @uniformdev/canvas-next (not canvas-react):
Slots#
Slots define where child components can be placed within a parent component. A page component typically has slots like content, header, and footer.
Rendering slots with UniformSlot#
note
In the Page Router SDK, UniformSlot uses the name prop (a string matching the slot name in your component definition). This differs from the App Router SDK, which passes a slot object.
Custom slot wrapper#
UniformSlot accepts a wrapperComponent prop for wrapping the list of rendered slot items:
Custom component resolver#
Instead of using registerUniformComponent, you can pass a custom resolveRenderer function:
When using resolveRenderer, you do not need registerUniformComponent or the canvasComponents.ts import file.
Data fetching helpers#
The SDK provides three main helpers for data fetching in the Page Router. These wrap the Uniform Route API and handle composition resolution, redirects, and not-found responses automatically.
withUniformGetServerSideProps#
Creates a getServerSideProps implementation that resolves compositions via the Uniform Route API on every request:
withUniformGetStaticProps#
Creates a getStaticProps implementation that resolves compositions at build time:
withUniformGetStaticPaths#
Creates a getStaticPaths implementation that fetches all paths from the Uniform Project Map:
Handler options#
All three helpers accept these common options:
| Option | Type | Description |
|---|---|---|
client | RouteClient | Override the default route client |
prefix | string | Strip a prefix from the resolved URL before route resolution |
modifyPath | (path, context) => string | Transform the path before sending it to the Route API |
projectMapId | string | Override the project map ID (defaults to UNIFORM_PROJECT_MAP_ID env var) |
requestOptions | Partial<RouteGetParameters> | Override route API request parameters (state, locale, etc.) |
silent | boolean | Disable logging of response information and timings |
handleComposition | function | Override handling of a composition route response |
handleRedirect | function | Override handling of a redirect route response |
handleNotFound | function | Override handling of a not-found route response |
Custom composition handling#
Use handleComposition to add extra data (like navigation links) to the page props:
withUniformGetStaticPaths options#
| Option | Type | Description |
|---|---|---|
projectMapId | string | The project map ID to query |
rootPath | string | Starting path to fetch nodes from |
prefix | string | Prepend this string to all returned paths |
preview | boolean | Include draft compositions |
requestOptions | Partial<ProjectMapNodeGetRequest> | Override request parameters |
callback | (nodes) => Promise<nodes> | Modify the node list before building paths |
client | ProjectMapClient | Override the default project map client |
redirectClient | RedirectClient | Override the default redirect client |
Localization#
The SDK supports Next.js Internationalized Routing. Use the prependLocale path modifier to automatically prepend the current locale to paths before sending them to the Uniform Route API:
This works with both withUniformGetServerSideProps and withUniformGetStaticProps. Configure your locales in next.config.js:
Personalization and testing#
Client-side personalization (default)#
By default, personalization and A/B testing are evaluated client-side by the UniformContext provider. The context engine runs in the browser, evaluates visitor scores against the personalization manifest, and selects the winning variant. This works with both SSR and SSG.
No additional code is required in your components -- the Personalize and Test system components are handled automatically by UniformComposition.
Server-side personalization#
For SSR pages, you can enable server-side personalization by setting up enableNextSsr in _document.tsx. This runs the context engine on the server during the initial render, so the visitor sees the correct personalized variant without a client-side flicker:
enableNextSsr monkey-patches renderPage to inject the server-side context into the app as serverUniformContext. The _app.tsx then uses this server context (when available) instead of the client-side one.
Edge-side personalization (Vercel)#
For SSG pages deployed on Vercel, you can use edge middleware to evaluate personalization at the edge, achieving flicker-free personalization with static pages:
warning
Edge-side personalization requires the @uniformdev/context-edge-vercel package and SSG mode. Enable outputType="edge" on <UniformContext /> in _app.tsx when using this approach.
Client-side context hooks#
The @uniformdev/context-react package provides hooks for reading and updating visitor data on the client.
useUniformContext#
Access the Uniform Context instance:
useQuirks#
Provides reactive access to the current visitor's quirk values:
useScores#
Provides reactive access to the current visitor's score values:
Context configuration#
The Context constructor accepts several options for fine-tuning personalization behavior:
NextCookieTransitionDataStore options#
| Option | Type | Description |
|---|---|---|
serverContext | NextPageContext | undefined | Pass the Next.js page context for server-side cookie reading |
cookieAttributes | object | Cookie options (expires, path, domain, secure, sameSite) |
cookieName | string | Override the default cookie name (default: ufvd) |
experimental_quirksEnabled | boolean | Enable quirk serialization in cookies |
Manifest management#
warning
The Page Router requires downloading the manifest locally. Unlike the App Router SDK (which fetches the manifest at runtime), the Page Router SDK reads the manifest from a local JSON file. You must download the manifest before building your project.
Add these scripts to your package.json:
The scripts explained:
uniform:manifest-- Downloads the personalization manifest to a local JSON file. Must run beforedevandbuild.uniform:publish-- Required after making changes to signals, enrichments, or quirks. Publishes the personalization manifest so the download command picks up the latest version.uniform:push/uniform:pull-- Syncs component definitions, compositions, and other content between your local project and the Uniform cloud.
note
Use npm-run-all (or run-s / run-p) to chain scripts. Install it with npm install -D npm-run-all.
Rendering strategy comparison#
| Aspect | SSR (getServerSideProps) | SSG + ISR (getStaticProps) |
|---|---|---|
| When content is fetched | Every request | Build time + on-demand revalidation |
| Personalization | Server-side (with enableNextSsr) or client-side | Client-side or edge middleware |
| First visit latency | ~200-500ms (API call on each request) | ~10-50ms (served from CDN) |
| Content freshness | Always fresh | Stale until revalidated |
| Best for | Authenticated pages, real-time data, getting started quickly | Marketing pages, high-traffic public pages |
For a detailed guide on SSG with ISR, see Incremental Static Regeneration (ISR).