Was this page helpful?

Set up Experiences SDK

Table of contents

Prerequisites

  • Before you start setting up the Experiences SDK, ensure to accomplish setup in the Contentful web app: enable in a space(s) and configure Experiences. To learn more about the Experiences setup steps, refer to What are Experiences?.
  • The SDK works with React applications (React v18+).
  • The SDK requires the Contentful SDK v10.6.0 or newer, which is a peer dependency.
  • This guide provides basic setup steps for our SDK. Depending on your setup, you may need to do some extra steps to make it fit properly.
  • Your web application must support being rendered in an iframe. The iframe is used for the editing experience but not when displaying the experience on your site. If you have a CSP policy blocking this, you must include https://app.contentful.com into the list of allowed domains.

Install the SDK

For npm:

npm install @contentful/experiences-sdk-react contentful -S

For yarn:

yarn add @contentful/experiences-sdk-react contentful

Usage

To use Experiences, you will need to follow these steps:

Create your component

In this example, we will create a "Button" component. The component will be a simple React component which will render a button with some text.

We use TypeScript in all our examples, but you can also use JavaScript.

import React from 'react';

interface ButtonComponentProps {
  text: string;
}

export const Button: React.FC<ButtonComponentProps> = ({ text }) => {
  return <button>{ text }</button>;
};

Register your component

Next, your component needs to be registered with the Experiences SDK. This is done with the help of the defineComponents function which can be imported from the @contentful/experiences-sdk-react package. We suggest registering your components once at the startup of your application; for example in the index.tsx (or like) file.

// src/registeredComponents.ts

import { defineComponents } from '@contentful/experiences-sdk-react';
import { Button } from './components/Button';

defineComponents([
  {
    component: Button,
    definition: {
      id: 'button',
      name: 'Button',
      category: 'Custom Components',
      variables: {
        text: {
          displayName: 'Text',
          type: 'Text',
          defaultValue: 'Click me!',
        },
      },
    },
  },
]);

Above, we register the Button component and provide a component definition that Experiences will use to help you configure the component in the editor. In the component definition, we specify the id and name of the component, the category (where it will display in the components area), and the variables it supports. We configure the text variable to be of type Text and provide a default value. This variable will be editable in the Experiences and its value will be passed down to the Button component as a prop.

To learn more about the component definitions and what features they provide, please refer to the Component definition schema.

Create a Contentful client

To fetch the experience, you will need to create a Contentful client which requires a few variables to initialize. We'll pull these variables from environment variables here in this example. Refer to your framework for more info on how to use environment variables from within it.

For more info on using the Contentful SDK and how get access to the variables needed, please refer to the Contentful SDK documentation.

// src/contentfulClient.ts

import { createClient } from 'contentful';

export const client = createClient({
  // your space id
  space: process.env.CTFL_SPACE_ID,
  // your environment id
  environment: process.env.CTFL_ENV_ID,
  // Supported values: 'preview.contentful.com' or 'cdn.contentful.com',
  host: process.env.CTFL_API_HOST,
  // needs to be access token if host = 'cdn.contentful.com' and preview token if 'preview.contentful.com'
  accessToken: process.env.CTFL_TOKEN
});

We recommend creating the client only once in your application and reusing it across the different components that use Experiences.

Register design tokens

Design tokens allow you to easily manage and use consistent design values throughout Experiences.

To register design tokens, import the defineDesignTokens function and provide your design tokens definition.

Below you can see an example of a design tokens definition:

// src/registeredTokens.ts

import { defineDesignTokens } from '@contentful/experiences-sdk-react';

// register design tokens
defineDesignTokens({
  spacing: { XS: '4px', S: '16px', M: '32px', L: '64px', XL: '128px' },
  sizing: { XS: '16px', S: '100px', M: '300px', L: '600px', XL: '1024px' },
  color: {
    Slate: '#94a3b8',
    Azure: 'azure',
    Orange: '#fdba74',
    Blue: '#0000ff',
  },
  border: {
    Azure: { width: '1px', style: 'solid', color: 'azure' },
    Hero: { width: '2px', style: 'dashed', color: '#ffaabb' },
    Card: { width: '1px', style: 'solid', color: '#ffccbb' },
    Carousel: { width: '2px', style: 'dotted', color: 'rgba(30, 25, 25, 0.75)' },
  },
  fontSize: { XS: '12px', SM: '14px', MD: '16px', LG: '24px', XL: '32px' },
  lineHeight: { XS: '1', SM: '1.25', MD: '1.5', LG: '200%' },
  letterSpacing: {
    None: '0',
    XS: '0.05em',
    SM: '0.1em',
    MD: '0.15em',
    LG: '0.2em',
  },
  textColor: { Dark: '#1a1a1a', Light: '#efefef', Slate: '#94a3b8' },
});

To learn more about the design tokens, please refer to the Design tokens definition schema.

Display the experience

