> This page is for Experience Orchestration.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://contentful.com/developers/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/developers/docs/_mcp/server.

# Style components

> Style registered components from their design properties and resolve design tokens with the Experiences SDK.

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

Design properties authored in Contentful, such as spacing, color, and typography, reach your components as ordinary props. The SDK resolves them while it resolves the experience, so your components style themselves from props, with no SDK code.

## Style from props

Each design property arrives as a prop with the same name: a design property named `backgroundColor` arrives as `backgroundColor`, with design tokens already resolved. Declare the design properties a component uses, read them by name, and style from them:

#### React

```tsx
// components/Heading.tsx
import type { CSSProperties } from 'react';

interface HeadingProps {
  text?: string;
  // Design properties:
  as?: 'h1' | 'h2' | 'h3';
  fontSize?: string;
  fontWeight?: string;
}

export function Heading({ text, as = 'h2', fontSize, fontWeight }: HeadingProps) {
  const Tag = as;
  const style: CSSProperties = { fontSize, fontWeight };
  return <Tag style={style}>{text}</Tag>;
}
```

Don't collect undeclared props with `...rest` and spread them onto an HTML element. Design property names aren't HTML attributes, so React warns and they end up in the markup.

#### Svelte

```svelte
<!-- components/Heading.svelte -->
<script lang="ts">
  export interface HeadingProps {
    text?: string;
    as?: 'h1' | 'h2' | 'h3';
    fontSize?: string;
    fontWeight?: string;
  }

  let { text, as = 'h2', fontSize, fontWeight }: HeadingProps = $props();
</script>

<svelte:element this={as} style="font-size: {fontSize}; font-weight: {fontWeight};">{text}</svelte:element>
```

#### Angular

The renderer sets only the inputs a component declares, because binding an undeclared input is an error. Declaring the input is what makes a design property arrive:

```ts
// components/heading.component.ts
import { NgStyle } from '@angular/common';
import { Component, Input, computed, signal } from '@angular/core';

@Component({
  selector: 'app-heading',
  imports: [NgStyle],
  template: `<h2 [ngStyle]="style()">{{ textValue() }}</h2>`,
})
export class HeadingComponent {
  protected readonly textValue = signal<string | undefined>(undefined);
  protected readonly fontSizeValue = signal<string | undefined>(undefined);
  protected readonly fontWeightValue = signal<string | undefined>(undefined);

  @Input() set text(value: string | undefined) {
    this.textValue.set(value);
  }

  @Input() set fontSize(value: string | undefined) {
    this.fontSizeValue.set(value);
  }

  @Input() set fontWeight(value: string | undefined) {
    this.fontWeightValue.set(value);
  }

  protected readonly style = computed(() => ({
    fontSize: this.fontSizeValue(),
    fontWeight: this.fontWeightValue(),
  }));
}
```

Signal inputs (`input()`) work only with ahead-of-time compilation. The examples use `@Input()` setters that write to a signal.

Design properties come in two kinds, and both arrive the same way:

* **CSS values**, such as `fontSize` or `backgroundColor`: pass them straight into a style.
* **Semantic values**, such as `variant`, `as`, or `ratio`: read them by name and map them to markup or CSS. A design property name doesn't have to match a CSS property.

The SDK doesn't select responsive values at runtime. Use CSS media queries for responsive styling.

## Resolve design tokens

A design property can hold a design token instead of a value. The token arrives as `{ type: 'DesignToken', value: '<token-id>' }`. Only your design system knows what a token ID means, so add a `resolveToken` function to your configuration that returns the value for an ID:

```ts
import type { Config, ResolveToken } from '@contentful/experiences-react';

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

export const experienceConfig: Config = { components, resolveToken };
```

The token ID format is yours: dotted, slashed, or whatever your token export uses. The SDK passes you `token.value` and uses whatever you return. Some common sources:

```ts
// CSS custom properties: no JavaScript cost, and the browser handles theme changes.
resolveToken: (token) => `var(--${token.value.replaceAll('.', '-')})`;

// A tokens object or design tokens package.
resolveToken: (token) => designTokens[token.value];

// A resolved Tailwind theme, walked by ID path.
resolveToken: (token) => token.value.split('.').reduce((o, k) => o?.[k], theme);
```

When `resolveToken` returns `undefined`, or isn't configured, the raw token reaches the component and the SDK reports a diagnostic.

## Use Tailwind, CSS-in-JS, or class names

The SDK doesn't generate a stylesheet or class names. Class-based and CSS-in-JS systems work with the values `resolveToken` returns: return a `var(--...)` reference, a theme value, or a class name, and use the prop in a `style` object, a styled component, or a `clsx` call.

## Read design values without props

A child that isn't a registered component gets no props from the SDK. To style it from the design values of the nearest registered component, read them from context. Use this only for nested children, or for design values needed outside rendering, such as in an effect. Registered components should read props.

#### React

```tsx
'use client';

import { useDesignValues } from '@contentful/experiences-react';

// Not registered, so it has no design props of its own.
function Badge({ label }: { label: string }) {
  const design = useDesignValues<{ color?: string }>();
  return <span style={{ color: design.color }}>{label}</span>;
}
```

`useDesignValues()` is a client hook, so the component must be a client component. It returns `{}` outside a renderer. The type argument is an assertion, not a runtime check, so treat each key as possibly `undefined`.

To turn a design record into a style object, use `toCss(design, { include, exclude })`. It keeps the keys that map to CSS properties and drops semantic ones.

#### Svelte

```svelte
<script lang="ts">
  import { getDesignValues } from '@contentful/experiences-svelte';

  let { label }: { label: string } = $props();

  const design = $derived(getDesignValues<{ color?: string }>());
</script>

<span style="color: {design.color};">{label}</span>
```

Read `getDesignValues()` inside a `$derived`, so it stays reactive.

#### Angular

`injectDesignValues()` returns a `Signal`. Read it inside a `computed()`, and call it from an injection context, like any `inject()` API. It also exposes design properties that a component doesn't declare as inputs.