Using Experiences with NextJS
Table of contents
Using pages router
Server-side rendering using pages router
When using pages router in NextJS, you can render your experience on the server.
Important:
- Use the vanilla JS fetchers (
fetchBySlug
orfetchById
) inside of the server side methods (getServerSideProps
orgetStaticProps
) instead of the React hooksuseFetchBySlug
oruseFetchById
. - The
experience
object returned by the fetchers is not serializable by NextJS directly due to their strict JSON serialization techniques. Therefore, we need to serialize theexperience
ourselves and pass the JSON string as a prop to the component. - In the component, we need to recreate the
experience
object from the JSON string by passing it to thecreateExperience
function.
Below is an example page using NextJS SSR in the pages router:
import { createClient } from 'contentful';
import {
fetchBySlug,
ExperienceRoot,
createExperience,
} from '@contentful/experiences-sdk-react';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
const accessToken = process.env.NEXT_PUBLIC_CTFL_ACCESS_TOKEN!;
const space = process.env.NEXT_PUBLIC_CTFL_SPACE!;
const environment = process.env.NEXT_PUBLIC_CTFL_ENVIRONMENT!;
const experienceTypeId = process.env.NEXT_PUBLIC_CTFL_EXPERIENCE_TYPE!;
const localeCode = 'en-US';
const client = createClient({
space,
environment,
accessToken,
});
function MyPage({
experienceJSON,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
//Recreate the experience object from the serialized JSON
const experience = createExperience(experienceJSON);
return (
<main style={{ width: '100%' }}>
<ExperienceRoot experience={experience} locale={'en-US'} />
</main>
);
}
export const getServerSideProps = async ({}: GetServerSidePropsContext) => {
const experience = await fetchBySlug({
client,
slug: 'homePage', //could be fetched from the context
experienceTypeId,
localeCode,
});
//Serialize the experience manually
const experienceJSON = JSON.stringify(experience);
return {
props: {
experienceJSON: experienceJSON,
},
};
};
export default MyPage;
Client-side rendering using pages router
When using client side rendering, fetching and displaying an experience is similar to how it is done in a normal single page application.
import React from 'react';
import { createClient } from 'contentful';
import {
ExperienceRoot,
useFetchBySlug,
} from '@contentful/experiences-sdk-react';
const accessToken = process.env.NEXT_PUBLIC_CTFL_ACCESS_TOKEN!;
const space = process.env.NEXT_PUBLIC_CTFL_SPACE!;
const environment = process.env.NEXT_PUBLIC_CTFL_ENVIRONMENT!;
const experienceTypeId = process.env.NEXT_PUBLIC_CTFL_EXPERIENCE_TYPE!;
const localeCode = 'en-US';
const client = createClient({
space,
environment,
accessToken,
});
const MyComponent: React.FC = (props) => {
const { experience, isLoading, error } = useFetchBySlug({
client,
slug: 'homePage', //Could be fetched from the url,
experienceTypeId: experienceTypeId,
localeCode,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <ExperienceRoot experience={experience} locale={localeCode} />;
};
export default MyComponent;
Using app router
Server-side rendering using app router
Using the app router in Next.js, data is fetched in a server component for efficient and secure handling and passed to a client component to render the experience. This way we ensure the benefits of SSR while enabling client-side interactivity.
Important:
- Use the vanilla JS fetchers (
fetchBySlug
orfetchById
) inside the server component instead of the React hooksuseFetchBySlug
oruseFetchById
. fetchBySlug
orfetchById
must be imported from@contentful/experiences-core
and not from@contentful/experiences-sdk-react
to avoid issues with the server-side rendering.
Server component
Fetch an experience in a server component.
app/[lang]/[slug]/pages.tsx
import { cache } from 'react';
import { createClient } from 'contentful';
import { ExperienceAppRouter } from '@/app/ExperienceAppRouter';
// Note: import from experiences-core
import { fetchBySlug } from '@contentful/experiences-core';
const accessToken = process.env.NEXT_PUBLIC_CTFL_ACCESS_TOKEN!;
const space = process.env.NEXT_PUBLIC_CTFL_SPACE!;
const environment = process.env.NEXT_PUBLIC_CTFL_ENVIRONMENT!;
// example experience type ID
const experienceTypeId = process.env.NEXT_PUBLIC_CTFL_EXPERIENCE_TYPE!;
const client = createClient({
space,
environment,
accessToken,
});
export const fetchData = cache(
async ({ slug, locale }: { slug: string; locale: string }) => {
const currentLocale = locale;
const experience = await fetchBySlug({
client,
slug: slug as string,
experienceTypeId,
localeCode: currentLocale,
});
if (!experience) {
throw new Error('Experience not found');
}
const experienceString = JSON.stringify(experience);
return {
experienceString,
};
}
);
async function AppPage({ params }: { params: { slug: string; lang: string } }) {
const { experienceString } = await fetchData({
slug: params.slug,
locale: params.lang,
});
return (
<ExperienceAppRouter
experienceString={experienceString}
locale={params.lang}
/>
);
}
export default AppPage;
Client component
Render your experience in a client component.
app/ExperienceAppRouter.tsx
'use client';
import React, { useMemo } from 'react';
import {
ExperienceRoot,
createExperience,
detachExperienceStyles,
} from '@contentful/experiences-sdk-react';
// Here you can register your components with Experiences SDK
export const ExperienceAppRouter = ({
experienceString,
locale,
}: {
experienceString: string;
locale: string;
}) => {
const experience = useMemo(() => {
return createExperience(experienceString);
}, [experienceString]);
const styles = useMemo(() => {
return detachExperienceStyles(experience);
}, [experience]);
return (
<main style={{ width: '100%' }}>
<style>{styles}</style>
<ExperienceRoot experience={experience} locale={locale} />
</main>
);
};
Client-side rendering using app router
When using client-side rendering, fetching and displaying an experience is similar to how it is done in a normal single page application, except you add the 'use client' directive at the top of the component:
'use client';
import React from 'react';
import { createClient } from 'contentful';
import {
ExperienceRoot,
useFetchBySlug,
} from '@contentful/experiences-sdk-react';
const accessToken = process.env.NEXT_PUBLIC_CTFL_ACCESS_TOKEN!;
const space = process.env.NEXT_PUBLIC_CTFL_SPACE!;
const environment = process.env.NEXT_PUBLIC_CTFL_ENVIRONMENT!;
const experienceTypeId = process.env.NEXT_PUBLIC_CTFL_EXPERIENCE_TYPE!;
const localeCode = 'en-US';
const client = createClient({
space,
environment,
accessToken,
});
const MyComponent: React.FC = (props) => {
const { experience, isLoading, error } = useFetchBySlug({
client,
slug: 'homePage', //Could be fetched from the url,
experienceTypeId: experienceTypeId,
localeCode,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <ExperienceRoot experience={experience} locale={localeCode} />;
};
export default MyComponent;