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 only boost and hide, dropdown appearance.

Save the content type.

2. Enable Uniform features on the rule content type

  1. Open the Uniform app configuration (Apps > Installed apps > Uniform > Configure).

  2. Enable enrichment taggings and personalization criteria on Personalization Rule, then save.

  3. Back in the content type, rename the generated fields:

    • Enrichment TagsContent Criteria (tags the entry must have).

    • Personalization CriteriaVisitor Criteria (dimensions that must be true of the visitor's session).

  4. Add field Content Criteria Match Type (contentCriteriaMatchType): Short text, accept only all and any, radio appearance. all = every selected tag must be set; any = at least one.

  5. 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:

  1. Add field Personalization Rules (personalizationRules): Reference, many references, required, accept only entry type Personalization Rule, dropdown appearance.

  2. 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

  1. Create a Curated Location List entry.

  2. Create Personalization Rule entries and assign them to the list's Personalization Rules field.

  3. Publish the entries and test (see the demo video above).