Coded components

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.

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:

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

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:

SourceHow it is resolvedDeterministic
Typed slotsRead directly from the typed slots your source declares. No flag beyond --composite.Yes
Composition mapA hand-authored parent → children map you pass with --composition-map.Yes
Composition agentA 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:

$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:

$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:

1{
2 "version": 1,
3 "groups": {
4 "Card": ["Button", "Media"],
5 "Section": ["Card"]
6 }
7}
  • 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:

$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:

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

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:

$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:

$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