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.
Supported browsers
The Experiences SDK supports all modern desktop browsers.
- Google Chrome
- Mozilla Firefox
- Opera
- Apple Safari
- Microsoft Edge
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
- Register your component
- Create a Contentful client
- Fetch and display the experience
- Register design tokens
- Display the experience
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,
detachExperienceStyles
} 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
}
}
const experienceStyles = detachExperienceStyles(experience) ?? '';
return {
props: {
experienceEntryJSON: JSON.stringify(experienceEntry),
locale: currentLocale,
experienceStyles
},
};
} catch (e) {
// handle error
}
};
function ExperienceBuilderPage({
experienceEntryJSON,
locale,
experienceStyles
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
const experience = createExperience(experienceEntryJSON);
return (
<>
// in this example we simply put the styles of our experience in a style tag
<style>{experienceStyles}</style>
<main style={{ width: '100%' }}>
<ExperienceRoot experience={experience} locale={locale} />
</main>
</>
);
}
export default ExperienceBuilderPage;
// gatsby-node.js
import path from 'path';
import { fetchById, detachExperienceStyles } 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
});
let experienceStyles = '';
if (experienceEntry) {
experienceStyles = detachExperienceStyles(experienceEntry) ?? '';
}
createPage({
path: `/experience/${slug}`,
component: path.resolve('src/templates/experiencePage.js'),
context: {
title,
experienceEntryJSON: JSON.stringify(experienceEntry),
locale: localeCode,
experienceStyles
}
});
}
};
// 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 (
<>
// in this example we simply put the styles of our experience in a style tag, but a better way to do it would
// be to inject the styles using Helmet or a similar package
<style>{pageContext.experienceStyles}</style>
<ExperienceRoot
experience={experience}
// The locale that will appear on the website first
locale={pageContext.locale}
/>
</>
);
}
import React, { useEffect, useRef } from 'react'
import {
useFetchBySlug,
ExperienceRoot,
detachExperienceStyles
} 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;
}
// optionally detach styles to have them on the page on initial render
const experienceStyles = detachExperienceStyles(experience);
return (
<>
// in this example we simply put the styles of our experience in style tag, but a better way to do it would
// be to inject the styles using Helmet or a similar package
<style>{experienceStyles}</style>
<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.