Scalable List-Based Personalization in Contentful with Uniform Context
Last updated: June 22, 2026
Problem Statement
Content authors curate lists of entries (e.g. locations) in Contentful and want to personalize which entries are shown and in what order. The same entry can appear in many lists, and the rules depend on the list, not the entry — so personalization rules must be applied to the list, not to the entries themselves.
Demo of this recipe in action. (watch on Vimeo)
A rule combines criteria (what must be true) with an action (what happens then). Instructions assume a Next.js app (page router, TypeScript) using client-side personalization, with the Uniform app installed in Contentful.
Solution
Walk-through the of the steps in this section. (watch on Vimeo)
1. Create content type: Personalization Rule
In Contentful, create a content type Personalization Rule (API ID personalizationRule) with:
Name (
name): Short text, entry title, required.Action (
action): Short text, required, accept onlyboostandhide, dropdown appearance.
Save the content type.
2. Enable Uniform features on the rule content type
Open the Uniform app configuration (
Apps>Installed apps>Uniform>Configure).Enable enrichment taggings and personalization criteria on
Personalization Rule, then save.Back in the content type, rename the generated fields:
Enrichment Tags→Content Criteria(tags the entry must have).Personalization Criteria→Visitor Criteria(dimensions that must be true of the visitor's session).
Add field Content Criteria Match Type (
contentCriteriaMatchType): Short text, accept onlyallandany, radio appearance.all= every selected tag must be set;any= at least one.Order the fields: Name, Action, Visitor Criteria, Content Criteria, Content Criteria Match Type. Save.
3. Update the list content type
The Location content type already has enrichment tagging enabled — no changes needed. On Curated Location List:
Add field Personalization Rules (
personalizationRules): Reference, many references, required, accept only entry typePersonalization Rule, dropdown appearance.Order the fields: Title, Personalization Rules, Locations. Save.
4. Install npm packages
npm i @uniformdev-collab/rule-based-personalization \
@uniformdev-collab/rule-based-personalization-contentful \
@uniformdev-collab/rule-based-personalization-react \
@uniformdev-collab/rule-based-personalization-react-contentful
5. Configure personalization in _app.tsx
Define lookup configs before the default component:
// ./src/pages/_app.tsx
import {
ContentfulPzConfigLookupConfig,
ContentfulPzRuleLookupConfig,
} from "@uniformdev-collab/rule-based-personalization-contentful";
// How Uniform handles entries that have personalized lists on them.
const contentfulPzConfigs: ContentfulPzConfigLookupConfig = {
// No default: a content type must be listed in 'elements'
// for Uniform to personalize a list on its entries.
defaultElement: undefined,
elements: {
curatedLocationList: {
contentEntriesFieldId: "locations",
pzRulesFieldId: "personalizationRules",
},
},
};
// How Uniform handles entries that represent personalization rules.
const contentfulPzRuleConfigs: ContentfulPzRuleLookupConfig = {
defaultElement: {
nameFieldId: "name",
actionFieldId: "action",
contentCriteriaMatchTypeFieldId: "contentCriteriaMatchType",
},
elements: {},
};
Wrap your app with the provider:
// ./src/pages/_app.tsx
import {
RuleBasedPersonalization,
} from "@uniformdev-collab/rule-based-personalization-react";
<UniformContext context={...}>
<RuleBasedPersonalization
context={clientContext}
pzConfigs={contentfulPzConfigs}
pzRuleConfigs={contentfulPzRuleConfigs}
>
...
</RuleBasedPersonalization>
</UniformContext>
6. Personalize the list where it renders
In the page that displays the location list (the curated list entry is provided in props):
import { Entry } from "contentful";
import { useEffect, useState } from "react";
import { useUniformContext } from "@uniformdev/context-react";
import {
useContentfulRuleBasedPz,
} from "@uniformdev-collab/rule-based-personalization-react-contentful";
export default function HomePage(props: HomePageProps) {
const { context } = useUniformContext();
const { doPersonalize } = useContentfulRuleBasedPz();
const [locations, setLocations] = useState<Entry>();
const { entry } = props;
// Run personalization on mount and whenever visitor dimensions change.
useEffect(() => {
personalize();
context.events.on("scoresUpdated", personalize);
return () => {
context.events.off("scoresUpdated", personalize);
};
}, []);
function personalize() {
if (entry) {
const { personalized } = doPersonalize(entry);
setLocations(personalized);
}
}
...
7. Author the content
Create a Curated Location List entry.
Create Personalization Rule entries and assign them to the list's Personalization Rules field.
Publish the entries and test (see the demo video above).