Adding Uniform to Create Next App
18th Nov 2021
I have found it super easy to add Uniform Canvas to a bare-bones NextJs app. I thought I would show you what it takes to go from Zero to Hero with a very basic setup. This will then give a good foundation to add more things on top.
Create Next App
I'll start with the familiar, I'll use yarn create next-app --ts
to generate a bare-bones NextJs application with TypeScript.
yarn create next-app --ts...⨠Done in 13.16s.Initialized a git repository.Success! Created zero-to-hero at /Users/davetayls/projects/test-projects/zero-to-heroInside that directory, you can run several commands:
Add some Uniform Packages
There are two packages I need to get started with a React application:
@uniformdev/canvas
to use core canvas utilities@uniformdev/canvas-react
to use React specific components
Setup the Uniform Canvas Client
The interaction with the Uniform APIs happens through an instance of CanvasClient
so I will need to create that instance as well as setup a Uniform project to connect it to.
I created a new file src/uniform/uniformClient.ts
and added the following code.
import { CanvasClient } from '@uniformdev/canvas'export const uniformClient = new CanvasClient({apiKey: process.env.UNIFORM_API_KEY,projectId: process.env.UNIFORM_PROJECT_ID,})
This same uniformClient
can be used across the rest of the codebase.
Uniform Project Environment Variables
You will notice that the CanvasClient
needs two environment variables so that it can connect to the right project. It will need UNIFORM_API_KEY
and UNIFORM_PROJECT_ID
. I have set these in a .env
file in the root of the application.
First, I need to create a project and get those values.
Then I need to create an API key to use locally (don't worry, this project and API key no longer exists!) đ¨
The final .env file now looks like this:
UNIFORM_API_KEY=my-api-keyUNIFORM_PROJECT_ID=my-project-id
Basic Empty Enhancers
Uniform doesn't store your data, it just stores pointers and IDs to the various systems you want to integrate with. This simplifies access to the connected systems and keeps the orchestration of them simple.
I still need the actual data within those systems to be able to render components on a website. This is where enhancers come in, they take the raw uniform data and add the values for those pointers from each system you have configured.
I'm not going to connect anything for this example, I can look at that later. What I want to do is setup an empty EnhancerBuilder
so that I can add enhancers later.
I will put this into a new file src/enhancers/index.ts
.
import { EnhancerBuilder } from '@uniformdev/canvas'export const enhancers = new EnhancerBuilder()
Convert the Main Section into Components
My next job is to convert the <main>
section of the index.tsx page into Uniform components.
This means I will have 3 basic components in my components folder:
- Title
- ContentBlock
- Grid
I have just copied the components exactly, I will look at connecting them to external content at a later date. So the following JSX gives me the exact same page as I started with:
<main className={styles.main}><Title/><ContentBlock/><Grid/></main>
But that's not where I want to go, I'll create these components in Uniform Canvas.
btw - these components can also be created in yaml files and synced to a project. I'll want to keep these definitions in the codebase later on.
I can then add a "Composition" which is basically a component which can be used like a layout. I'll add a single "slot" which is a place to put other components. In this slot I will allow all three of my new components.
That deals with the component I will use for my main composition. Now to actually create the composition and add my three components in to it.
Build the Components from Uniform Canvas
Now for the final piece of the puzzle. I need a function which will return a React component based on the ID.
I'll create a file src/components/resolveRenderer.ts
import { ComponentType } from "react"import { ComponentProps, RenderComponentResolver } from "@uniformdev/canvas-react"import { Title } from './Title'import { ContentBlock } from './ContentBlock'import { Grid } from './Grid'const componentMap = new Map<string, ComponentType<ComponentProps>>([['Title', Title],['ContentBlock', ContentBlock],['Grid', Grid]])export const resolveRenderer: RenderComponentResolver =({ type }) => componentMap.get(type) || null
I can then include the composition data in getStaticProps
.
import { uniformClient } from "../src/uniform/uniformClient"import { ComponentInstance, enhance } from "@uniformdev/canvas"import { enhancers } from '../src/enhancers'//...export const getStaticProps: GetStaticProps = async (context) => {const { composition } = await uniformClient.getCompositionBySlug({slug: '/home',})await enhance({ composition, enhancers, context })return { props: { composition } }}
And finally I use the Composition
component inside the <main>
tag to render the components we had before.
import { Composition, Slot } from '@uniformdev/canvas-react'import { resolveRenderer } from "../src/components/resolveRenderer"// ...interface HomeProps {composition: ComponentInstance}const Home: NextPage<HomeProps> =({ composition }) => {//...<main className={styles.main}><Compositiondata={composition}resolveRenderer={resolveRenderer}><Slot name="main" /></Composition></main>
Now to build out the app...
That is it, the app is now ready to build. I have all the usual NextJs tools and functionality at my disposal and can inject Uniform Compositions, Personalisation and Testing wherever I want to use them.