npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@contentful/experiences-react

v0.11.1

Published

React adapter for Contentful Experiences — Server and Client renderers over a PortableRenderPlan

Readme

@contentful/experiences-react

⚠️ Alpha. Published to npm. APIs are unstable and will change.

The React adapter for the Contentful Experiences SDK. You bring your own React components; it renders Experience payloads from the Experience Delivery API (XDA) with them.

npm install @contentful/experiences-react

This is the only rendering SDK package you install. It re-exports everything you need from @contentful/experiences-sdk-core, @contentful/experiences-design, and @contentful/experiences-client. The optional @contentful/experiences-live-preview package is also customer-facing and provides the framework-neutral live-preview client.


Public API

Authoring

defineComponent<Props>(config); // Type-narrowing identity for component-type configs
defineExperienceTemplate<Props>(config); // Same shape, for coded Experience Template configs

Fetching

fetchExperience(experienceOptions, clientOptions, resolveOptions)  // Async; fetches from XDA and resolves in one call
fetchPreviewSession(previewSessionOptions, clientOptions, resolveOptions) // Async; fetches and resolves a Preview Session Experience
createClient(options)                       // Functional constructor matching the SDK's option shape
ContentfulViewDeliveryClient                // Re-exported delivery client for advanced use cases
NotFoundError                               // Thrown when the Experience ID doesn't exist
PreviewSessionFetchError                   // Thrown when fetching a Preview Session fails
LivePreviewConnectionError                 // Reported when the live-preview connection fails
ContentfulViewDelivery                      // Full error namespace from the delivery client
type ExperienceOptions, PreviewSessionExperienceOptions, PreviewSessionClientOptions,
  PreviewSessionResolveOptions, ClientOptions, ResolveOptions, CreateClientOptions

Use fetchPreviewSession for the initial render when the Contentful app provides a preview_session_id. It fetches the current Preview Session snapshot and resolves it into a PortableRenderPlan. Use useLivePreview to receive subsequent WebSocket updates:

const plan = await fetchPreviewSession(
  { spaceId, environmentId, sessionId },
  { previewToken },
  { config: experienceConfig }
);

If the Preview Session references resources from other spaces, pass the same encoded resourceResolution value in the previewSessionOptions passed to fetchPreviewSession and useLivePreview.

Resolver

resolveExperience(payload, config, opts?)   // Async; walks payload, runs resolveData, returns a PortableRenderPlan

Live preview

Use useLivePreview when the app should subscribe to Preview Session updates, resolve each payload, and render the resulting plan from data:

const livePreview = useLivePreview({
  previewSessionOptions,
  initialPayload,
  initialPlan,
  resolveOptions: { config: experienceConfig },
});

<ClientExperienceRenderer experience={livePreview.data} config={experienceConfig} />;

For separate access to the raw payload and rendered plan, use useLivePreviewExperience and useExperiencePlan:

const livePreview = useLivePreviewExperience({
  previewSessionOptions: {
    spaceId,
    environmentId,
    previewToken,
    sessionId,
  },
  initialPayload,
});

const plan = useExperiencePlan({
  payload: livePreview.data,
  initialPlan,
  resolveOptions: { config: experienceConfig },
});

<ClientExperienceRenderer experience={plan.data} config={experienceConfig} />;

useLivePreviewExperience returns the latest raw Experience payload. initialPayload seeds the first value. useExperiencePlan turns that payload into the PortableRenderPlan consumed by the renderer and keeps the current rendered experience while an update is being resolved.

The live-preview results also expose error when the Preview Session connection fails. The last valid data remains available.

Renderers

ServerExperienceRenderer; // RSC-friendly, active viewport seeded from initialViewportId
ClientExperienceRenderer; // 'use client', subscribes to window.matchMedia, alias: ExperienceRenderer
MissingComponent; // Default fallback for unregistered component types
useActiveViewport; // Hook used inside ClientExperienceRenderer (you'll rarely need it directly)

Styling + runtime context (hooks)

