Uniform how-to/Building websites with Uniform: a beginner's guide
31 min read
Building websites with Uniform: a beginner's guide
Given today’s ever-dynamic interactions among content managers, content architects, and developers, optimizing workflows for rapid development and iteration is crucial. Hence the birth of digital experience composition (DXC).
This tutorial steps you through the process of generating a SaaS landing page for a fictional ride-sharing application called Joyride. You’ll learn how to build a personalized website powered by Uniform, a DXC Platform (DXCP), with content from Sanity, a modern content management system (CMS). You create the front end with Gatsby.js, a React.js framework for constructing high-performance and SEO-friendly websites.
The concepts, architecture, and methods described reflect the foundation of and apply to Uniform-based development efforts.
A DXC platform offers an abstraction layer for all content and composable services—a single source of truth of content—along with a no-code interface for managing that content by practitioners and business users.
CMSes, digital asset-management systems, analytics tools, product information management (PIM) systems, headless commerce systems, etc., all connect to the DXC platform, Uniform, with the resulting data piped structurally and rendered through a front end or presentation layer.
This tutorial shows you how to perform the following high-level steps, which are typical of building websites with Uniform:
Set up a CMS instance and application.
Sign up for a Uniform account and integrate with a CMS.
First, create a project with content from Sanity, as follows:
1. Create a project folder on your computer called joyrideClick to copy.
2. In the joyrideClick to copy folder, create a Sanity installation and project called cmsClick to copy by following these steps in the Sanity documentation.
The rule of thumb is to add core content that’s long-lived and reusable in a CMS, e.g., feature lists, articles, offerings, etc. In a later step, you will, through Uniform, add data that supports the content's presentation and navigation.
3. In the schemasClick to copy directory in your Sanity project, create two schema files called genericContentClick to copy and offerings with the following fields:
This tutorial does not cover the intricacies of publishing Sanity projects. For details on that topic, see these two articles: one on how Schemas work in Sanity and the other on how to publish content to your Sanity Studio.
Once you’ve published the project, add and publish three genericContentClick to copy and four offeringClick to copy documents.
5. Note your Sanity project ID and dataset, which are different from the deployed CMS interface, from your Sanity account’s dashboard for later use in Uniform.Click to copy
Recall that Uniform is a DXC platform with a no-code tool for connecting data sources and composable systems, thus enabling business users to craft digital experiences.
As a first step, sign up for a new Uniform account.
Uniform creates your first project with the essential components, compositions, and personalization entities. You’ll modify that project to fit this tutorial. Now do the following:
Uniform creates your first project with the essential components, compositions, and personalization entities. You’ll modify that project to fit this tutorial. Now do the following:
Rename your project to Joyride.
Navigate to the Security tab of your team dashboard to create an API key: Name the key and click Save.
Note down the new API key for use in your front-end application later.
Components in Uniform are content agnostic, mirroring those in front-end applications. To create webpages, Uniform uses Components as reusable blocks of content to model your layout.
Separately, Compositions in Uniform assemble Components to create a structured presentation—a webpage in this tutorial.
Uniform ships sample Starters with Components and Compositions, which are displayed in the Integrations menu of your Uniform project.
To integrate with Sanity:
Click the Integrations tab in your project and select and add the Sanity integration (under Content).
Configure the Sanity integration by authenticating your account, choosing a project, and specifying the dataset. A Sanity Entry Selector type then becomes available for selection in Components.
1. In your Uniform project, navigate to Canvas, where content modeling and page building occur.
2. Optional. Delete the Compositions and Components shipped with the starter if you prefer not to modify them for your project.
A brief background:
Components in Uniform contain parameters and slots. Uniform parameters, which correspond to the fields for the actual content, are similar to component props in React, e.g., title, description, and link. Uniform slots, which are containers for other child components, are similar to the children component prop in React.
Compositions in Uniform, which are also components, are root components that form the basis of other components. When creating a Component, checking the Composition Component box marks that Component as a Composition. You can create Components in a different form with Variants.
3. Create the Components with the schedule below. Apply any Component icon you prefer.
Name: Call to Action
Parameters and type
Title: text
Link: text
Name: Generic Card
Parameters and type
Title: text
CTA Link: text
CTA Title: text
Body: Sanity Entry Selector
Allowed Content Types: genericContentClick to copy
Searchable Fields: bodyClick to copy
Name: Generic Grid
Parameters and type
Title: text
Slots
Name: Items
Required Component Quantity: Minimum 3 (no maximum)
Searchable Fields: offeringNameClick to copy, offeringImageClick to copy, offeringSummaryClick to copy
Name: Offering Grid
Parameters and type
Title: text
Slots
Name: Offerings
Required Component Quantity: Minimum 2 (no maximum)
Allowed Components: Offering Card
Name: Page
Composition Component: trueClick to copy. (Check the box.)
Parameters & type
Meta Title: text
Required: trueClick to copy
Slots
Name: Content
Allowed Components: All except Page
All public IDs, a camelCaseClick to copy version of the name, are automatically generated by Uniform.
4. Navigate to the Compositions side menu and create a Composition called HomepageClick to copy: Select the PageClick to copy Composition type, open the Composition, and assign it a slug of homeClick to copy.
homeClick to copy is a unique identifier for the Composition. You’ll fetch Compositions with a slug in the front end.
5. Save and publish the empty Composition homeClick to copy.
4. Install Uniform dependencies with this command line in your project:
1npm i @uniformdev/canvas @uniformdev/canvas-react @uniformdev/canvas-sanity @uniformdev/cli @uniformdev/context @uniformdev/context-react dotenv
That command line installs Uniform packages to manage Compositions in Canvas, a React.js software development kit (SDK) for Canvas, a Uniform Context package for managing personalization, a React.js SDK for Context, the Uniform CLI, a Uniform SDK for Sanity and dotenv to manage environment variables in Node.js.
To avoid version-mismatch errors, ensure that the Uniform packages have the same version number.
5. In your project’s root directory, create a .envClick to copy file with the following keys:
Be sure to add the values you obtained previously from Sanity and Uniform. Variables prefixed with GATSBYClick to copy are required and processed by Gatsby as runtime variables.
6. Start a local development server on localhost:8000Click to copy for your Gatsby application by typing in your project directory:
npm run devClick to copy
In the src/componentsClick to copy directory of your project, create Components, which would match those on Uniform, as you’ll soon see when connecting your application to Uniform.
Tip: To skip this Component-creation step, clone the branch by setting up a base project with the Components in this repository.
Note the SlotComponentClick to copy from Uniform, which identifies the containers for child components with Slots with the name prop as specified in Uniform.
Gatsby touts a file system-based routing and builds files in the src/pages directory as pages. index.tsx is the website’s homepage. By fetching Uniform data at runtime with Gatsby’s server-side rendered (SSR) function, serverData, you can publish updated content on Uniform without rebuilding the static website.
Also, you can implement robust personalization in SSR mode through Uniform.
Do the following:
Create a function that renders a page with the code below in src/pages/index.tsxClick to copy:
1import*asReactfrom"react";2import type {3PageProps,4}from"gatsby";5import{PageComponent}from"../components/Page";6constHomepage=(props:PageProps)=>{7return(8<PageComponent>9{// component to go in here}10</PageComponent>11);12};13exportdefaultHomepage;
Depending on your application's architecture, you can fetch Compositions from Uniform in multiple ways. For this tutorial, fetch each Composition on its corresponding page.
Another flexible way is to find out the page path and fetch a Composition based on that page. For this tutorial, keep it basic and fetch Compositions based on their Slug.
Add the code below to index.tsxClick to copy to create a function that fetches a Composition by Slug:
1import*asReactfrom"react";2import type {PageProps}from"gatsby";3import{PageComponent}from"../components/Page";4import{CanvasClient}from"@uniformdev/canvas";5// function to get composition6exportconstgetComposition=async()=>{7const client =newCanvasClient({8apiKey: process.env.GATSBY_UNIFORM_API_KEY,9projectId: process.env.GATSBY_UNIFORM_PROJECT_ID,10});11const{ composition }=await client.getCompositionBySlug({slug:"home"});12return composition;13};14constHomepage=(props:PageProps)=>{15return(16<PageComponent>17{// component to go in here}18</PageComponent>19);20};21exportdefaultHomepage;
Enhancing the Compositions
To ensure accuracy, instead of storing actual data from multiple sources, Uniform stores references to the data. Based on the reference passed in the Composition payload, you enhance the data, which converts the reference to the actual content.
For Sanity and other integrations, Uniform has created an enhancer to juice up the fetched Composition with the actual data.
Create a function with the following code in index.js to enhance the composition:
1// Other imports go in here2import{3CanvasClient,4ComponentInstance,5 enhance,6EnhancerBuilder,7}from"@uniformdev/canvas";8importcreateSanityClientfrom"@sanity/client";9import{10CANVAS_SANITY_PARAMETER_TYPES,11 createSanityEnhancer,12}from"@uniformdev/canvas-sanity";13// function to get composition by slug14exportconstgetComposition=async()=>{15// function definition goes in here16};17// Sanity enhancer function18exportasyncfunctionenhanceComposition(composition:ComponentInstance){19const sanityClient =createSanityClient({20 projectId: process.env.GATSBY_SANITY_PROJECT_ID,21 dataset: process.env.GATSBY_SANITY_DATASET,22 useCdn:false,23});24// Create a modified enhancer to enhance the images and return offeringImage25const sanityEnhancer =createSanityEnhancer({26 client: sanityClient,27modifyQuery:(options)=>{28 options.query=`*[_id == $id][0] {
29 "offeringImage": offeringImage.asset->url,
30 ...
31 }`;32return options;33},34});35awaitenhance({36 composition,37 enhancers:newEnhancerBuilder().parameterType(38CANVAS_SANITY_PARAMETER_TYPES,39 sanityEnhancer
40),41 context:{},42});43}44constHomepage=(props:PageProps)=>{45return(46<PageComponent>47{// Components to go in here}48</PageComponent>49);50};51exportdefaultHomepage;
The above block does the following:
Import all the required modules and methods.
Create a new instance of the Sanity client with your project ID and dataset to leverage the Sanity enhancer.
Create an enhancer with the createSanityEnhancerClick to copy function.
By default, the Sanity enhancer does not enhance images, i.e., the image reference remains as is. Instead, the modifyQueryClick to copy option modifies the enhancer. The modified query matches Sanity’s query-language syntax, fetching all the data while adding an offeringImageClick to copy field with an image URL as the value.
Pass the Composition and a prebuilt Sanity enhancer to the enhance function. Calling the sanityEnhancerClick to copy function with the raw Composition as an argument enhances it.
Rendering the enhanced Composition
To render the enhanced Composition, do the following:
1. Fetch the composition and run the enhancer on it in Gatsby’s SSR-specific getServerDataClick to copy function by adding the code below to the pages/index.tsxClick to copy file:
1import*asReactfrom"react";2import type {3GetServerDataProps,4GetServerDataReturn,5PageProps,6}from"gatsby";7// Other imports go in here8// function to get composition9exportconstgetComposition=async()=>{10// function definition goes in here11};12// Function to fetch Composition server-side13exportasyncfunctiongetServerData({14 headers,15 method,16 url,17 query,18 params,19}:GetServerDataProps):GetServerDataReturn{20const composition =awaitgetComposition();21// Enhance composition22awaitenhanceComposition(composition);23// Return enhanced composition24return{25status:200,26props:{ composition },27};28}29// Sanity enhancer function30exportasyncfunctionenhanceComposition(composition:ComponentInstance){31// function definition goes in here32}33constHomepage=(props:PageProps)=>{34const{ serverData }= props;35return(36<PageComponent>37{// render components here}38</PageComponent>39);40};41exportdefaultHomepage;
The getServerDataClick to copy function fetches and enhances the Composition, making the returned data available to the page Component HomepageClick to copy as serverDataClick to copy.
2. Create a resolver function that matches a Uniform Component with a Component in your Component library: Add the following code to pages/index.tsxClick to copy:
1import*asReactfrom"react";2import type {PageProps}from"gatsby";3import{PageComponent}from"../components/Page";4import{Hero}from"../components/Hero";5import{CTA}from"../components/CTA";6import{GenericGrid}from"../components/GenericGrid";7import{GenericCard}from"../components/GenericCard";8import{OfferingCard}from"../components/OfferingCard";9import{OfferingGrid}from"../components/OfferingGrid";10import{ComponentInstance}from"@uniformdev/canvas";11import{ComponentProps}from"@uniformdev/canvas-react";12import{Default}from"../components/Default";13// function to get composition14exportconstgetComposition=async()=>{15// Function definition goes in here16};17// Function to fetch Composition serverside18exportasyncfunctiongetServerData({19 headers,20 method,21 url,22 query,23 params,24}:GetServerDataProps):GetServerDataReturn{25// Function definition goes in here26}27// Sanity enhancer function28exportasyncfunctionenhanceComposition(composition:ComponentInstance){29// Definition goes in here30}31// Resolve Render function32exportfunctioncomponentResolutionRenderer(33component:ComponentInstance34):React.ComponentType<ComponentProps<any>>{35switch(component.type){36case"hero":37returnHero;38break;39case"callToAction":40returnCTA;41break;42case"genericCard":43returnGenericCard;44break;45case"genericGrid":46returnGenericGrid;47break;48case"offering":49returnOfferingCard;50break;51case"offeringGrid":52returnOfferingGrid;53break;54default:55returnDefault;56break;57}58}59constHomepage=(props:PageProps)=>{60return(61<PageComponent>62{// Render components here}63</PageComponent>64);65};66exportdefaultHomepage;
In the above code, the componentResolutionRendererClick to copy function switches the returned Component based on the Component typeClick to copy.
3. Render the Composition with Uniform’s CompositionClick to copy Component by adding this code:
1import*asReactfrom"react";2import type {3GetServerDataProps,4GetServerDataRetur5PageProps,6}from"gatsby";7import{ComponentInstance}from"@uniformdev/canvas";8import{9ComponentProps,10Composition,11Slot,12}from"@uniformdev/canvas-react";13// function to get composition14exportconstgetComposition=async()=>{15// Definition goes in here16};17// Function to fetch Composition serverside18exportasyncfunctiongetServerData({19 headers,20 method,21 url,22 query,23 params,24}:GetServerDataProps):GetServerDataReturn{25// definition goes in here26}27// Sanity enhancer function28exportasyncfunctionenhanceComposition(composition:ComponentInstance){29// Definition goes in here30}31// Resolve Render function32exportfunctioncomponentResolutionRenderer(33component:ComponentInstance34):React.ComponentType<ComponentProps<any>>{35// Definition goes in here36}37constHomepage=(props:PageProps)=>{38const{ serverData }= props;39const{ composition }= serverData as any;40return(41<PageComponent>42<Composition43 data={composition}44 resolveRenderer={componentResolutionRenderer}45></Composition>46</PageComponent>47);48};49exportdefaultHomepage;
4. Restart your Gatsby development server for the updated page with the homepage Composition fetched from Uniform and rendered with your local Components.
Uniform renders with the Composition Component. For child elements, rendering is done with named Slot components in the parent Components.
Here’s a GitHub gist on the content of the index.tsxClick to copy component.
Uniform features a rich editing tool for building interfaces based on the existing layout and components in the front end. To use that tool, first set up the front end to provide context to Uniform.
Available from Uniform are front-end functions that enable contextual editing or, in some cases, bake it into the Canvas package. For React.js, Uniform offers useContextualEditingClick to copy in canvas-reactClick to copy.
First, add the code below to pages/index.tsxClick to copy to import and set up contextual editing in the renderClick to copy function of the page:
1import*asReactfrom"react";2import type {3GetServerDataProps,4GetServerDataReturn,5PageProps,6}from"gatsby";7import{PageComponent}from"../components/Page";8import{9ComponentProps,10Composition,11 useContextualEditing,12}from"@uniformdev/canvas-react";13// All other imports go here14// Sanity enhancer function goes here15// Function to get composition goes here16// Function to fetch Composition serverside for use in the page component, goes here.17// Resolve Render function goes here18constHomepage=(props:PageProps)=>{19const{ serverData }= props;20const{composition: initialCompositionValue }= serverData as any;21const{ composition }=useContextualEditing({22 initialCompositionValue,23enhance:async({ composition })=>{24awaitenhanceComposition(composition);25return composition;26},27});28return(29<PageComponent>30<Composition31 data={composition}32 resolveRenderer={componentResolutionRenderer}33></Composition>34</PageComponent>35);36};37exportdefaultHomepage
The block above performs these tasks:
Set up useContextualEditingClick to copy with the initial compositionClick to copy value.
Specify the enhancerClick to copy function for the Composition.
Pass the new compositionvalueClick to copy to the Composition component.
Specifying the enhancerClick to copy function provides Uniform with the context of what enhancers are in use. Without that information, Uniform’s contextual-editing interface renders Component references instead of the enhanced version.
To complete the contextual-editing setup, specify your website’s preview URL in Uniform. During local development, that URL is the local host, i.e., localhost:8000Click to copy, which requires the local development server to run at all times. Alternatively, specify a URL for a deployed host.
Live-preview URLs are an SSR Component of websites that direct the web framework to generate the pages on the server with the updated content. Thus, the logic behind the URL (seen as the entry URL) can handle redirects to other pages.
Different web frameworks create preview URLs in different ways. Uniform providesslugClick to copy information as query parameters in the request to the preview URL with which you can manage page redirects.
For this tutorial, specify the local development URL without redirects, as follows:
On Uniform, go to Canvas and open the Homepage Composition. Enter the URL in the Preview URL text field
Alternatively, specify the preview URL by first choosing Project dashboard > Settings > Canvas Settings for the dialog box. To avoid CORS errors during composition enhancement, add the localhostClick to copy URL to the list of allowed domains in your Sanity admin dashboard.
The webpage is then loaded in the visual panel (midsection) of the Canvas editor.
Editing the structure on the left panel renders immediately in the middle. Selecting an element on the website highlights the corresponding component in the structure panel. See this example:
You can now create Compositions on Uniform and compose the Components with the contextual editor.
Uniform Context is a group of elements and tools for identifying, classifying, personalizing, and analyzing digital experiences. Personalization with Uniform takes two steps:
Set up the personalization artifacts and criteria on Uniform—usually done by practitioners and business users.
Set up the front end to recognize Uniform Context.
Setting up Signals in Uniform
The goal of the personalization demo below is to identify and convert potential drivers.
Uniform offers various ways to classify users. For this tutorial, identify a user with Signals and show that individual a unique version of the website's Hero section. Your target users are those who follow a promotion link with the parameter utm_campaign=driverlaunchClick to copy.
Perform these steps:
1. In your Uniform project, navigate to Personalization > Signals and create a Signal with the name Driver Campaign. Signals have improved scores if users match the specified criteria up to the score's threshold.
2. With the Signal builder, set up a Signal with the criterion Query String named utm_campaignClick to copy, which equals driverlaunchClick to copy, like this:
3. Leave the Signal score and threshold unchanged at 50 and 100, respectively.
4. Save and close the new Signal.
As is obvious on the interface, you can chain Signal criteria.
5. In the personalization dashboard, click Publish in the top-right corner to publish all Context entities, including Personalization.
Be sure to publish each time you change a Context entity.
6. In the Canvas editor, personalize the Hero component with a different title, description, and CTA: Hover below the Content slot in the structure panel and add a Personalization component.
In the right panel, add an analytics name as the personalization element when the data is sent to your configured analytics tools. Leave empty the Number of variations to show field. That’s the number of personalized variants—with a default of 1—to show at a time.
Since you specified the Personalize Component as being allowed while defining the Page Component's Slot, you can add that Component to the Content Slot.
7. Add the following content to two new Hero Component variants in the Personalized Variations slot of the structure panel.
Component: Hero
Title: Ride for us
Description: We don't just move people around. We move feelings and create lasting memories for people. Come join us to deliver joy!
8. Assign a Signal to the variant "Ride for us”: Click the variant in the structure pane and, in the editing pane on the right, click the Context tab.
9. Click Add Criteriaand select the Driver Campaign Signal that exceeds a score of 50.
Users who match that Signal see this version of the hero.
10. Save the changes. Leave the context tab of the second Hero variant, Let's take a Ride, as is. That’s the default variant presented to users who don't meet the Signal criteria.
11. Delete the unpersonalized Hero Component you added previously.
12. Save and publish the composition.
The visual pane in the middle is now broken because you must design the website to render personalized components. The next section shows you how to do that.
Personalizing the front end
While configuring personalization on Uniform, set it up in Gatsby so that Uniform renders the Composition with the right Components.
Since you’ve already installed context packages, update the package.json file to fetch Uniform's manifest that contains all Uniform Context configurations and setups, and save the manifest file to a designated folder. Do the following:
1. Update the package.json file's script entries with the code below:
2. Download the manifest file to the project's root directory with the uniform:download-manifestClick to copy script.
Rerunning that script overwrites the manifest file.
3. Download the manifest file on dev and build commands.
4.,Rerun the dev command to download the manifest file.
Your Uniform API key needs the correct permissions to download the manifest file. In case you encounter an error on lack of authorization in step 4, update the API key’s permissions in your Uniform account.
Set up Context on the pages by wrapping each page with the UniformContextClick to copy component by means of the Gatsby-specific wrapPageElementClick to copy API in the project's root files, gatsby-browser.tsxClick to copy and gatsby-ssr.tsxClick to copy, like this:
1// gatsby-browser.tsx2import"./src/styles/global.css";// tailwind required3import{UniformContext}from"@uniformdev/context-react";4import{5Context,6 enableContextDevTools,7ManifestV2,8}from"@uniformdev/context";9importmanifestfrom"./uniform-manifest.json";10import{GatsbyBrowser}from"gatsby";11import*asReactfrom"react";12const context =newContext({13 defaultConsent:true,14 plugins:[enableContextDevTools()],// Use this to enable the Chrome plugin15 manifest: manifest asManifestV2,16});17exportconstwrapPageElement:GatsbyBrowser["wrapPageElement"]=({18 element,19})=>{20return<UniformContext context={context}>{element}</UniformContext>;21};22Footer23// gatsby-ssr.tsx24import"./src/styles/global.css";25import{UniformContext}from"@uniformdev/context-react";26import{27Context,28 enableContextDevTools,29ManifestV2,30}from"@uniformdev/context";31importmanifestfrom"./uniform-manifest.json";32import type {GatsbySSR}from"gatsby";33import*asReactfrom"react";34const context =newContext({35 defaultConsent:true,36 plugins:[enableContextDevTools()],// Use this to enable the Chrome plugin37 manifest: manifest asManifestV2,38});39exportconstwrapPageElement:GatsbySSR["wrapPageElement"]=({ element })=>{40return<UniformContext context={context}>{element}</UniformContext>;41};
You’ve now imported the required modules in both files, instantiated the Uniform context, specified the downloaded manifest file, and, as an optional step, enabled the Chrome plug-in, with which you can simulate context scenarios without user input.
In addition, since you’ve fetched Compositions with Gatsby's getServerDataClick to copy function, you’ve wrapped all the pages with the UniformContextClick to copy Component in SSR mode.
6. Restart your development server. To display a personalized homepage, click the link with the UTM parameter localhost:8000?utm_campaign=driverlaunchClick to copy.
Subsequently, content updates or personalization entities from business users are immediately served to end-users on a new build.
The personalized variants on Uniform are displayed in the Canvas editor's visual pane. Clicking a variant renders it.