Experiences SDK uses Contentful client to fetch the experience. The SDK package provides helpers, such as the useFetchBySlug hook to fetch the experience by its slug (configured in the settings tab for the experience) or the useFetchById hook to fetch the experience by its ID.

The hook returns the experience object, which can be rendered with the help of ExperienceRoot component.

Import the ExperienceRoot component, provided by the SDK and place it somewhere within the page that you plan to build.

// src/pages/[locale]/[slug].tsx

import React from 'react';
import { createExperience, fetchBySlug, ExperienceRoot } from '@contentful/experiences-sdk-react';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';

import { client } from '../../contentfulClient.ts';
// trigger component registration
import '../../registeredComponents.ts';
// trigger design tokens registration
import '../../registeredTokens.ts';

// example experience content type ID
const experienceTypeId = process.env.CTFL_EXPERIENCE_CONTENT_TYPE_ID;

export const getServerSideProps = async ({
  params,
  locale,
}: GetServerSidePropsContext) => {
  if (!params?.slug) {
     return {
         notFound: true
     }
  }

  const currentLocale = (locale || params?.locale) as string;
  const slug = params.slug; // slug in this example, but you can choose your own way to implement routing

  try {
     const experienceEntry = await fetchBySlug({
       client,
       experienceTypeId,
       localeCode: currentLocale,
       slug,
     });

     if (!experienceEntry) {
        return {
            notFound: true
        }
     }

     return {
       props: {
         experienceEntryJSON: JSON.stringify(experienceEntry),
         locale: currentLocale,
       },
     };
  } catch (e) {
    // handle error
  }
};

function ExperienceBuilderPage({
  experienceEntryJSON,
  locale,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {

  const experience = createExperience(experienceEntryJSON);

  return (
    <main style={{ width: '100%' }}>
      <ExperienceRoot experience={experience} locale={locale} />
    </main>
  );
}

export default ExperienceBuilderPage;
// gatsby-node.js
import path from 'path';
import { fetchById } from '@contentful/experiences-sdk-react';

import { client } from '../../contentfulClient.ts'

exports.createPages = async ({ graphql, actions, reporter }) => {
   const { createPage } = actions;

   // assuming that your experience type id is "experience"
   const response = await graphql(`
      allContentfulExperience {
         nodes {
            contentful_id
            slug
            title
            node_locale
            sys {
               contentType {
                  sys {
                     id
                  }
               }
            }
         }
      }
   `);

   if (response.errors) {
      // handle errors
   }

   const { nodes } = response.data.allContentfulExperience;

   for (const node of nodes) {
      const { slug, title, node_locale: localeCode, contentful_id, sys } = node;

      const experienceEntry = await fetchById({
         client,
         experienceTypeId: sys.contentType.sys.id
         locale: localeCode,
         id: contentful_id
      });

      createPage({
         path: `/experience/${slug}`,
         component: path.resolve('src/templates/experiencePage.js'),
         context: {
            title,
            experienceEntryJSON: JSON.stringify(experienceEntry),
            locale: localeCode
         }
      });
   }
};
// src/templates/experiencePage.js
import React from 'react'
import {
  createExperience,
  ExperienceRoot,
  defineComponents
} from '@contentful/experiences-sdk-react'

// trigger component registration
import '../registeredComponents.ts';
// trigger design tokens registration
import '../registeredTokens.ts';

const ExperiencePage = ({ pageContext }) => {
  const experience  = createExperience(pageContext.experienceEntryJSON);

  return (
    <ExperienceRoot
      experience={experience}
      // The locale that will appear on the website first
      locale={pageContext.locale}
    />
  );
}
import React, { useEffect, useRef } from 'react'
import {
  useFetchBySlug,
  ExperienceRoot
} from '@contentful/experiences-sdk-react'

import { client } from '../contentfulClient.ts';
// trigger component registration
import '../registeredComponents.ts';
// trigger design tokens registration
import '../registeredTokens.ts';

const App = () => {
  // replace with your router;
  const router = {};

  const locale = router.query.locale;
  const slug = router.query.slug;

  const { isLoading, experience } = useFetchBySlug({
    client,
    // id of the experience type (content type)
    experienceTypeId: process.env.REACT_APP_CTFL_EXPERIENCE_CONTENT_TYPE_ID,
    localeCode: locale,
    slug,
  })


  if (!experience || isLoading) {
    return null;
  }

  return (
    <ExperienceRoot
      experience={experience}
      // The locale that appears on the website first
      // You could nicely tie it to the useParam() from router or intenral state or locale manager
      // this value - en-US here is provided as an example reference
      locale={locale}
    />
  )
}

export default App

ExperienceRoot marks the place which users will be able to edit using the Experiences. With ExperienceRoot, you have the flexibility to enable users to build entire pages or only allow them to modify a specific portion of the web page.

To learn more about rendering Experiences with NextJS, refer to Using Experiences with NextJS.