Skip to main content

Adding Contentful data

tip

This tutorial assumes you have completed the basic tutorial and have the artifacts from that already present in Uniform.

Let's add some content data from Contentful into your Uniform Canvas-powered Next app. To do this we will need to add a new Contentful-powered parameter to your component type, and then tell your Next app how to fetch and parse the Contentful data to display it. Canvas provides a framework for enhancing data in your Canvas composition with data from other tools, like CMSes, DAMs, commerce, and search indexes. This enhancement is done on the app side, leaving full flexibility to the developer as to the best and most efficient way to get the data.

Step 1: Install the integration

First, let's connect Uniform to Contentful by installing the integration. You'll need a Contentful account to do this (a free one is fine).

  1. Go to Settings on your on https://uniform.app project, then click Integrations
  2. Find the Contentful integration and add it
  3. Click Log in to Contentful and enter your Contentful credentials
  4. Choose the Space and Environment that you want to bring content from into Canvas

Step 2: Add a Contentful parameter

Now that we're connected to Contentful, we can add a new parameter to our Canvas component type that links to a Contentful entry.

  1. Go to your Component Library settings
  2. Open your component type (there should be only one, if you've been following along)
  3. Click (+) next to Parameters
  4. Name your parameter something like 'Contentful Entry', choose Contentful Entry Selector as the type, pick Contentful content type(s) that can be linked, and as before choose a public ID to use as the name in code.

hello world component

Now that the parameter is added to the component type, we need to link the composition we have been rendering to an actual Contentful entry:

  1. Click Compositions, then open your composition from earlier

  2. You should see a Contentful parameter alongside the text parameter we created before hello world composition

  3. Choose a Contentful entry to link to and save & publish your composition

Composition publishing required

Make sure you publish the composition after the change. While it is possible to have Canvas SDK use draft compositions, this requires changes to the way you setup your Canvas client.

Step 4: Wire up your Next.js app

Now that you've linked to a Contentful entry from your composition. Now let's let your Next.js app know about this. Right now, we're doing nothing with the Contentful data - the Next app will look exactly the same as it did before, except it has a new parameter we're doing nothing with. To get the Contentful data, we need to add an enhancer to the Next app's data fetching so that Canvas knows what to do with Contentful parameters.

info

If you do not already have a Contentful delivery API token, create one in Contentful. Read about it more here.

  1. Install the Contentful-specific packages, which contain the Canvas Contentful enhancer and the Contentful JS SDK respectively.

      yarn add contentful @uniformdev/canvas-contentful 
  2. Open your pages/index.js file and import the Contentful enhancer, and add enhance to the existing canvas import:

    import {
    createContentfulEnhancer,
    ContentfulClientList,
    CANVAS_CONTENTFUL_PARAMETER_TYPES,
    } from '@uniformdev/canvas-contentful';
    import { enhance, CanvasClient, EnhancerBuilder } from '@uniformdev/canvas';
    import { createClient } from 'contentful';
  3. In getStaticProps, create the enhancer and configure it to connect to Contentful and enhance your composition data:

    // ...fetching the composition here

    // create the Contentful client
    const contentfulClient = createClient({
    // NOTE: for production code ensure you use environment variables to configure Contentful, not hard-coded values.
    space: 'your-contentful-space-id',
    // only needed if not the default 'master' environment
    environment: 'your-contentful-environment',
    accessToken: 'your-contentful-content-delivery-api-key-here',
    });

    // create a Contentful client list
    // NOTE: the ContentfulClientList allows you to use Canvas data that references multiple spaces / environments
    // by providing a Contentful client for each space / environment.
    const clientList = new ContentfulClientList({ client: contentfulClient });

    // apply the enhancers to the composition data, enhancing it with external data
    // in this case, the _value_ of the Contentful parameter you created is enhanced
    // from the entry ID it is linked to, to the Contentful entry REST API response
    // for that entry. You can create your own enhancers easily; they are a simple function.
    await enhance({
    composition,
    enhancers: new EnhancerBuilder()
    .parameterType(CANVAS_CONTENTFUL_PARAMETER_TYPES, createContentfulEnhancer({ client: clientList }))
    .parameterType(
    CANVAS_CONTENTFUL_MULTI_PARAMETER_TYPES,
    createContentfulMultiEnhancer({ clients: clientList })
    )
    .parameterType(
    CANVAS_CONTENTFUL_QUERY_PARAMETER_TYPES,
    createContentfulQueryEnhancer({ clients: clientList })
    ),
    context: {},
    });

    // ...returning the props

    The Next app should still be working, but we're not rendering any of the Contentful data yet. Let's do that!

    important

    If you are using Canvas Contentful enhancer version 12.0.0 or earlier, you will need to update to the latest version in order to use ContentfulClientList for Canvas data that references multiple spaces / environments.

  4. In the Home component implementation, get the Contentful parameter value and pick a field from it:

    export default function Home({ composition }) {
    return (
    <Composition data={composition}>
    {/* note: `contentfulEntry` is the "public id" value you provided for the Contentful Entry Selector parameter. */}
    {({ greeting, contentfulEntry }) => (
    <article>
    <h1>{greeting}</h1>
    {/* we assume your entry type has a field called 'title' on it */}
    <h1>{contentfulEntry.fields.title}</h1>
    </article>
    )}
    </Composition>
    );
    }
That's it!

Now you're rendering data from Contentful! The structure of the enhancer result is that of the Contentful REST entry API.

Other things you can do

Use multiple Contentful spaces

You can connect to multiple Contentful spaces for content data by creating a Contentful client for each space. To do this, you will need:

  1. Register the space/environment in the dashboard and give it a source ID. This source ID is an arbitrary key that you will use to refer to the environment in Canvas' web app as well as your enhancement code.

  2. Register the enhancer for the source ID with your enhancer infrastructure

    const defaultClient = createClient({
    space: 'your-contentful-space-id',
    accessToken: 'your-contentful-content-delivery-api-key-here',
    });

    const namedSourceClient = createClient({
    space: 'your-named-source-space-id',
    accessToken: 'your-named-source-delivery-api-key-here',
    });

    const clientList = new ContentfulClientList({ client: defaultClient });

    clientList.addClient({ source: 'namedSource', client: namedSourceClient });

Enable enhancement for Multi Entry and Query parameters

Ensure you have registered the following enhancers:

new EnhancerBuilder()
.parameterType(
CANVAS_CONTENTFUL_MULTI_PARAMETER_TYPES,
createContentfulMultiEnhancer({ clients: clientList })
)
.parameterType(
CANVAS_CONTENTFUL_QUERY_PARAMETER_TYPES,
createContentfulQueryEnhancer({ clients: clientList })
)

Overriding default sort order for Query parameter

Contentful Query enhancer uses default Contentful sort order when sorting is not configured.

Default sort order can be overriden with enhancer options:

new EnhancerBuilder()
.parameterType(
CANVAS_CONTENTFUL_QUERY_PARAMETER_TYPES,
createContentfulQueryEnhancer({
clients: clientList,
createQuery: ({ defaultQuery, parameter}) => {
const newDefaultOrder = 'sys.createdAt';
const hasOrder = !!parameter.value?.sortBy;

return {
...defaultQuery,
...(!hasOrder ? { order: newDefaultOrder } : undefined),
};
},
})
)

Using Contentful clients in preview mode

In order to fetch draft content from Contentful during preview mode, the following changes are needed within your Next.js app. See highlighted changes below:

pages/index.js
import {
CANVAS_DRAFT_STATE,
CANVAS_PUBLISHED_STATE,
enhance,
EnhancerBuilder
} from '@uniformdev/canvas';

import {
createContentfulEnhancer,
ContentfulClientList,
CANVAS_CONTENTFUL_PARAMETER_TYPES,
} from "@uniformdev/canvas-contentful";

import createClient from "contentful";

// read the value of preview from the Next.js context
export const getStaticProps = async ({ preview }) => {
// keep the canvas client as is
const client = new CanvasClient({
apiKey: process.env.UNIFORM_API_KEY,
projectId: process.env.UNIFORM_PROJECT_ID,
});

const { composition } = await client.getCompositionBySlug({
slug: '/',
// add state dependent on the Next.js preview value
// this enables fetching draft compositions from Canvas API if preview=true
state: preview ? CANVAS_DRAFT_STATE : CANVAS_PUBLISHED_STATE,
});

// as an example here we are configuring the Contentful enhancer to fetch preview entries from Contentful,
// as well as preview layout from Canvas. This code creates a Contentful client, as outlined in the Contentful data tutorial.
const contentfulClient = createClient({
space: 'space-id',
accessToken: 'delivery-token',
});

// for previewing Contentful data, we need a second client that tells it how to get preview entries
const contentfulPreviewClient = createClient({
space: 'space-id',
accessToken: 'preview-token',
host: 'preview.contentful.com',
});

// add the previewClient to the ContentfulClientList
// NOTE: the ContentfulClientList allows you to use Canvas data that references multiple spaces / environments
// by providing a Contentful client for each space / environment.
const clientList = new ContentfulClientList([
{
spaceId: 'space-id',
environmentId: "environment-name",
client: contentfulClient,
previewClient: contentfulPreviewClient,
},
]);

// create the Contentful enhancer with client list
const contentfulEnhancer = createContentfulEnhancer({ client: clientList });

await enhance({
composition,
enhancers: new EnhancerBuilder().parameterType(CANVAS_CONTENTFUL_PARAMETER_TYPES, contentfulEnhancer),
// make sure to set the preview context here
context: { preview },
});

return {
props: {
composition,
},
};
};