Using Experiences with Next.js
Table of contents
Using pages router
Server-side rendering using pages router
When using pages router in Next.js, 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 Next.js 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.
Below is an example page using Next.js SSR in pages router:
import { createClient } from 'contentful';
import {
ExperienceRoot,
defineComponents,
detachExperienceStyles,
fetchBySlug,
} from '@contentful/experiences-sdk-react';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import Head from 'next/head';
defineComponents([
// Add your custom components here
// example:
// {
// component: Button,
// definition: {
// id: 'custom-button',
// name: 'Button',
// category: 'Custom Components',
// variables: {
// text: {
// displayName: 'Text',
// type: 'Text',
// defaultValue: 'Click me'
// },
// },
// },
// },
]);
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,
stylesheet,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<>
{stylesheet && (
<Head>
<style data-ssg>{stylesheet}</style>
</Head>
)}
<main style={{ width: '100%' }}>
<ExperienceRoot experience={experienceJSON} locale={localeCode} />
</main>
</>
);
}
export const getServerSideProps = async ({
params,
locale = 'en-US',
}: GetServerSidePropsContext<{ slug: string }>) => {
const { slug = 'home-page' } = params || {};
const experience = await fetchBySlug({
client,
slug,
experienceTypeId,
localeCode: locale,
});
// extract the styles from the experience
const stylesheet = experience ? detachExperienceStyles(experience) : null;
// experience currently needs to be stringified manually to be passed to the component
const experienceJSON = experience ? JSON.stringify(experience) : null;
return {
props: {
experienceJSON: experienceJSON,
stylesheet,
},
};
};
export default MyPage;
To see a more detailed info on how to use the Experiences with Next.js Pages Router, check out the Next.js Pages Router.
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,
defineComponents,
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';
defineComponents([
// Add your custom components here
// example:
// {
// component: Button,
// definition: {
// id: 'custom-button',
// name: 'Button',
// category: 'Custom Components',
// variables: {
// text: {
// displayName: 'Text',
// type: 'Text',
// defaultValue: 'Click me'
// },
// },
// },
// },
]);
const client = createClient({
space,
environment,
accessToken,
});
const Experience: 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 Experience;
Using app router
Server-side rendering using app router
When using app router in Next.js, you can render your experience on the server.
Important:
- Use the vanilla JS fetchers (
fetchBySlug
orfetchById
) inside the server component instead of the React hooksuseFetchBySlug
oruseFetchById
. - The
experience
object returned by the fetchers is not serializable by Next.js 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.
Below is an example page using Next.js SSR in app router:
src/app/[locale]/[slug]/pages.tsx
import Experience from '@/components/Experience';
import { createClient } from 'contentful';
import {
detachExperienceStyles,
fetchBySlug,
} 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 client = createClient({
space,
environment,
accessToken,
});
async function AppPage({
params,
}: {
params: { slug: string; locale: string };
}) {
const { locale = 'en-US', slug = 'home-page' } = params || {};
const experience = await fetchBySlug({
client,
slug,
experienceTypeId,
localeCode: locale,
});
// extract the styles from the experience
const stylesheet = detachExperienceStyles(experience!);
// experience currently needs to be stringified manually to be passed to the component
const experienceJSON = experience ? JSON.stringify(experience) : null;
return (
<main style={{ width: '100%' }}>
{stylesheet && <style>{stylesheet}</style>}
<Experience experienceJSON={experienceJSON} locale={locale} />
</main>
);
}
export default AppPage;
src/components/Experience.tsx
'use client';
import {
ExperienceRoot,
defineComponents,
} from '@contentful/experiences-sdk-react';
import React from 'react';
// it is important that this call is done in a client component
defineComponents([
// Add your custom components here
// example:
// {
// component: Button,
// definition: {
// id: 'custom-button',
// name: 'Button',
// category: 'Custom Components',
// variables: {
// text: {
// displayName: 'Text',
// type: 'Text',
// defaultValue: 'Click me'
// },
// },
// },
// },
]);
interface ExperienceProps {
experienceJSON: string | null;
locale: string;
}
const Experience: React.FC<ExperienceProps> = ({ experienceJSON, locale }) => {
return <ExperienceRoot experience={experienceJSON} locale={locale} />;
};
export default Experience;
Note: The
ExperienceRoot
component is a client component. While it can be initially rendered on the server, on the client side it will hydrate and continue rendering on the client.
To see a more detailed info on how to use the Experiences with Next.js App Router, check out the Next.js App Router example.
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;