How To Do Lightning Fast and Decoupled Personalization With Umbraco Heartcore
In the headless and decoupled world, performance and user experience are at the center when creating new and compelling websites. The importance of driving relevant conversations with site visitors are documented time and time again. The relevant discussions are driven by personalized content delivered to individual visitors based on behavior and actions performed on the digital channels.
However, these tailored journeys and content experiences have always been complex to implement and even harder to maintain. This can become even more complex in a headless world and doing it without compromising performance by introducing unnecessary dependencies on origins can be very tricky.
But what if you could actually do this, do it easy and make it work quickly on Umbraco Heartcore as well?
Uniform provides the fastest possible personalized websites possible by enabling edge or client-based personalization and decoupled tracking without any origin dependency during runtime. All data and functionality can be delivered on edge and as first-party assets embedded into your application for the best possible performance and security.
For more information on security when delivering websites with Uniform and Jamstack: https://uniform.dev/blogs/what-you-need-to-know-about-jamstack-personalization-and-privacy
The principles shown in this article can be applied to any CMS from which you can extract content on a REST API, GraphQL API or similar, for delivery through a front-end centric application built with e.g., Next, Nuxt or similar.
Let's get started!
This short video will show what we are aiming to implement and how the personalization will work in practice afterwards.
What do you need to follow along:
Access to Umbraco Heartcore
You can set up a trial account on try.umbraco.com and from there, you can create yourself an Umbraco Heartcore project.
Access to Uniform
You can set up a free account on uniform.app and deploy a starter to have some default intents and signals generated that we can use in this PoC. For documentation on Intents and Signals, please visit: Uniform documentation Don't worry about which CMS is chosen during the setup, as we will "bypass" that in this demo.
The simplest next.js starter
Clone from here: Github
If you fancy Nuxt - read this type: entry-hyperlink id: 2rrvWxDhRnyeIPy5AtRpjn as well.
Setting up Umbraco Heartcore
To get started we need to define some simple content types in Umbraco Heartcore – a json output can be found here: Github
Please see screenshots for details.
Document types in Umbraco:
Overview of contenttypes in the simple solution done. The "Compositions" and "Elements" are folders for organization purposes.
"Hero Composition" is used a Composition in the "Textpage" contenttype. All Property Editors are defined directly in the Contenttype.
"Personalized Hero" is used as a Composition in the "Frontpage" and utilizing the element Content type "Hero" as nested Content. See next image.
"Hero" is the Document type that will be used to hold the hero variants that are used for personalization based on the intents from Uniform. The Document type is used, as described above in the "Personalized Hero" Document type.
"Uniform Data" hold the relevant data needed to inform Uniforms tracker about how to do personalization. The Document type are used in the "Hero" and "Textpage" Document types.
"Frontpage" is the Document type that are used as the Root type and and use the "Personalized Hero" as the Composition type.
"Textpage" use "Hero Composition" as the Composition type to get the relevant fields used on the page.
Content created based on above Content types:
Create the root node in the tree - "Home" and define "Hero variants". The field "unfrmOptIntentTag" hold the name of the Intent from Uniform to define when the particular "Hero variant" are to be shown. In above image - Item 1 will be shown if I have the "marketer" intent, which I pick up from visiting the "Marketer" page..more on that later.
The "Developer" variant.
The "Call for paper" campaign variant.
The "Default" variant. Both Uniform field need to be empty here. The type of integration we do here is somewhat manual and shallow as at the time of writing, there is no way to create custom Property Editors etc. in Umbraco Heartcore and therefore we are unable to pull data etc. directly from Uniform.
Defining the "Developer" page. The Uniform field are both filled with data here. The "unfrmOptIntentTag" field holds the Intent ID from Uniform and the "unfrmOptIntentTagStrength" hold the amount of "points" consuming this page will accumulate againtst
There is not much content here, except for a few headlines and some information necessary to instruct Uniform on tracking behavior and doing personalization.
Essentially, we have defined two simple pages and a hero. The data needed for Uniform is embedded into an Element Content Type in Umbraco Heartcore called “Uniform Data” and for this simple scenario it holds only two fields that, in this PoC, are manually filled with data.
unfrmOptIntentTag will hold the Intent name configured in Uniform, and the field
unfrmOptIntentTagStrength will hold the value that we initially choose for the content if any value is needed. In this PoC – no value is given in any Personalizable hero.
Setting up code and getting Uniform into our code
The application we “build” is based on a very simple next.js starter. Run the below code in your favourite editor or terminal to get started:
To get Uniform and the packages necessary for communicating with Umbraco Heartcore into our project, install the following packages with your favourite packagemanager. Please note that version numbers might have changed since publishing this blogpost:
Uniform runs all personalization either on the Edge or directly in the client browser. All is handled through the tracker, just imported, and the Intent Manifest.
The intent manifest - defines an array of known intents and their signals. It is usually provided at build time by a request to Uniform Optimize and baked into your JS bundle. https://docs.uniform.app/optimize/dev/architecture/#data-sources.
In this PoC, we manually generate and deploy the intent manifest into our project. https://docs.uniform.app/optimize/dev/getting-started/#fetch-intent-manifest. You could however, easily include manifest generation as part of your build.
If you do no customizations to the manifest generation, the manifest will end up in the /Lib folder and look like this: https://github.com/uniformdev/uniform-umbraco-heartcore-poc/blob/main/lib/intentManifest.json.
We can now begin to build our small application and fetch some data from Umbraco Heartcore.
I have created a simple _app.tsx that allows me to bootstrap my entire application into Uniforms tracker.
localTracker is defined in: ../lib/localtracker.tsx
Which very simply just creates an instance of the Uniform Tracker with the previously generated Intent Manifest as the input:
Next, we have to setup the clientconnection to Umbraco Heartcore - https://github.com/uniformdev/uniform-umbraco-heartcore-poc/blob/main/lib/HearthCoreClient.tsx
We can now create the
Hero that we will later Personalize.
The actual data for the
Hero’s will be fetched in the
Index.tsx that we create soon.
First, let's create our
hero.tsx, we define an interface for the
Fields that the
Hero component should contain and set up the actual hero component.
The Uniform specific fields in the
HeroFields interface are “type” and “intentTag” – which originate from the Interface PersonalizableListItem and are necessary for the Tracker to correctly track the intent tags and personalize on them as well.
The definition of the Hero component is not super complex but contains one important Uniform piece:
We add this line of code to instruct Uniform that this particular component should influence how visitors get classified based on intents in the system. https://docs.uniform.app/optimize/dev/react/behavior/
Now that the hero component is in place let’s create a Personalized version to use on the front page.
Create the PersonalizedHero.tsx in the /components folder.
<Personalize /> component is how we do the actual personalization with the list of Hero components parsed in once we get to the
<Personalize /> component can accept a list of possible variants and emit a rendered React component based on the type(s) of variants determined to be most relevant to the visitor.
Now we have all the basics in place, and can now move on to create the index.tsx page that will show the actual personalization.
We set up the index component first:
props.heroFieldsList are populated as part of
We use “
MyClient” to fetch content from Umbraco, similar to this output.
And by mapping out the contents of “heroVariants” to, we create a list that we can use in
Because we have not integrated Uniforms application into the backend of Umbraco Heartcore, we need to “manually” map the array:
To get from Umbraco’s JSON output to the correct
IntentTags Interface format for each
HeroFields instance we create our map function. For this purpose, I created the
mapIntentJsonToIntentTag function in /Lib/utils.tsx.
It will return IntentTags objects in the correct format to use for Personalization and behavior tracking.
The last piece of the puzzle is to create pages containing content tagged with specific intents, to drive personalization.
I have created two pages,
marketer.tsx. Both contain a Hero component with data fetched from Umbraco and rendered.
Because we already set
useBehaviorTracking in the hero component – we trigger Uniform’s tracker and get tagged with the relevant intent by visiting these two pages.
That’s it – you now have blazing fast personalization executed completely decoupled from your backend and with the ability to work in a Jamstack manner with statically generated pages, etc., for optimum speed on all pages.
For more information and to see a Demo, visit http://uniform.dev
I joined Uniform in December 2020. In the past, I have spent 6,5 years with Sitecore, a year with Avanade and running my own consulting business for 10 years. Based in "sunny" Denmark.....LinkedIn