> 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.

# Coded components

> Import components that embed other components through typed slots, and resolve the parent-child hierarchy with composite mode.

**Coded components** are components that embed other components through typed **slots**. By default, the CLI imports every component as a flat, standalone definition. Composite mode reads those slot relationships and imports the related components together as one hierarchy, so the nesting in your codebase survives into your Contentful manifest.

Composite mode does more than preserve how the hierarchy looks. It imports a parent and everything it slots as a single unit, and it detects problematic relationships, such as slot dependency cycles, before they reach Contentful.

This page explains what coded components are, when to reach for composite mode, where the hierarchy comes from, and how the interactive wizard surfaces it. For the flag list, see [Command reference](/experience-orchestration/command-reference).

## When to use composite mode

Import is **atomic** by default: components are extracted as a flat list with no embedded-component hierarchy. Reach for composite mode when your components nest other components and you want them imported together with that relationship preserved rather than flattened.

Opt in with `--composite`:

```bash
experiences import --project ./my-design-system --composite
```

Any composition flag (for example, `--composition-map` or `--composition-agent`) implies `--composite`, so you rarely pass it explicitly once you use a composition source.

> **Info**
>
> The difference between atomic and composite is whether one component can slot another. Atomic imports every component standalone; composite reads the slot relationships between components and imports them together. On top of slots, composite mode adds the grouped review experience, with search, lineage, breaking-change, and cycle tooling the atomic review does not have.

## Slots and allowed components

A **slot** is a typed property on a parent component that holds another component. Each slot carries a list of the component types permitted to fill it. A component that contains only slots is valid; it does not need its own authorable properties.

A parent can slot a component that itself slots others. Composite mode imports the whole chain together, so accepting a parent pulls in every component reachable through its slots as one unit.

## Where the hierarchy comes from

Composite mode resolves parent-child relationships from up to three sources. When more than one source describes the same relationship, the higher-precedence source wins:

| **Source**        | **How it is resolved**                                                                                       | **Deterministic** |
| ----------------- | ------------------------------------------------------------------------------------------------------------ | ----------------- |
| Typed slots       | Read directly from the typed slots your source declares. No flag beyond `--composite`.                       | Yes               |
| Composition map   | A hand-authored `parent → children` map you pass with `--composition-map`.                                   | Yes               |
| Composition agent | A coding agent resolves the hierarchy for codebases that do not declare typed slots (`--composition-agent`). | Best effort       |

### Typed slots

When your components declare typed slots, composite mode picks up the hierarchy automatically. The extractor reads the slots directly from your source, whether a React prop typed as `ReactElement<XProps>`, a Svelte `Snippet<[XProps]>`, or a slot prop annotated with the `@allowedComponents` JSDoc tag to set its allowed types explicitly. This is the reproducible path and needs no extra configuration:

```bash
experiences import --project ./my-design-system --composite
```

To capture what typed-slot extraction resolved, write it out with `--generate-map`. The generated map always mirrors the slots found in your source, so it doubles as a starting point for the composition map below:

```bash
experiences import --project ./my-design-system --generate-map ./composition-map.json
```

### Composition map

When your codebase does not declare typed slots, hand-author a composition map and pass it. A composition map is a small JSON file that lists which children each parent slots:

```json
{
  "version": 1,
  "groups": {
    "Card": ["Button", "Media"],
    "Section": ["Card"]
  }
}
```

* `version` — the interchange format version. Use `1`.
* `groups` — an object keyed by parent component name. Each value is the list of child component names that parent slots.

Write this file from scratch, or start from a `--generate-map` skeleton, hand-edit it, then feed it back:

```bash
experiences import --project ./my-design-system --composition-map ./composition-map.json
```

A composition map is the reproducible option for codebases without typed slots, because the same map produces the same hierarchy on every run.

### Composition agent

When you cannot declare slots or author a map, a coding agent can infer the hierarchy:

```bash
experiences import --project ./my-design-system --composition-agent --agent claude
```

By default the agent writes a sandboxed parser and runs it (`--composition-agent-mode parser`); pass `--composition-agent-mode edges` to have it list relationships directly. The agent path is best effort and can vary between runs, so results are cached. Pass `--composition-refresh` to bypass the cache and resolve from scratch. The agent only finds relationships your code actually expresses; a codebase with no recognizable component definitions yields no hierarchy.

## Review coded components in the wizard

Composite mode reworks the wizard's two review screens, the **Scope gate** and **Final review**, into grouped, hierarchy-aware views.

### Scope gate

The **Scope gate** replaces the flat list with a grouped tree whose glyphs reflect which components nest inside which. A counter strip and "Added components" / "Added groups" columns track your selection as you go. Accept the components you want individually, or in bulk; anything you leave untouched is simply not imported:

