Skip to navigation

Fetch experiences

Experiences SDK is currently available in alpha. APIs are unstable and subject to change.

fetchExperience fetches an experience from the Experience Delivery API and resolves it in one call. Every framework adapter exports the same function with the same options. The resolved experience is plain, serializable data, so you can fetch on the server and pass it to the client.

Fetch by ID

fetchExperience takes three arguments: what to fetch, how to fetch it, and how to resolve it.

import { fetchExperience } from '@contentful/experiences-react';
const experience = await fetchExperience(
{ spaceId: '<space-id>', environmentId: 'master', experienceId, locale: 'en-US' },
{ accessToken: process.env.CDA_TOKEN! },
{ config: experienceConfig, metadata: { slug: experienceId }, debug: false }
);
ArgumentFieldsDescription
Experience optionsspaceId, environmentId, experienceId, locale?, parameters?, extensions?Which experience to fetch.
Client optionsaccessToken, previewToken?, preview?, host?, or { client }How to fetch: credentials and endpoint, or your own client.
Resolve optionsconfig, metadata?, debug?How to resolve: your configuration, application data for your components, and debug mode.

metadata and debug are stored on the resolved experience, so the renderer reads them without being passed them again. metadata must be plain serializable data, because it travels with the experience.

Call fetchExperience on the server, so your access token stays out of the browser:

  • Next.js: In a server component or route handler.
  • SvelteKit: In a +page.server.ts load function, returning the experience as page data.
  • Angular: In your Express server, passing the experience to AngularNodeAppEngine.handle() as request context. Route resolvers also run in the browser, so they would ship your token to the client.

Handle errors

fetchExperience throws NotFoundError when the experience doesn’t exist. Route it to your framework’s not-found handling:

import { fetchExperience, NotFoundError } from '@contentful/experiences-react';
import { notFound } from 'next/navigation';
try {
const experience = await fetchExperience(experienceOptions, clientOptions, resolveOptions);
// render the experience
} catch (error) {
if (error instanceof NotFoundError) notFound();
throw error;
}

Other delivery failures throw ExperienceFetchError, with the original error, including status and body, as cause. An experience with no published nodes isn’t a not-found error: it resolves to an experience with an empty nodes array.

Fetch draft content

To fetch unpublished changes from the Content Preview API, configure both tokens and set preview: true on the request:

const experience = await fetchExperience(
{ spaceId, environmentId, experienceId },
{ accessToken: process.env.CDA_TOKEN!, previewToken: process.env.CPA_TOKEN, preview: true },
{ config: experienceConfig }
);

preview: true without a previewToken throws an error. To show unsaved canvas edits as they happen, see Set up live preview.

Pass runtime parameters

An experience can declare parameters, which its bindings read as $parameters/<id>. Pass values with parameters to resolve the experience with them:

import { fetchExperience, type ExperienceParameters } from '@contentful/experiences-react';
const parameters = {
featuredPosts: { limit: 6, order: '-fields.title' },
} satisfies ExperienceParameters;
const experience = await fetchExperience(
{ spaceId, environmentId, experienceId, locale: 'en-US', parameters },
{ accessToken },
{ config: experienceConfig }
);
  • Values: Strings, numbers, or one-level records of strings and numbers.
  • Validation: The Experience Delivery API validates names, required values, fallbacks, and values. The SDK doesn’t need the experience definition before fetching.
  • Ordering: A collection order is one field path, fields.<apiName> or sys.<id>, optionally prefixed with - for descending order, such as -fields.title or sys.createdAt.

To change the values, fetch the experience again and pass the new experience to your renderer. Each fetch runs resolveData again. In a browser fetch, use a token that’s safe to expose, or a trusted proxy.

Preview fetches accept the same parameters. The Content Preview API can report missing or invalid values in the experience’s deliveryDiagnostics instead of failing the request. See Errors and debugging.

The types ExperienceParameters, ExperienceParameterValue, ExperienceParameterRecord, and ExperienceParameterOrder are exported by every adapter and by the Node and Web SDKs.

Request the content source map

Add extensions: { sourceMap: {} } to the experience options to fetch the content source map with the experience:

const experience = await fetchExperience(
{ spaceId, environmentId, experienceId, extensions: { sourceMap: {} } },
{ accessToken },
{ config: experienceConfig }
);
experience.sourceMap; // ExperienceSourceMap | undefined

With a source map, the SDK adds attribution to the experience:

  • experience.attribution.scopes: The experience and the persisted experience fragments in it, each with entityId, entityKind, and the selected variantId.
  • node.attribution: The scopes a node belongs to (scopes, outer to inner), the scopes it’s a top-level node of (roots), and the entries its content is bound to (entryIds).

Only nodes with an ID get attribution. Read it in a component through the node payload’s attribution field.

The source map is large, so it’s opt-in. Requesting it also switches the request from GET to POST, which isn’t cacheable by a CDN the way GET is.

Fetch by destination

If your space uses destinations, fetch the experience routed to a path or a destination node instead of by ID:

const result = await fetchExperience(
{ spaceId, destinationId, path: '/products/shoes' },
{ accessToken },
{ config: experienceConfig }
);
if ('redirect' in result) {
// Navigate to result.redirect.path, or fetch again with that path.
} else {
// result is the resolved experience.
}

Use nodeId instead of path to fetch by destination node. Destination fetches don’t take environmentId or locale, and they’re delivery only: preview: true throws.

To list every path in a destination, for example to build a sitemap or pre-render routes, use fetchDestinationSitemap:

import { fetchDestinationSitemap } from '@contentful/experiences-react';
const { items, pages } = await fetchDestinationSitemap(
{ spaceId, destinationId },
{ accessToken },
{ limit: 100 }
);
// items: [{ path, experienceId, type }]. Pass pages.next as pageNext to get the next page.

Use your own client

To manage the delivery client yourself, for example to add custom caching or middleware, create one with createClient and pass it as { client }:

import { createClient, fetchExperience } from '@contentful/experiences-react';
const client = createClient({ accessToken: process.env.CDA_TOKEN!, host: 'https://proxy.example.com' });
const experience = await fetchExperience({ spaceId, environmentId, experienceId }, { client }, { config: experienceConfig });

A client is bound to one token, so it can’t switch between delivery and preview per request. Use the inline { accessToken, previewToken, preview } options for that.

If you call the client directly, resolve its response with resolveExperience. toExperiencePayload narrows the response type, and readSourceMap extracts the source map, if present:

import { createClient, readSourceMap, resolveExperience, toExperiencePayload } from '@contentful/experiences-react';
const response = await client.experience.get(spaceId, environmentId, experienceId, { locale: 'en-US' });
const experience = await resolveExperience(toExperiencePayload(response), experienceConfig, {
sourceMap: readSourceMap(response),
});

resolveExperience(payload, config, options?) is the resolve half of fetchExperience. It takes the same metadata and debug options, plus sourceMap.

Fetch without a framework adapter

To fetch outside a framework adapter, use the Node or Web SDK. Both return the same resolved experience:

import { ContentfulExperiences } from '@contentful/experiences-node';
const experiences = new ContentfulExperiences({
spaceId,
environmentId: 'master',
delivery: { accessToken: process.env.CDA_TOKEN! },
locale: 'en-US',
});
// For each request:
const experience = await experiences
.forRequest({ locale })
.fetchExperience({ experienceId }, { config: experienceConfig });

The Node and Web SDKs also send events and personalize experiences. See their READMEs.