useDesignValues<T>(); // Escape hatch: the same resolved design record that auto-fills props
toCss(design, options?); // Turns a design record into CSSProperties, keeping only real CSS keys
useExperience(); // RenderContext: debug, metadata, viewports, activeViewport
useContentfulComponent(); // Raw payload for the enclosing node (or null)
useContentfulExperienceTemplate(); // Same, for an enclosing coded Experience Template node
type ToCssOptions;

Resolved design values (viewport-cascaded + token-resolved server-side) are auto-filled onto your component's props by key, alongside content. Styling from those props is the one recommended path. useDesignValues() exposes the same record as an escape hatch. Reach for it only for a nested child that isn't itself a registered component, or for design needed outside the render path (an effect, an imperative measurement) — see Styling components. Token resolution is configured with resolveToken on your Config (type ResolveToken).

Re-exported types and utilities

// From core
type Config, Components, ExperienceTemplates, Registration, ExperienceTemplateRegistration,
type ComponentConfig, ExperienceTemplateConfig,
type ContentfulComponent, ContentfulExperienceTemplate,
type RenderContext, ResolveToken,
type ExperiencePayload, ExperienceNode, ComponentNode, ExperienceTemplateNode,
type PortableRenderPlan, PortableRenderNode, PortableRegistration,
type DesignPropValue, ManualDesignValue, DesignToken, ValuesByViewport,
type ViewportDef, ExperienceContext, ResolveContext,
type ResolverConfig, ResolveExperienceOptions

// From design (if you want to do your own viewport-aware resolution)
getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery,
isCssProperty, toCssKey, CSS_PROPERTIES

Quick reference

// components/Button.tsx: content + resolved design both arrive as props
'use client';

interface ButtonProps {
  label?: string;
  url?: string;
  backgroundColor?: string; // resolved design, auto-filled
  color?: string;
}

export function Button({ label, url, backgroundColor, color }: ButtonProps) {
  return (
    <a href={url} style={{ background: backgroundColor, color }}>
      {label}
    </a>
  );
}
// lib/experience-config.tsx
import {
  defineComponent,
  type Components,
  type Config,
  type ResolveToken,
} from '@contentful/experiences-react';

import { Button } from './components/Button';

const components: Components = {
  // Bare component, or defineComponent({...}) when you need defaults/resolveData.
  Button: defineComponent({
    resolveData: ({ content }) => ({ url: ensureScheme(content.url) }),
    component: Button,
  }),
};

const resolveToken: ResolveToken = (token) => designTokens[token.value];

export const experienceConfig: Config = { components, resolveToken };
// app/[slug]/page.tsx: in a server component
import { fetchExperience, ServerExperienceRenderer } from '@contentful/experiences-react';

const experience = await fetchExperience(
  { spaceId: process.env.SPACE_ID!, environmentId: 'master', experienceId: slug },
  { accessToken: process.env.CDA_TOKEN! },
  { config: experienceConfig }
);
return <ServerExperienceRenderer experience={experience} config={experienceConfig} />;

Slot children

Every slot arrives as a prop named after the slot, holding an array of pre-rendered nodes (ReactNode[]) keyed by the renderer — not a single wrapping node. Drop the array straight into JSX for the common "just render them" case (React renders keyed arrays), or map / filter over it to wrap, reorder, or drop children individually.

// components/Section.tsx
import type { ReactNode } from 'react';

export function Section({ children }: { children?: ReactNode[] }) {
  // Common case — render them all:
  return <div>{children}</div>;

  // Or take control of each child:
  // return (
  //   <div>
  //     {children?.map((child, i) => (
  //       <div className="cell" key={i}>{child}</div>
  //     ))}
  //   </div>
  // );
}

children is not special — it is simply the conventional name for the default slot. Every slot is merged into props under its own name, each as its own ReactNode[], so a component with a header slot just declares header?: ReactNode[] and renders it the same way. This applies identically to coded Experience Templates: a template with a content slot receives a content prop.

For the full getting-started walkthrough, the merge-precedence rules, viewport handling, and design rationale, see the root README and AGENTS.md.


License

MIT. See the repository LICENSE and NOTICE for full attribution.