* **Accept or reject any component.** Rejecting a parent cascades to its descendants after a confirmation.
* **Accept in bulk.** The accept-all key seeds every non-cycle component, then pulls in any cycle group reachable from an accepted parent; press it again to reject all. A separate key accepts everything the AI auto-filter did not flag.
* **Switch views.** Toggle between the grouped tree and a flat list, and expand or collapse all groups at once.
* **Trace lineage.** Open the **lineage panel** on a nested component to see its full ancestor tree, with shared-parent and cycle markers.
* **Focus a subtree.** Jump to a component and filter the view to it plus its transitive ancestors, or filter to only components in a cycle.
* **Search.** A fuzzy-search overlay filters the grouped list as you type, with autocomplete.

When the AI auto-filter has run, rejected components carry an `[×]` badge; open the rationale panel on one to read the full, untruncated reason. Cycle members are marked and pinned first. Consult the on-screen legend for the exact keys.

### Final review

The **Final review** step opens on the same grouped sidebar so you can inspect and edit each generated component before push:

* **Field editor.** Open a component to edit its properties and its slots. For each slot you can edit its allowed components, whether it is required, and its description. The allowed-components picker is cycle-aware, and unselected slot rows show a summary of what they allow.
* **Removed-components strip.** A strip pinned at the top lists every component that will be **deleted** from Contentful on push, so removals never happen silently.
* **Breaking changes.** Filter to only components with breaking changes, and open a breaking-changes panel that renders slot-level mutations in plain text rather than a raw diff.
* **Undo/redo and reload.** Edits are undoable, group-expansion state and history survive a reload from a saved run, and tabbing away from the field editor with unsaved edits warns you first.

Editing a slot's allowed components in the field editor is also one of the ways you resolve a dependency cycle, described below.

## Slot dependency cycles

A **slot dependency cycle** is a loop in the hierarchy, where component A slots B, and B slots A, directly or through a longer chain such as `Card → Media → Banner → Card`. Contentful can't create components with circular slot references, so the CLI blocks the push before it reaches Contentful and names the components in the loop.

The wizard surfaces cycles at both review screens:

* The **Scope gate** marks cycle members and shows a guidance line. Cycle members are always accepted or rejected together as a unit.
* The **Final review** step shows a banner naming the cycle, auto-rejects the cycle members and their slot-ancestors when it opens, and blocks finalize until no cycle remains.

You can resolve a cycle in three ways:

* **Break an edge from the cycles panel** — open the break-cycle overlay from the cycles panel (`c`, then `x`), pick a slot relationship to remove, and confirm. This deletes one allowed-component reference and clears the cycle without dropping any component.
* **Break an edge in the field editor** — open a component in the cycle, select the slot that closes the loop, and remove the offending entry from its allowed components. This has the same effect as the overlay, edited by hand.
* **Reject a member** — reject one of the components in the cycle, which removes it from the import along with the loop.

> **Info**
>
> The recommended workflow is to accept the cycle members together into the **Final review** step, then break an edge, either from the cycles panel or by editing a slot in the field editor, so you keep every component and only drop the one relationship that closed the loop.

On the headless path, pass `--auto-reject-cycles` to automatically exclude the components in a cycle and continue. Without it, a headless run that hits a cycle refuses to save or push and names the offending components:

```bash
experiences import \
  --project ./my-design-system \
  --composite \
  --auto-accept-scope \
  --auto-reject-cycles \
  --yes
```

## Run coded components headless

Composite mode works on the headless path for CI and non-interactive shells. Pass `--composite` (or any composition flag) alongside your automation flags:

```bash
experiences import \
  --project ./my-design-system \
  --composite \
  --space-id $CONTENTFUL_SPACE_ID \
  --environment-id $CONTENTFUL_ENVIRONMENT_ID \
  --cma-token $CONTENTFUL_MANAGEMENT_TOKEN \
  --agent claude \
  --auto-accept-scope \
  --auto-reject-cycles \
  --yes
```

The composition flags forward to the extraction step, so the hierarchy resolves the same way it does interactively. Atomic remains the default, so a headless run with no composition flag imports a flat list.

## Next steps

* **[Import components](/experience-orchestration/import-components)** — The guided walkthrough of the import wizard.
* **[Command reference](/experience-orchestration/command-reference)** — Every composition flag on `experiences import`.
* **[Run the pipeline step by step](/experience-orchestration/pipeline-steps)** — Run each stage as an individual command.
* **[FAQ](/experience-orchestration/faq)** — Common questions, including atomic versus composite and cycle resolution.