UniformConf with Next.js
This tutorial guides you through adding personalization to the UniformConf site built using Next.js. By the end of this tutorial you will have a personalized Next.js application with content and personalization configured by content authors in Contentful using the Uniform app from the Contentful marketplace:
- Content authors can assign enrichments to entries to identify the visitor's interests.
- Content authors can define the conditions that must be met in order for each hero component to be displayed on the home page.
- When the visitor's interests are identified, the home page displays personalized content.
Prerequisites
Before you get started, be sure you have the following:
- Administrator access to Contentful - to import content and add the Uniform app to your Contentful space.
- Administrator access to Uniform - to configure the personalization settings that are available in Contentful. If you do not already have a Uniform account, you can get a free one at https://uniform.app.
- Node.js (version 14 or greater) - installed on your machine to work with the Next.js app.
- Uniform Context browser extension - to facilitate development and testing personalization on the Next.js app. You can get the extension here. This is not required to use Uniform Context, but it makes development and testing much easier.
Create Uniform project
In Uniform, a project is the basic unit of organization. A project is where you will define the criteria you want to use to configure personalization.
Log into Uniform.
On the Projects page, click (+) to create a new project.
Enter the following for the project name:
UniformConf for Contentful
Scroll down and click Empty project.
Click Continue.
Your project is created and you are taken to the project home page.
Configure Contentful
Load sample content into Contentful
In Contentful, create a content management token.
About this stepThis token is needed to load data into your Contentful space. Note this value because you will need to add it to your
.env
file later.In Contentful, create a content delivery access token.
About this stepThis access token is needed to read content from your Contentful space. Note this value because you will need to add it to your
.env
file later.Clone the following repository:
https://github.com/uniformdev/uniform-docs-examples
Check out the following branch:
apps/contentful/nextjs/no-uniform
Add the following file:
.envNEXT_PUBLIC_CONTENTFUL_SPACE_ID=<YOUR CF SPACE ID>
NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN=<YOUR CF CONTENT DELIVERY ACCESS TOKEN>
CONTENTFUL_MANAGEMENT_TOKEN=<YOUR CF MANAGEMENT TOKEN>Open a terminal in the root of the repository.
Enter the following commands:
npm install
npm run cf:importIn Contentful, confirm you have a entries in the content model, content, and media sections.
About this stepYou might notice there are two hero content types: Enriched Hero and Personalized Hero. There is nothing special about these content types - yet. You will assign enrichment tags to the former and personalization criteria to the latter.
Install Marketplace app
You must install and configure the Uniform app from the Contentful Marketplace to your space.
Install the Marketplace app.
infoBe sure to note the Uniform API key you create. You will need this value in a later step.
Configure space
Now you need to add classification and personalization settings to Contentful. This involves the following:
- Add enrichment tagging to the content type Enriched Hero.
- Assign enrichment tags to Enriched Hero content entries.
- Add personalization criteria to the content type Personalized Hero.
- Assign personalization criteria to Personalized Hero content entries.
- Create a content type to define a list of Personalized Hero entries.
- Configure a content entry for the list of Personalized Hero entries for the home page.
If you prefer to get Contentful configured quickly and import the finished configuration into your Contentful space, you can skip to the finished configuration section.
Configure Enriched Hero entries
Uniform personalization depends on the Uniform tracker. This is a client-side component that captures visitor activity. When the visitor views a page with an enriched hero, you want the Uniform tracker to keep track of the persona associated with the hero. This enables you to understand the personas the visitor's activity matches.
In Contentful, navigate to Apps > Manage apps.
Open the Uniform app.
In the ENRICHMENT TAGGING section, from the dropdown select Enriched Hero.
About this stepThis adds a new field to the content type Enriched Hero.
Click Save.
Open the content type Enriched Hero. Notice the field Enrichment Tags.
Open the entry Marketer Content.
Scroll down to the field Enrichment Tags.
Click on the word here to open a new tab with the Uniform Enrichments page.
Click (+) to create a new enrichment.
Enter the following values:
- Name:
Persona
- Public ID:
1
- Name:
Click Save.
Click Add value.
Enter the following values:
- Name:
Marketer
- Public ID:
1
- Name:
Click Save.
In Contentful, refresh the entry page.
Click on the word here.
Click Select > Enrichment: Marketer.
Click Add.
Click Publish.
In Uniform, add another value to the enrichment Persona:
- Name:
Developer
- Public ID:
2
- Name:
Click Publish.
About this stepWhen settings are configured under personalization, they are not available to be used in an application until they are published.
In Contentful, set the value on the field Enrichment Tags on the following Enriched Hero entries:
Title Enrichment Tags Developer Content Enrichment: Developer Publish the entries.
Configure Personalized Hero entries
Personalized Hero entries provide the personalized content that will be displayed on the home page. The specific entry that is used when a visitor views the home page depends on the visitor's activity.
In Uniform, navigate to Personalization > Signals.
Click the red (+) button.
Enter the following values:
- Name:
Registered for Event
- Public ID:
registeredForEvent
- Name:
Click Cookie.
Enter the following values:
- Cookie Name:
unfrmconf_registered
- Comparison: equals
- Match:
true
- Cookie Name:
Click Save and close.
In Uniform, add another signal using the following settings:
- Name:
UniformConf Campaign
- Public ID:
uniformconfCampaign
- Criteria: Query String
- Query String Name:
utm_campaign
- Comparison: equals
- Match:
unfrmconf
- Name:
Click Publish.
About this stepWhen settings are configured under personalization, they are not available to be used in an application until they are published.
In Contentful, navigate to Apps > Manage apps.
Open the Uniform app.
In the PERSONALIZATION CRITERIA section, from the dropdown select Personalized Hero.
About this stepThis adds a new field to the content type Personalized Hero.
Click Save.
Open the content type Personalized Hero. Notice the field Personalization Criteria.
Open the entry Marketering Hero.
Scroll down to the field Personalization Criteria.
Click Add Criteria.
Click Select > Enrichment: Marketer.
About this stepBy default, the criteria is set to match when the enrichment score is greater than 0. The enrichment score is set to 50 when a page with the Enriched Hero on it is viewed. This means the criteria will match if the page is viewed at least one time.
Click Publish.
Edit the following Personalized Hero entries:
Title Personalization criteria Call for papers open now! Signal: UniformConf Campaign Can't wait to see you soon at UniformConf! Signal: Registered for Event Developer Hero Enrichment: Developer Publish the entries.
Configure list of Personalized Heros
You want to select the Personalized Hero entries that can appear on the home page. This is done by creating a new entry that you can use to select and order the heros.
In Contentful, navigate to Apps > Manage apps.
Open the Uniform app.
In the PERSONALIZATION LISTS section, click + Create New.
Enter the following value and click Confirm:
Personalized Hero List
Click Save.
Navigate to Content model. Notice a new content type Personalized Hero List.
Add a new entry using the content type Personalized Hero List.
For the field List Name, enter the following:
Home Page Heros
Click Link existing entries.
Filter the search by the content type Personalized Hero and select the following entries:
- Call for papers open now!
- Can't wait to see you soon at UniformConf!
- Developer Hero
- Marketer Hero
- Welcome to UniformConf
Click Insert 5 entries.
Drag the entries into the following order:
- Can't wait to see you soon at UniformConf!
- Call for papers open now!
- Developer Hero
- Marketer Hero
- Welcome to UniformConf
About this stepThe order of the entries in the list is significant because the personalization criteria is evaluated in order. The first match is the entry that is displayed. This creates a priority for the personalization instructions. This is important because it is possible a visitor matches multiple criteria. It also affects the way you should go about testing that personalization works.
Click Publish.
Finished configuration
This section guides you through the process of importing the finished configuration into your Contentful space.
If you followed the steps above, you can skip this section.
In Uniform, add the following permissions to your Uniform API key:
Uniform Context > Enrichments > Create
Uniform Context > Enrichments > Update
Uniform Context > Signals > Create
Uniform Context > Signals > UpdatecautionYou should always assign the minimal permissions required to meet your requirements. Since this you are going to load configuration from a code repository into Uniform, you need permissions to write to Uniform.
For more details on permissions, see the administrator guide.
Open a terminal in the root of the repository.
Enter the following command:
npm run uniform:push
About this stepThis imports the finished versions of the enrichments and signals.
In Uniform, navigate to Personalization > Enrichments.
About this stepYou will see the Persona enrichment with two values.
Navigate to Signals.
About this stepYou will see two signals.
In the terminal, enter the following command:
npm run cf:import-finished
About this stepThis imports the finished versions of the content types, content entries, and media.
In Contentful, open the content type Enriched Hero.
For the field Enrichment Tags, click Settings.
Click Appearance.
Click the Uniform logo.
Click Confirm.
Click Save to save the changes to the content type.
Repeat these steps for the following content types:
Content type Field Personalized Hero Personalization Criteria Personalized Hero List List Items
Update Next.js app
Uniform is designed to require minimal Uniform-specific product knowledge so developers can incorporate it into their apps as easily as possible. This section guides you through the process of adding personalization to the Next.js app.
If you learn better by studying a fully implemented, working example, see the finished code section.
Add Uniform environment variables
Add the following values to your .env
file. You collected
these values when you created the Uniform API key:
Uniform value | Environment variable |
---|---|
API Key |
|
Project ID |
|
These environment variables are needed in order for the npm build script to communicate with Uniform.
Download manifest
The Next.js application needs to have access to the personalization settings from Uniform. Uniform can generate a manifest that contains this information so you can use the settings in your application.
In most cases, when using Uniform in an application that uses static site generation, you should generate the manifest before building the application. This ensures the latest manifest is available to the application.
Open a terminal in the root of the repository.
Enter the following command:
npm install -D @uniformdev/cli
npm install @uniformdev/contextAbout this stepThis adds a reference to the package with the Uniform CLI, which is a tool that enables you to interact with Uniform from a command-line interface. It also adds a reference to the package that includes a Uniform CLI extension for interacting with Uniform Context from a CLI.
Add the following to
package.json
:{
"name": "contentful-uniformconf-nextjs",
"description": "UniformConf for Contentful",
"version": "0.1.0",
"private": true,
"scripts": {
"cf:export": "node ./content/cf/export.cjs",
"cf:import": "node ./content/cf/import.cjs",
"start": "next start",
"download:manifest": "uniform context manifest download --output ./contextManifest.json",
"dev": "run-s download:manifest dev:next",
"dev:next": "next dev",
"build": "run-s download:manifest build:next",
"build:next": "next build",
"export": "next export",
"ci:build": "next build && next export",
"lint": "next lint"
},
"dependencies": {
...About this stepThis adds a new script that downloads the manifest from Uniform, saves it to a file named
contextManifest.json
, and incorporates it into the scripts that start and build the application.Add the following to your
.gitignore
file:# uniform
contextManifest.jsonAbout this stepThis ensures the manifest file does not get added to your source code repository.
Add Context to app
Adding Uniform Context to your application involves two primary tasks. One is making the manifest available to the application. This is required because it establishes the parameters for tracking and personalization. Two is creating a "context" object that is maintains state for Uniform Context within the app.
Open a terminal in the root of the repository.
Enter the following command:
npm install @uniformdev/context-react cookie
About this stepThis adds a reference to the package that enables you to add Uniform Context into applications build using React.
Open the file
./pages/_app.jsx
in your text editor.Add the following code:
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import "../styles/style.css";
import {
Context,
} from "@uniformdev/context";
const context = new Context({
});
...Add the following code:
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import "../styles/style.css";
import {
Context,
} from "@uniformdev/context";
import manifest from "../contextManifest.json";
const context = new Context({
manifest,
});
function MyApp({
Component,
pageProps,
}) {
return (
<>
<Navbar />
<Component {...pageProps} />
<Footer />
<>
);
}
export default MyApp;Add the following code:
...
const context = new Context({
defaultConsent: true,
manifest,
});
...Add the following code:
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import "../styles/style.css";
import {
Context,
} from "@uniformdev/context";
import { UniformContext } from "@uniformdev/context-react";
import manifest from "../contextManifest.json";
const context = new Context({
defaultConsent: true,
manifest,
});
function MyApp({
Component,
pageProps,
}) {
return (
<UniformContext context={context}>
<Navbar />
<Component {...pageProps} />
<Footer />
</UniformContext>
);
}
export default MyApp;Add the following code:
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import "../styles/style.css";
import {
Context,
enableContextDevTools,
} from "@uniformdev/context";
import { UniformContext } from "@uniformdev/context-react";
import manifest from "../contextManifest.json";
const context = new Context({
defaultConsent: true,
manifest,
plugins: [
enableContextDevTools(),
],
});
function MyApp({
Component,
pageProps,
}) {
return (
<UniformContext context={context}>
<Navbar />
<Component {...pageProps} />
<Footer />
</UniformContext>
);
}
export default MyApp;About this stepThis enables the Uniform browser extension. Production sites usually do not have this enabled, but it can be very helpful during development and testing.
Start app
In the terminal, enter the following command:
npm run dev
Open a browser window to
http://localhost:3000
to view the UniformConf application.If you have the Chrome extension installed, you should see that the Uniform logo is now in full color (previously it was black and white). This means that Uniform Context was detected on the site.
Activate tracking
In Contentful you assigned enrichment tags to entries based on the Enriched Hero content type. The Uniform tracker is able to use these enrichment tags to track the visitor. The tracker can do this automatically, as long as the enrichment tags are included in the props for the page.
Open the file
./lib/cf/fetch.js
in your text editor.Add the following code:
function getFields(entry) {
const { fields } = entry;
if (fields?.image) {
fields.image = fields.image.fields.file.url;
}
if (!fields.id && entry.sys?.id) {
fields.id = entry.sys?.id;
}
if (fields.unfrmOptEnrichmentTag) {
fields.enrichments = fields.unfrmOptEnrichmentTag;
delete fields.unfrmOptEnrichmentTag;
}
return fields;
}Open the file
./components/Hero.jsx
in your text editor.Add the following code:
export const Hero = ({
title,
description,
buttonText,
image,
buttonLinkSlug,
enrichments,
}) => {Add the following code:
import Link from "next/link";
import Splitter from "./Splitter";
import { Track } from "@uniformdev/context-react";
export const Hero = ({
title,
description,
buttonText,
image,
buttonLinkSlug,
enrichments,
}) => {
return (
<Track behavior={enrichments}>
<div className="pt-24">
...
</div>
<Splitter />
</Track>
);
};Open the file
./components/Navbar.jsx
in your text editor.Add the following code:
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useScrollPosition } from "@n8tb1t/use-scroll-position";
import NavMenu from "./NavMenu";
import Logo from "./Logo";
import { useUniformContext } from "@uniformdev/context-react";
...
const Nav = () => {
const [submenuVisible, setSubmenuVisible] = useState(false);
const [isScrolled, setScrolled] = useState(false);
const { context } = useUniformContext();
const router = useRouter();
...Add the following code:
...
<ActionLink
isScrolled={isScrolled}
onClick={async () => {
setSubmenuVisible(false);
await context.forget(true);
document.cookie =
"unfrmconf_registered=; Path=/; samesite=lax; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
}}
label="Forget me"
icon={<LockIcon />}
/>
...In the terminal, enter the following command:
npm install cookie
Open the file
./components/RegistrationForm.jsx
in your text editor.Add the following code:
import { useUniformContext } from '@uniformdev/context-react';
import React from 'react';
import { useState } from 'react';
import Splitter from './Splitter';
export const RegisterForm = (fields) => {
const [registered, setRegistered] = useState(
typeof document !== 'undefined' ? !!document.cookie.match(/unfrmconf_registered/) : false
);
const { context } = useUniformContext();
const onRegister = () => {
document.cookie = 'unfrmconf_registered=true; path=/; samesite=lax';
setRegistered(true);
};
...Add the following code:
import { useUniformContext } from '@uniformdev/context-react';
import { parse } from 'cookie';
import React from 'react';
import { useState } from 'react';
import Splitter from './Splitter';
export const RegisterForm = (fields) => {
const [registered, setRegistered] = useState(
typeof document !== 'undefined' ? !!document.cookie.match(/unfrmconf_registered/) : false
);
const { context } = useUniformContext();
const onRegister = () => {
document.cookie = 'unfrmconf_registered=true; path=/; samesite=lax';
context.update({
cookies: parse(document.cookie)
});
setRegistered(true);
};
...Open a browser window to
http://localhost:3000/developers
to view a page with an enrichment tag assigned.Open the browser extension and navigate to Dimensions.
About this stepThis shows that the tracker collected data, but it's not very intuitive. It says that the dimension "1_2" has a score of "50". The "1_2" is a combination of the public IDs for the enrichment Persona and the enrichment value Developer.
In the extension, click Settings.
Click Manage Connection to Uniform.
Enter the Quick Connect Code you used to configure the app in Contentful.
About this stepThese settings give the browser extension the ability to read metadata directly from Uniform. This makes it possible to translate "1_2" into something meaningful.
Click Save.
In the extension, click Dimensions. Now you will see more meaningful descriptions of what the tracker has collected.
In your browser, navigate to
http://localhost:3000/marketers
.Open the browser extension. Now you will see that you have scores for 2 enrichments.
About this stepYou can see that tracking is working. The next step is to personalize the visitor's experience based on this information that has been tracked.
Activate personalization
In Contentful you configured a content entry that represents a personalized list of hero entries for the home page. Uniform provides a React component that can execute the personalization settings configured in the list. This section describes how to add that component to the front-end application.
Open the file
./lib/cf/fetch.js
in your text editor.Add the following code:
function getFields(entry) {
const { fields } = entry;
if (fields?.image) {
fields.image = fields.image.fields.file.url;
}
if (!fields.id && entry.sys?.id) {
fields.id = entry.sys?.id;
}
if (fields.unfrmOptEnrichmentTag) {
fields.enrichments = fields.unfrmOptEnrichmentTag;
delete fields.unfrmOptEnrichmentTag;
}
if (fields.unfrmOptPersonalizationCriteria) {
fields.pz = fields.unfrmOptPersonalizationCriteria;
delete fields.unfrmOptPersonalizationCriteria;
}
return fields;
}About this stepThis code should look familiar because it is very similar to the code you added to rename the field for the enrichment tags. In this case, the personalization criteria is stored in Contentful in a field
unfrmOptPersonalizationCriteria
, where Uniform expects the fieldpz
.Add the following code:
export async function fetchVariations(id) {
const entry = await fetch(id);
const { unfrmOptP13nList } = entry;
return unfrmOptP13nList.map(getFields);
}In Contentful, open the content entry Home Page Heros.
Navigate to Info and note the ENTRY ID.
Open the file
./pages/index.jsx
.Make the following changes:
import { Hero } from "../components/Hero";
import { fetchVariations } from "../lib/cf/fetch";
export async function getStaticProps() {
const fields = await fetchVariations("29zgaRj1vU1idUq3ydCQeU");
return {
props: { ...fields }
}
}
export default function Home(props) {
return <Hero {...props} />
};About this stepFor the call to
fetchVariations
, make sure you use the entry ID for the Home Page Heros entry.Make the following changes:
import { Hero } from "../components/Hero";
import { Personalize } from '@uniformdev/context-react';
import { fetchVariations } from "../lib/cf/fetch";
export async function getStaticProps() {
const fields = await fetchVariations("29zgaRj1vU1idUq3ydCQeU");
return {
props: { ...fields }
}
}
export default function Home({ variations }) {
return (
<Personalize
variations={variations}
name="heroPersonalized"
component={Hero}
/>
);
};About this stepThe Personalize component receives the collection of the possible variations that were configured in the personalized list in Contentful and is able to evaluate the personalization criteria to find a match.
Finished code
We provide a finished version of the UniformConf application that you can get running in your preferred environment.
- StackBlitz
- Local machine
- Open StackBlitz to create your development environment.
After you open StackBlitz you will see an error in the terminal that environment variables are not set. You must set the variables in
.env
to match the settings that apply to your Contentful and Uniform environments.Changing the
.env
file should cause the container to restart. If it doesn't, enter the following command in the StackBlitz terminal:npm run dev
In the activity bar the plug icon indicates that 1 port is in use. Click this icon.
You will see links to the web app running on the 1 port. Click the link for Port 3000.
A new browser tab opens. This is your Next.js application running in develop mode.
https://uniformdev-uniform-docs-examples-######--3000.local.webcontainer.io/
Enter the following commands:
git clone https://github.com/uniformdev/examples
cd examples
cd examples/docs/apps/contentful/nextjs/finishedSet variables in
.env
to match the settings that apply to your Contentful and Uniform environments.Enter the following command:
npm install
Enter the following command:
npm run dev
About this stepThis will start the Next.js app in develop mode on port 3000.
Next steps
Now that you have a fully functional Next.js application powered with content from Contentful and personalized using Uniform Context, you can use this as a starting point for exploring everything Uniform has to offer, including:
- Presentation builder - Uniform Canvas offers a tool that gives content authors control over presentation within constraints defined by developers, in a way that fits into modern digital production processes.