React SDK
The Uniform SDK for React provides two complementary packages for building personalized, composable experiences in any React application:
@uniformdev/canvas-react-- Renders Uniform Canvas compositions as React component trees with support for slots, parameters, rich text, and visual editing.@uniformdev/context-react-- Provides React bindings for Uniform Context, enabling personalization, A/B testing, visitor scoring, and behavior tracking.
These packages work with any React setup -- Vite, Remix, Gatsby, a custom SSR server, or any other React-based architecture. For Next.js App Router projects, use the dedicated @uniformdev/next-app-router package instead.
Getting started#
Prerequisites#
- React 18+
- Node.js 18+
- A Uniform project with compositions and component types configured
Starter projects#
Two official starter projects are available on GitHub to help you get up and running quickly:
| Starter | Description | Architecture |
|---|---|---|
| react-starter-cra | Context-only personalization with React Router. No Canvas compositions -- ideal for adding personalization to an existing React SPA. | Create React App, client-side only |
| react-vite-ssr | Full Canvas + Context integration with server-side rendering, including visual editor preview setup. | Vite + Express SSR with hydration |
Both starters include sample content that you can push to your own Uniform project with npm run uniform:push.
Install packages#
| Package | Purpose |
|---|---|
@uniformdev/canvas-react | React components for rendering Canvas compositions |
@uniformdev/context-react | React provider and hooks for personalization and testing |
@uniformdev/canvas | Core Canvas client, types, and utilities |
@uniformdev/context | Core Context engine for scoring and personalization |
Peer dependencies#
Both React packages require react and react-dom as peer dependencies. If you use client-side visibility rules via the useClientConditionsComposition hook, you also need immer >= 10:
note
immer is only required if you use useClientConditionsComposition. Most projects do not need it.
Canvas#
Canvas is Uniform's visual composition system. The @uniformdev/canvas-react package provides React components to render Canvas compositions -- turning the JSON composition tree from the Uniform API into a rendered React component tree.
Core concepts#
A Canvas composition is a tree structure where:
- The root is a composition instance (e.g., a "Page" component)
- Each component can have slots -- named locations where child components are placed
- Each component has parameters -- typed values (text, rich text, images, links, etc.) configured in the Uniform dashboard
- Personalization and A/B test containers are special system components that wrap variants
The React SDK walks this tree and renders each node using a component resolver -- a function you write that maps Uniform component types to your React components.
Fetching compositions#
Before rendering, you need to fetch the composition data from the Uniform API. Use RouteClient from @uniformdev/canvas:
note
You can find your project ID and API key under Settings > API Keys in your Uniform project. See the API keys guide for details.
Rendering a composition#
The UniformComposition component is the entry point for rendering a composition tree. It takes the composition data and a component resolver, and renders the full component tree:
UniformComposition props#
| Prop | Type | Required | Description |
|---|---|---|---|
data | RootComponentInstance | Yes | The composition data from the Uniform API |
resolveRenderer | RenderComponentResolver | Yes | Function that maps component types to React components |
behaviorTracking | 'onLoad' | 'onView' | No | When to track enrichment tags. 'onView' uses IntersectionObserver (default), 'onLoad' tracks immediately on render |
contextualEditingEnhancer | (message) => RootComponentInstance | No | Enhancer for contextual editing updates |
Component resolution#
The component resolver is a function that receives a ComponentInstance and returns the React component to render. This is how you map Uniform component types to your React components:
tip
Always return a fallback component (such as DefaultNotImplementedComponent) for unknown types. This prevents render errors and shows a helpful message in development that includes the missing component type name and a code snippet to help you register it.
Alternative: Component store registration#
Instead of a switch statement, you can use the component store pattern to register components declaratively:
Then use componentStoreResolver as your resolver:
The store supports variant-specific registration:
Resolution priority: exact type + variant match, then type-only match, then the default fallback.
ComponentProps#
Every component rendered by Canvas receives ComponentProps. This type gives you access to the raw component data:
The ComponentProps<T> type extends your custom props with:
| Property | Type | Description |
|---|---|---|
component | ComponentInstance | The raw Canvas component instance with all metadata, slots, and parameters |
Parameter values are automatically extracted from component.parameters[id].value and flattened onto the component props by the SDK's internal convertComponentToProps helper. So if your Canvas component has a parameter named title with value "Hello", your component receives { title: "Hello", component: { ... } }.
UniformSlot#
UniformSlot renders child components placed in a named slot:
UniformSlot props#
| Prop | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The slot name as defined in the Uniform component type |
resolveRenderer | RenderComponentResolver | No | Override the parent's component resolver for this slot's children |
wrapperComponent | React.ComponentType | No | A React component that wraps each item in the slot |
emptyPlaceholder | React.ReactNode | No | Content to render when the slot has no children |
Custom wrapper for slot items#
Use wrapperComponent to wrap each child in the slot with custom markup:
Empty slot placeholder#
Show fallback content when a slot has no children:
UniformText#
UniformText renders text parameters with built-in support for inline editing in the Uniform visual editor:
UniformText props#
| Prop | Type | Required | Description |
|---|---|---|---|
parameterId | string | Yes | The parameter ID as defined in the Uniform component type |
as | React.ElementType | No | HTML element to render (default: 'span') |
placeholder | string | ((parameter) => string) | No | Placeholder text shown in the editor when the value is empty |
isMultiline | boolean | No | Enable line break support (default: false) |
render | (value: string | undefined) => React.ReactNode | No | Custom render function for transforming the value |
Custom rendering#
Use the render prop to transform the parameter value before display:
note
UniformText reads the parameter value from the nearest UniformComponent context. It must be used inside a component rendered by UniformComposition or UniformSlot.
UniformRichText#
UniformRichText renders rich text parameters with full formatting support including headings, lists, links, images, tables, and text formatting:
UniformRichText props#
| Prop | Type | Required | Description |
|---|---|---|---|
parameterId | string | Yes | The parameter ID for the rich text field |
as | React.ElementType | null | No | Wrapper element (default: 'div'). Set to null for no wrapper |
placeholder | string | ((parameter) => string) | No | Placeholder shown in the editor when empty |
resolveRichTextRenderer | RenderRichTextComponentResolver | No | Custom renderer for rich text nodes |
Custom rich text node rendering#
Override how specific rich text nodes are rendered:
The SDK includes built-in renderers for all standard rich text node types: paragraphs, headings (h1-h6), lists, list items, links, images/videos/audio assets, tables, line breaks, tabs, and formatted text (bold, italic, underline, strikethrough, code, subscript, superscript).
Accessing raw parameter values#
When you need parameter values directly (for non-visual use like URLs, booleans, or conditional logic), access them from the component prop:
getParameterAttributes helper#
For advanced inline editing scenarios, getParameterAttributes returns the data attributes needed to mark an element as editable by the Uniform visual editor:
This is useful when you cannot use UniformText but still want inline editing support.
Hooks#
useUniformCurrentComposition#
Returns the data and configuration from the nearest UniformComposition ancestor:
Return value:
| Property | Type | Description |
|---|---|---|
data | RootComponentInstance | undefined | The full composition data |
isContextualEditing | boolean | Whether the composition is being edited in the Uniform visual editor |
matchedRoute | string | undefined | The matched route path |
dynamicInputs | Record<string, any> | undefined | Dynamic input values from route parameters |
resolveRenderer | RenderComponentResolver | undefined | The active component resolver |
behaviorTracking | 'onLoad' | 'onView' | undefined | The behavior tracking mode |
useUniformCurrentComponent#
Returns the data from the nearest UniformComponent ancestor (the currently rendering component):
useUniformContextualEditing#
Enables contextual editing -- the two-way communication between your React app and the Uniform visual editor. When a content author edits a composition in the Uniform dashboard, this hook receives live updates and re-renders the composition in real time:
| Property | Type | Description |
|---|---|---|
composition | RootComponentInstance | The current composition data (updates live during editing) |
isContextualEditing | boolean | true when the Uniform editor is active |
note
Contextual editing is automatically set up when you use UniformComposition with the contextualEditingEnhancer prop. Use useUniformContextualEditing directly only when you need full control over the editing lifecycle.
useUniformContextualEditingState#
Provides information about the current contextual editing state, such as which component is selected in the editor:
| Property | Type | Description |
|---|---|---|
isContextualEditing | boolean | Whether contextual editing is active |
selectedComponentReference | object | undefined | Reference to the currently selected component in the editor |
previewMode | 'editor' | 'preview' | undefined | The current preview mode |
useClientConditionsComposition#
Evaluates client-side visibility rules on a composition and returns a filtered composition with invisible components removed:
This hook uses immer internally for immutable updates and reads the current visitor's quirks from the Uniform Context to evaluate visibility conditions.
Visual editing#
The Uniform visual editor allows content authors to edit compositions directly in the browser. The React SDK supports this with:
- Contextual editing -- Live preview updates when editing in the Uniform dashboard
- Inline editing -- Direct text editing within the rendered page (via
UniformTextandUniformRichText) - Component selection -- Click-to-select components in the visual editor
To enable contextual editing, pass the contextualEditingEnhancer prop to UniformComposition:
The SDK automatically:
- Detects when the page is loaded inside the Uniform visual editor
- Sets up a message channel for live composition updates
- Adds data attributes to components and parameters for editor interaction
- Provides the
isContextualEditingflag viauseUniformCurrentComposition
Setting up the visual editor preview#
To enable the Uniform visual editor (contextual editing) with a React app, your server must handle two special requests from the Uniform dashboard:
- Config check -- The editor pings your preview URL to check capabilities (e.g., whether a playground route exists).
- Preview route -- When a content author opens the visual editor, the dashboard loads your app inside an iframe. On preview requests, your server should skip fetching composition data and instead return a page with an empty composition stub -- the editor sends the actual composition data over a
postMessagechannel.
Here is a complete Express server setup demonstrating both (from the react-vite-ssr starter):
note
isAllowedReferrer checks that the request originates from the Uniform dashboard domain. EMPTY_COMPOSITION is a minimal valid composition stub that lets the page render before the editor injects the real data. Both are exported from @uniformdev/canvas.
Configuring the preview URL in Uniform#
After setting up your server, configure the preview URL in your Uniform project so the visual editor knows where to load your app:
- Go to Settings > Canvas Settings > Preview in your Uniform project.
- Add a preview URL pointing to your preview route, for example:
http://localhost:5173/api/preview. - Optionally configure preview viewports (Desktop, Tablet, Mobile) for responsive previewing.
The react-vite-ssr starter includes a pre-configured preview URL and viewport definitions in the uniform-data/ folder that are pushed to your project with npm run uniform:push.
UniformPlayground#
UniformPlayground is a special component for previewing individual components and patterns in the Uniform dashboard. It renders with an empty composition and supports contextual editing:
Context#
Context is Uniform's personalization and optimization engine. The @uniformdev/context-react package provides React components and hooks that integrate the Context engine into your React application, enabling personalization, A/B testing, visitor scoring, and behavior tracking.
Using Context without Canvas#
You don't need Canvas compositions to use Uniform personalization. The @uniformdev/context-react package works standalone in any React app -- you can add personalization, A/B testing, and visitor scoring to an existing React application without adopting Canvas or changing your rendering architecture.
The react-starter-cra starter demonstrates this approach with a client-side React app that uses React Router for page navigation and Uniform Context for personalization. No Canvas compositions, no @uniformdev/canvas packages -- just Context.
In this pattern, you track enrichments programmatically inside your page components:
This approach is ideal when you want to:
- Add personalization to an existing React SPA without a CMS
- Use Uniform signals and scoring with your own component architecture
- Implement programmatic enrichment tracking based on user navigation patterns
Setting up UniformContext#
The UniformContext provider wraps your application and provides the Context instance to all child components:
Persisting visitor data with CookieTransitionDataStore#
By default, visitor scores and quirks are stored in memory and lost on page reload. Use CookieTransitionDataStore to persist visitor data across sessions via cookies:
tip
For SSR apps, read the ufvd cookie from the incoming request headers and pass it as serverCookieValue. This ensures the server-rendered HTML reflects the visitor's existing scores and quirks.
Enabling debug logging#
Use enableDebugConsoleLogDrain during development to log all Context activity (signal evaluations, score changes, enrichment tracking) to the browser console:
UniformContext props#
| Prop | Type | Required | Description |
|---|---|---|---|
context | Context | Yes | A configured Uniform Context instance |
outputType | 'standard' | 'edge' | No | Rendering mode (default: 'standard'). Use 'edge' for edge-side personalization |
trackRouteOnRender | boolean | No | Whether to track route changes on render (default: true) |
includeTransferState | 'always' | 'never' | 'server-only' | No | When to include server-to-client state transfer for SSR hydration (default: 'server-only') |
Transfer state for SSR#
When server-rendering, the UniformContext automatically injects a <script> tag containing the serialized context state. The client picks this up during hydration so that the visitor's scores and quirks are preserved without a flash of unpersonalized content:
Creating the manifest#
The personalization manifest defines your project's signals, enrichments, quirks, and A/B tests. There are several ways to create it:
Option 1: Hardcode for development
Option 2: Download via CLI (recommended for production)
Then import it:
Option 3: Fetch at runtime
warning
After modifying signals, enrichments, or quirks in the Uniform dashboard, you must publish the manifest for changes to take effect:
Personalization#
The Personalize component selects and renders content variants based on the visitor's scores, quirks, and other classification data.
How it works#
- You define multiple variations of a content component, each tagged with personalization criteria (enrichment tags/scores).
- The
Personalizecomponent callscontext.personalize()to evaluate the visitor's current scores against each variation's criteria. - The winning variation(s) are rendered. If no variation matches, the default (first) variation is shown.
Personalize props#
| Prop | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique name for this placement (used in analytics) |
variations | PersonalizedVariant[] | Yes | Array of possible variations to select from |
component | React.ComponentType | Yes | Component to render for the selected variation |
count | number | No | Number of variations to select (default: 1) |
algorithm | string | No | Personalization algorithm to use |
wrapperComponent | React.ComponentType | No | Wrapper component that receives personalizationOccurred flag |
compositionMetadata | CompositionMetadata | No | Metadata for analytics tracking |
The rendered component receives all variation properties plus a personalizationResult object:
Wrapper component#
Use wrapperComponent to add conditional styling or tracking based on whether personalization occurred:
A/B testing#
The Test component implements A/B testing by selecting a single variant based on test distribution percentages:
Test props#
| Prop | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique test name |
variations | TestVariant[] | Yes | Array of test variations, each with a testDistribution percentage |
component | React.ComponentType | Yes | Component to render for the selected variation |
loadingMode | 'default' | 'none' | React.ComponentType | No | How to handle loading state |
compositionMetadata | CompositionMetadata | No | Metadata for analytics tracking |
note
Key difference from personalization: A/B tests use deterministic distribution percentages (testDistribution) to split traffic. Personalization uses visitor scores and quirks to algorithmically select the best variant.
Behavior tracking#
Track visitor interactions to build up enrichment scores that drive personalization. The SDK provides two tracking components:
Track (viewport-based)#
Track tracks visitor behavior when content enters the viewport using IntersectionObserver:
Track props#
| Prop | Type | Required | Description |
|---|---|---|---|
behavior | EnrichmentData | EnrichmentData[] | Yes | Enrichment data to track |
children | React.ReactNode | Yes | Content to track visibility of |
tagName | keyof JSX.IntrinsicElements | No | Wrapper element (default: 'div') |
threshold | number | number[] | No | Visibility threshold (default: 0.5 = 50% visible) |
disableVisibilityTrigger | boolean | No | Disable viewport tracking (falls back to immediate) |
TrackFragment (page load-based)#
TrackFragment tracks immediately on page load / route change without adding a wrapper element:
Both tracking components:
- Track once per URL change per component instance (no duplicate tracking)
- Do not track when inside a
Personalizecomponent (prevents double-counting) - Call
context.update({ enrichments })to update the visitor's scores
Hooks#
useUniformContext#
Accesses the Uniform Context instance from the nearest UniformContext provider:
By default, this hook throws an error if no UniformContext provider is found. To make it optional:
warning
The context.scores and context.quirks properties accessed directly from the Context instance are not reactive -- they don't trigger re-renders when they change. Use useScores() and useQuirks() for reactive access.
useScores#
Provides reactive access to the visitor's current scores. The component re-renders automatically when scores change:
Returns a ScoreVector -- an object mapping signal names to numeric score values.
useQuirks#
Provides reactive access to the visitor's current quirks. Re-renders when quirks change:
Returns a Quirks object -- key-value pairs representing visitor attributes.
Updating quirks programmatically#
Use the Context instance to set quirks from user interactions:
useIsPersonalized#
Determines if the current component is inside a Personalize component:
Edge-side rendering#
The Context React SDK supports edge-side personalization and testing. When outputType is set to 'edge', the Personalize and Test components emit all variations wrapped in special HTML tags. An edge worker (Cloudflare, Vercel, Netlify, Akamai) then selects the winning variant before delivering the page to the visitor:
In edge mode:
Personalizerenders all variations with metadata tags for edge selectionTestrenders all variations with test distribution metadata- The edge worker evaluates scores/quirks and strips non-winning variants from the HTML
See the edge-side personalization guide for detailed setup instructions.
Full project example#
Here is a complete example showing how to wire up both Canvas and Context in a React + Vite SSR project.
Project structure#
App component#
Component resolver#
Page component#
Hero component#
API client#
Server entry#
Client entry#
Import reference#
@uniformdev/canvas-react#
| Export | Description |
|---|---|
UniformComposition | Renders a full Canvas composition tree |
UniformSlot | Renders child components from a named slot |
UniformText | Renders text parameters with inline editing support |
UniformRichText | Renders rich text parameters |
UniformPlayground | Playground for visual editing preview |
DefaultNotImplementedComponent | Fallback for unmapped component types |
registerUniformComponent | Registers a component in the component store |
componentStoreResolver | Resolver function that uses the component store |
useUniformCurrentComposition | Hook: access composition context |
useUniformCurrentComponent | Hook: access current component context |
useUniformContextualEditing | Hook: enable contextual editing |
useUniformContextualEditingState | Hook: read contextual editing state |
convertComponentToProps | Helper: convert component instance to flat props |
getParameterAttributes | Helper: get inline editing data attributes |
@uniformdev/context-react#
| Export | Description |
|---|---|
UniformContext | Provider component for the Uniform Context engine |
Personalize | Renders personalized content based on visitor scores |
Test | Renders A/B test variations based on distribution |
Track | Tracks behavior when content enters the viewport |
TrackFragment | Tracks behavior on page load (no wrapper element) |
useUniformContext | Hook: access the Context instance |
useScores | Hook: reactive visitor scores |
useQuirks | Hook: reactive visitor quirks |
useIsPersonalized | Hook: check if inside a Personalize component |
@uniformdev/canvas#
| Export | Description |
|---|---|
RouteClient | Client for resolving routes and fetching compositions |
CanvasClient | Client for direct composition API access |
RootComponentInstance | Type for composition root data |
ComponentInstance | Type for individual component data |
EMPTY_COMPOSITION | Minimal composition stub for visual editor preview routes |
isAllowedReferrer | Checks if a request originates from the Uniform dashboard |
IN_CONTEXT_EDITOR_CONFIG_CHECK_QUERY_STRING_PARAM | Query string parameter name used by the editor config check |
@uniformdev/context#
| Export | Description |
|---|---|
Context | The core personalization and scoring engine |
ManifestV2 | Type for the personalization manifest |
enableContextDevTools | Plugin for enabling the Uniform DevTools browser extension |
enableDebugConsoleLogDrain | Plugin for logging all Context activity to the browser console |
CookieTransitionDataStore | Persists visitor scores and quirks across sessions via cookies |
Recipes#
Recipe: Adding Uniform to an existing React + Vite project#
- Install packages:
- Create environment variables (
.env):
- Create the manifest file. Download it via CLI:
- Create the API client (
src/uniform/api.ts):
- Create the component resolver (
src/resolveRenderer.ts):
- Wrap your app with
UniformContextand render compositions withUniformComposition:
- Add CLI scripts to
package.json:
Recipe: Component with personalized slot content#
Recipe: Tracking user behavior for personalization#
Recipe: Updating Context on route changes (React Router)#
In a client-side SPA with React Router, Uniform Context needs to be told about route changes so signals based on URL patterns can evaluate correctly. Update Context in a useEffect that depends on the current location:
Place this component just below UniformContext in your tree so all child routes benefit from the updated Context state.
Recipe: Displaying visitor scores and quirks for debugging#
Build a debug panel that shows the current visitor's scores and quirks during development. This is useful for verifying that signals fire correctly and enrichments accumulate as expected:
tip
context.forget(true) clears all stored visitor data including scores, quirks, and consent state. The true argument also clears the cookie-based transition store if you are using CookieTransitionDataStore.
Recipe: DevTools integration#
Enable the Uniform Context DevTools browser extension for debugging:
The DevTools extension lets you inspect and modify visitor scores and quirks in real time during development.
Best practices#
Always wrap with
UniformContext-- Place theUniformContextprovider aboveUniformCompositionin the component tree. Canvas components need Context for personalization, A/B testing, and behavior tracking.Create the
Contextinstance once -- Instantiate theContextobject at the module level or in a top-level component, not inside render functions. Creating it in a render function resets visitor state on every render.Use
DefaultNotImplementedComponentas fallback -- Always return a fallback from your component resolver. It shows the component type name and a helpful code snippet during development.Mark parameter types as optional -- Even required parameters can be
undefinedat runtime (e.g., when first adding a component to a composition). Always handleundefinedvalues.Use
UniformTextandUniformRichTextfor visible text -- These components provide inline editing support in the Uniform visual editor. Use raw parameter access only for non-visible values like URLs, booleans, or conditional logic.Hydrate composition data for SSR -- When server-rendering, serialize the composition to a
<script>tag and read it on the client to avoid re-fetching during hydration.Keep the manifest up to date -- Run
npx uniform context manifest publishafter modifying signals, enrichments, or quirks. Then re-download or re-fetch the manifest.Use
useScoresanduseQuirksfor reactive data -- Accessingcontext.scoresdirectly does not trigger re-renders. Always use the hooks for values that affect the UI.Avoid tracking inside Personalize -- The
TrackandTrackFragmentcomponents automatically skip tracking when inside aPersonalizecomponent to prevent double-counting.Leverage
immerfor visibility rules -- TheuseClientConditionsCompositionhook requiresimmer >= 10as a peer dependency. Install it if you use client-side visibility rules.