---
name: mystique-components
scope: frontend
description: "Component configuration reference (fc.list, fc.card.attribute, fc.tabs, etc.)"
load: on-demand
relevant-skills: [fluent-mystique-builder, fluent-mystique-validate, fluent-mystique-scaffold, fluent-mystique-analyze]
---

# Mystique Component Configuration Reference

Comprehensive reference for configuring Fluent Commerce Mystique manifest components. Only use components documented here -- any guessing will lead to errors unless it is a custom component from a registered plugin.

---

## Component Width Options

All card-style components support a `width` prop based on a 12-column grid. On mobile devices all widths automatically change to 12 (full width).

| Named Width   | Grid Columns | Description                     |
|---------------|-------------:|---------------------------------|
| `quarter`     |            3 | 25% of row                      |
| `third`       |            4 | 33% of row                      |
| `half`        |            6 | 50% of row                      |
| `two-thirds`  |            8 | 66% of row                      |
| `full`        |           12 | 100% of row (default)           |
| `1`-`12`      |        1--12 | Direct column count             |

---

## Dynamic Attributes

Dynamic Attributes are a common convention used when defining key/value information in the manifest -- card attributes, table columns, and form labels. They support three types: **standard** (text), **image**, and **component**.

### Standard (Text) Attribute

The default type when `type` is omitted.

| Prop        | Type                        | Required | Description                                                                                                       |
|-------------|-----------------------------|---------:|-------------------------------------------------------------------------------------------------------------------|
| `type`      | `"standard"` or omitted     |       No | Attribute display type. Default is standard text.                                                                 |
| `label`     | `string`                    |      Yes | Key label shown on cards or column header in lists. Supports i18n keys.                                           |
| `value`     | `string` (template)         |      Yes | Template string evaluated against the data context.                                                               |
| `link`      | `string` (template)         |       No | If present and condition is truthy, render value as a clickable link to this URL.                                  |
| `condition` | `string` (template)         |       No | If evaluates to `true`, render the link/value. Otherwise render the defaultValue placeholder.                     |
| `enableCopyIcon` | `"displayAlways"` / `"displayOnHover"` | No | Show a copy-to-clipboard icon.                                                            |
| `options`   | `TextOptions`               |       No | Styling configuration (see below).                                                                                |

#### TextOptions

| Prop     | Type            | Description                                          |
|----------|-----------------|------------------------------------------------------|
| `styles` | `TextStyle[]`   | Array of style rules evaluated in order until first match. |

#### TextStyleOption

| Prop      | Type        | Required | Description                                                                                              |
|-----------|-------------|----------|----------------------------------------------------------------------------------------------------------|
| `value`   | `string`    | No       | Value to compare `matches` against. Defaults to the rendered attribute value itself.                     |
| `matches` | `string[]`  | No       | If any entry matches the value, apply this style. If omitted or empty, acts as catch-all default.        |
| `icon`    | `IconStyle` | No       | Icon to display beside the text.                                                                         |
| `text`    | `TextStyle` | No       | Text colour to apply.                                                                                    |

#### IconStyle

| Prop     | Type     | Required | Description                                                                                              |
|----------|----------|----------|----------------------------------------------------------------------------------------------------------|
| `name`   | `string` | Yes      | Icon name. Use `"loading"` for spinner. Otherwise a supported icon: `MdCheckCircle`, `FaCheck`, etc.    |
| `colour` | `string` | No       | Theme colour name (`"primary"`, `"secondary"`, `"error"`) or CSS hex (`"#00FF00"`).                      |

#### TextStyle

| Prop     | Type     | Required | Description                                                                 |
|----------|----------|----------|-----------------------------------------------------------------------------|
| `colour` | `string` | No       | Theme colour name or CSS hex value.                                         |

**Icon name prefixes:** `Md` = Material Design icons, `Fa` = Font Awesome icons. Always include the prefix.

#### Example: styled status attribute

```json
{
  "label": "i18n:fc.om.orders.column.status",
  "value": "{{status}}",
  "options": {
    "styles": [
      {
        "matches": ["BOOKED", "CONFIRMED"],
        "icon": { "name": "MdCheckCircle", "colour": "primary" },
        "text": { "colour": "green" }
      },
      {
        "matches": ["CANCELLED"],
        "icon": { "name": "MdCancel", "colour": "error" },
        "text": { "colour": "red" }
      },
      {
        "icon": { "name": "MdInfo" }
      }
    ]
  }
}
```

### Image Attribute

| Prop        | Type                       | Required | Description                                                                                              |
|-------------|----------------------------|---------:|----------------------------------------------------------------------------------------------------------|
| `type`      | `"image"`                  |      Yes | Must be `"image"`.                                                                                       |
| `label`     | `string`                   |      Yes | Label text.                                                                                              |
| `value`     | `string` (template)        |      Yes | Template string resolving to image URL, or a status value when using icon styles.                        |
| `link`      | `string` (template)        |       No | Wrap image in a link.                                                                                    |
| `condition` | `string` (template)        |       No | Show image only if truthy.                                                                               |
| `options`   | `ImageOptions`             |       No | Image display options (see below).                                                                       |

#### ImageOptions

| Prop      | Type      | Required | Description                                      |
|-----------|-----------|----------|--------------------------------------------------|
| `width`   | `number`  | No       | Image width in pixels (height auto-scales).      |
| `height`  | `number`  | No       | Image height in pixels (width auto-scales).      |
| `alt`     | `string`  | No       | Alt text for the image.                          |
| `noModal` | `boolean` | No       | `true` to prevent the default click-to-enlarge modal. |
| `styles`  | `TextStyleOption[]` | No | Same style array as text attributes -- used for status icon patterns. |

#### Example: product thumbnail

```json
{
  "type": "image",
  "label": "Product Image",
  "value": "{{node.attributes.byName.imageUrl}}",
  "link": "#/product/{{node.id}}",
  "condition": "{{and node.attributes.byName.imageUrl}}",
  "options": {
    "width": 50,
    "height": 50,
    "alt": "Product image"
  }
}
```

#### Example: status icon pattern

```json
{
  "type": "image",
  "label": "Status",
  "value": "{{wave.status}}",
  "options": {
    "styles": [
      {
        "matches": ["CREATED", "PICK"],
        "icon": { "name": "MdShoppingBasket" }
      },
      {
        "value": "{{wave.attributes.byName.customAttribute}}",
        "matches": ["customValue"],
        "icon": { "name": "FaBoxOpen" }
      },
      {
        "icon": { "name": "FaTruck" }
      }
    ]
  }
}
```

### Component Attribute

Embed a component inside another component that accepts DynamicAttributes. Not all components work well in all positions.

| Prop        | Type                          | Required | Description                                                    |
|-------------|-------------------------------|---------:|----------------------------------------------------------------|
| `type`      | `"component"`                 |      Yes | Must be `"component"`.                                         |
| `label`     | `string`                      |      Yes | Label text.                                                    |
| `value`     | `string`                      |       No | Not used in component attributes.                              |
| `link`      | `string`                      |       No | Not used in component attributes.                              |
| `options`   | `MystiqueComponentInstance`   |       No | Component definition with `component` and `props` properties.  |
| `condition` | `string` (template)           |       No | Show component only if truthy.                                 |

#### Example: embedded print button in table column

```json
{
  "label": "Print Label",
  "type": "component",
  "options": {
    "component": "fc.button.print.download",
    "props": {
      "label": "Print",
      "href": "api/v4/consignment/{{articleById.id}}/labelStream",
      "filename": "pack-label.pdf"
    }
  },
  "condition": "{{and articleById.id}}"
}
```

---

## Components

### fc.page

Page wrapper component. Used as the root component of a route.

| Prop        | Type                       | Required | Description                                                        |
|-------------|----------------------------|---------:|--------------------------------------------------------------------|
| `title`     | `string` (template)        |       No | Page title displayed in header. Supports templates and i18n.       |
| `actions`   | `true` or `PageActionConfig` |     No | Enable workflow user actions for the first entity returned by the page query, or provide action filtering/extensions. |
| `backButton`| `object`                   |       No | Configuration for a back/breadcrumb navigation button.             |

`fc.page` is a route/page definition, so nested components are declared in the top-level `descendants` array and page data is defined in the route `data` block.

```json
{
  "path": "/order/:id",
  "component": "fc.page",
  "props": {
    "title": "Order {{order.ref}}",
    "actions": true
  },
  "descendants": [
    { "component": "fc.card.attribute", "dataSource": "orderById", "props": { "..." : "..." } }
  ],
  "data": {
    "query": "query($id: ID!) { orderById(id: $id) { id ref status } }",
    "variables": { "id": "{{params.id}}" }
  }
}
```

### fc.list

Table/list component with columns, filters, pagination, density controls, and row expansion.

| Prop         | Type                           | Required | Default    | Description                                                                                                   |
|--------------|--------------------------------|---------:|------------|---------------------------------------------------------------------------------------------------------------|
| `title`      | `string`                       |       No | none       | Title text of the card.                                                                                       |
| `highlight`  | `MystiqueThemeColor` / hex     |       No | none       | Coloured highlight bar above the card.                                                                        |
| `attributes` | `DynamicAttribute[]`           |      Yes | none       | Columns to show. `label` is the header; `value` is re-evaluated per row.                                      |
| `rowLink`    | `string` (template)            |       No | none       | Link the entire row. Evaluated per row, e.g. `"/orders/{{node.id}}"`.                                        |
| `density`    | `DensityConfig`                |       No | false      | Row height controls: compressed (28px) or standard (52px).                                                    |
| `row`        | `RowConfig`                    |       No | none       | Per-row config for expansion and actions.                                                                     |

**Note:** The list component uses `dataSource` on the component instance (not inside `props`) to bind to a connection query result.

#### DensityConfig

| Prop      | Type                          | Default      | Description                            |
|-----------|-------------------------------|--------------|----------------------------------------|
| `toggle`  | `boolean`                     | `false`      | Enable toggle buttons for density.     |
| `initial` | `"compressed"` / `"standard"` | `"standard"` | Initial row density mode.              |

#### RowConfig

| Prop        | Type                          | Description                                                                                                  |
|-------------|-------------------------------|--------------------------------------------------------------------------------------------------------------|
| `expansion` | `MystiqueComponentInstance`   | Component displayed below each row when expanded. Receives row data as `data`. Set `toggle: true` on the expansion object for user-toggled expansion. |
| `actions`   | `CardActions`                 | Action buttons at the end of each row.                                                                       |

#### Example: order list with filters

```json
{
  "component": "fc.list",
  "dataSource": "orders",
  "props": {
    "title": "i18n:fc.om.orders.index.list.title",
    "row": {
      "actions": {
        "primary": [
          { "type": "userAction", "name": "Review" }
        ]
      },
      "expansion": {
        "toggle": true,
        "component": "fc.button.bar",
        "descendants": [
          {
            "component": "fc.button.print.download",
            "props": {
              "label": "Print",
              "href": "string",
              "filename": "string"
            }
          }
        ],
        "props": { "title": "Title" }
      }
    },
    "attributes": [
      {
        "label": "i18n:fc.om.orders.index.list.column.orderRef.heading",
        "value": "{{node.ref}}",
        "link": "#/orders/{{node.id}}"
      },
      {
        "label": "i18n:fc.om.orders.index.list.column.image.heading",
        "type": "image",
        "value": "https://fluentcommerce.com/logo.svg"
      }
    ]
  }
}
```

### fc.card.attribute

Key-value attribute card. Maps data onto a two-column layout with labels on the left and values on the right.

| Prop         | Type                                         | Required | Default | Description                                                                 |
|--------------|----------------------------------------------|---------:|---------|-----------------------------------------------------------------------------|
| `title`      | `string`                                     |       No | none    | Card title.                                                                 |
| `highlight`  | `MystiqueThemeColor` / hex                   |       No | none    | Coloured highlight bar above the card.                                      |
| `width`      | `CardWidth` (see Width Options above)        |       No | `full`  | Card width on the 12-column grid.                                           |
| `attributes` | `DynamicAttribute[]`                         |      Yes | none    | Key/value pairs to display.                                                 |
| `link`       | `string`                                     |       No | none    | Makes the card or attribute values clickable.                               |

#### Example: order summary card

```json
{
  "component": "fc.card.attribute",
  "dataSource": "orderById",
  "props": {
    "title": "i18n:fc.om.orders.detail.card.summary.title",
    "width": "half",
    "highlight": "#00FF00",
    "attributes": [
      {
        "label": "i18n:fc.om.orders.detail.card.summary.attribute.orderType.label",
        "value": "{{type}}",
        "enableCopyIcon": "displayAlways"
      },
      {
        "label": "i18n:fc.om.orders.detail.card.summary.attribute.orderStatus.label",
        "value": "{{status}}",
        "enableCopyIcon": "displayOnHover"
      },
      {
        "label": "i18n:fc.om.orders.detail.card.summary.attribute.creationDate.label",
        "value": "{{dateFormat createdOn}}"
      }
    ]
  }
}
```

### fc.tabs

Tabbed layout for organizing content into switchable panels.

| Prop      | Type       | Required | Description                                                         |
|-----------|------------|----------|---------------------------------------------------------------------|
| `layouts` | `Tab[]`    | Yes      | Array of tab definitions, each with `label` and `descendants`.      |

Each tab object:

| Prop          | Type          | Description                                           |
|---------------|---------------|-------------------------------------------------------|
| `label`       | `string`      | Tab label text. Supports i18n.                        |
| `descendants` | `Component[]` | Components rendered when this tab is active.          |

```json
{
  "component": "fc.tabs",
  "props": {
    "layouts": [
      {
        "label": "i18n:fc.om.order.tab.details",
        "descendants": [
          { "component": "fc.card.attribute", "props": { "..." : "..." } }
        ]
      },
      {
        "label": "i18n:fc.om.order.tab.items",
        "descendants": [
          { "component": "fc.list", "dataSource": "order.items", "props": { "..." : "..." } }
        ]
      }
    ]
  }
}
```

### fc.tile.metric

Dashboard metric tile displaying a KPI value with optional styling.

| Prop      | Type                                   | Required | Default   | Description                                                                                      |
|-----------|----------------------------------------|----------|-----------|--------------------------------------------------------------------------------------------------|
| `label`   | `string`                               | Yes      | none      | Main text label of the tile.                                                                     |
| `value`   | `string` (template)                    | Yes      | none      | Value displayed on the tile.                                                                     |
| `options` | `DynamicAttribute.TextOptions`         | No       | none      | Styling options. Can include `styles` with `expression` for conditional colouring.               |
| `variant` | `"large"` / `"small"`                  | No       | `"large"` | `"large"`: 54px centred. `"small"`: 36px left-aligned. Identical on mobile.                      |
| `width`   | `CardWidth`                            | No       | `"full"`  | Tile width on the grid.                                                                          |
| `link`    | `string` (template)                    | No       | none      | Navigate to this URL when tile is clicked.                                                       |

#### Example: escalated orders tile

```json
{
  "component": "fc.tile.metric",
  "props": {
    "label": "Escalated",
    "value": "{{orders.edges.length}}",
    "variant": "small",
    "width": "third",
    "link": "#/escalated-orders",
    "options": {
      "styles": [
        {
          "expression": "{{gt orders.edges.length 10}}",
          "text": { "color": "error" }
        }
      ]
    }
  }
}
```

### fc.card.metric

Dashboard metric card for KPI display with icon and highlight colour.

| Prop        | Type                              | Required | Description                                 |
|-------------|-----------------------------------|----------|---------------------------------------------|
| `title`     | `string`                          | No       | Card title.                                 |
| `width`     | `CardWidth`                       | No       | Card width on the grid.                     |
| `value`     | `string` (template)               | Yes      | The metric value to display.                |
| `icon`      | `{ name: string }`               | No       | Icon to display beside the metric.          |
| `highlight` | `MystiqueThemeColor`              | No       | Coloured highlight (`"primary"`, `"success"`, etc.). |

```json
{
  "component": "fc.card.metric",
  "props": {
    "title": "Orders Today",
    "width": "quarter",
    "value": "{{ordersToday.totalCount}}",
    "icon": { "name": "MdShoppingCart" },
    "highlight": "primary"
  }
}
```

### fc.activity.entity

Activity timeline component for displaying chronological entity events.

```json
{
  "component": "fc.activity.entity",
  "props": {
    "dataSource": "order"
  }
}
```

### fc.conditional

Conditional rendering component. Shows descendants only when the condition evaluates to true.

| Prop    | Type              | Required | Description                                                    |
|---------|-------------------|----------|----------------------------------------------------------------|
| `value` | `string` (template) | Yes   | Template expression to evaluate.                               |
| `matches` | `string[]`      | No       | If the value matches any entry, render descendants.            |

```json
{
  "component": "fc.conditional",
  "props": {
    "value": "{{order.status}}",
    "matches": ["BOOKED", "CONFIRMED"]
  },
  "descendants": [
    { "component": "fc.card.attribute", "props": { "..." : "..." } }
  ]
}
```

### fc.page.section

Section wrapper that visually groups content within a page.

```json
{
  "component": "fc.page.section",
  "props": {
    "title": "Customer Information"
  },
  "descendants": [
    { "component": "fc.card.attribute", "props": { "..." : "..." } }
  ]
}
```

### fc.attribute.json

Displays a JSON value in a formatted, readable view.

```json
{
  "component": "fc.attribute.json",
  "props": {
    "label": "Payload",
    "value": "{{stringify order.attributes.byName.payload}}"
  }
}
```

### fc.mystique.collapsible.text

Truncated text display with expand/collapse toggle.

```json
{
  "component": "fc.mystique.collapsible.text",
  "props": {
    "value": "{{order.attributes.byName.longDescription}}",
    "maxLines": 3
  }
}
```

### fc.button

Action button component.

| Prop     | Type                        | Required | Description                                          |
|----------|-----------------------------|----------|------------------------------------------------------|
| `label`  | `string`                    | Yes      | Button text.                                         |
| `icon`   | `{ name: string }`         | No       | Icon displayed beside label.                         |
| `action` | `ActionConfig`              | No       | Action to perform on click (navigate, print, etc.).  |

```json
{
  "component": "fc.button",
  "props": {
    "label": "Create Order",
    "icon": { "name": "MdAdd" },
    "action": {
      "type": "navigate",
      "path": "#/orders/create"
    }
  }
}
```

### fc.grid

Responsive grid layout container. Descendants are laid out using a 12-column grid system.

```json
{
  "component": "fc.grid",
  "descendants": [
    {
      "component": "fc.card.attribute",
      "props": { "title": "Summary", "width": "half", "attributes": [] }
    },
    {
      "component": "fc.card.attribute",
      "props": { "title": "Details", "width": "half", "attributes": [] }
    }
  ]
}
```

---

## CardActions

CardActions define buttons on cards and list rows that trigger workflow user actions. Primary actions appear as call-to-action buttons; secondary actions appear under a "More" dropdown menu.

| Prop          | Type            | Required | Default | Description                                                                      |
|---------------|-----------------|----------|---------|----------------------------------------------------------------------------------|
| `primary`     | `CardAction[]`  | No       | none    | Primary-level actions displayed as buttons.                                      |
| `secondary`   | `CardAction[]`  | No       | none    | Secondary-level actions grouped under "More" button.                             |
| `showAtLeast` | `number`        | No       | `0`     | Number of primary actions to keep visible even on mobile (normally all move to "More" menu). |

### CardAction: User Action

All workflow-defined User Actions are automatically appended. Only add them to the manifest to re-order or change behaviour.

| Prop        | Type            | Required | Description                                                                                          |
|-------------|-----------------|----------|------------------------------------------------------------------------------------------------------|
| `type`      | `"userAction"`  | Yes      | Specifies a workflow user action type.                                                               |
| `name`      | `string`        | Yes      | Ruleset name of the user action.                                                                     |
| `label`     | `string`        | No       | Override the button label text.                                                                      |
| `condition` | `string`        | No       | Template string; hide action if renders to `"false"`. Has access to page query data.                 |

```json
{
  "actions": {
    "primary": [
      {
        "type": "userAction",
        "name": "Approve",
        "label": "i18n:fc.om.action.approve",
        "condition": "{{eq order.attributes.byName.lockedOrder 'false'}}"
      }
    ],
    "secondary": [
      { "type": "userAction", "name": "Cancel" }
    ],
    "showAtLeast": 1
  }
}
```

### Workflow vs Manifest Override Precedence

Use this table when deciding where to change behavior:

| Concern | Source of truth | Manifest override supported? |
|---|---|---|
| Button label | Workflow `context[].label` | Yes (`CardAction.label`) |
| Button visibility in UI | Workflow status + ruleset + modules | Partially (`CardAction.condition` can hide) |
| Button order/grouping | Workflow default append order | Yes (reorder via `actions.primary`/`actions.secondary`) |
| Form field type | Workflow `attributes[].type` | No |
| Form field label | Workflow `attributes[].label` | No |
| Form field defaults | Workflow `attributes[].defaultValue` | No |
| Field option source | Workflow `attributes[].source` | No |
| Required/mandatory state | Workflow `attributes[].mandatory` | No |

For OMS App, workflow `context[].modules[]` should include `"adminconsole"` (the OMS App's legacy module alias, not `"oms"`). For Store App, use `"servicepoint"` (legacy alias).

If a change affects the confirmation form schema, change workflow JSON. If it affects only button presentation, change manifest CardAction config.

---

## Data Source Scoping

Components bind to data using the `dataSource` prop (on the component instance, not inside `props`). This scopes the data context for template evaluation inside that component.

```json
{
  "component": "fc.card.attribute",
  "dataSource": "orderById",
  "props": {
    "attributes": [
      { "label": "Ref", "value": "{{ref}}" }
    ]
  }
}
```

Here `{{ref}}` resolves to `orderById.ref` because the dataSource is scoped to `orderById`.

For nested data:
```json
{
  "component": "fc.card.attribute",
  "dataSource": "orderById.customer",
  "props": {
    "attributes": [
      { "label": "Name", "value": "{{firstName}} {{lastName}}" }
    ]
  }
}
```

### Accessing Attributes by Name

Entity attributes from GraphQL are returned as arrays. Use the `byName` shorthand:

```json
"{{order.attributes.byName.vipStatus}}"
```

instead of trying to index into the array.

### Showing a Count

Most queries do not have a total count. Request a large page and use the `edges.length` property:

```json
"{{orders.edges.length}}"
```

---

## fc.provider.graphql

Mid-tree data provider that executes its own GraphQL query and provides the result as data context to its descendants. Use when a subtree of components needs data from a different query than the page query.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `query` | `string` | Yes | GraphQL query string |
| `variables` | `object` | No | Query variables (supports templates from parent data context) |
| `path` | `string` | Yes | JSONPath into query result that becomes descendant data context |
| `max` | `number` | No | Max records to fetch |
| `loadingBehavior` | `string` | No | Loading indicator strategy (`"spinner"`, `"skeleton"`) |

**Key behavior:** Descendants inside the provider receive the query result scoped by `path` as their data context. They can further scope with their own `dataSource`. Variables support template expressions resolved against the parent component's data context.

### Example: inventory positions for a product

```json
{
  "component": "fc.provider.graphql",
  "props": {
    "query": "query($ref: [String]) { inventoryPositions(productRef: $ref) { edges { node { ref qty status locationRef } } } }",
    "variables": { "ref": "{{product.ref}}" },
    "path": "inventoryPositions",
    "max": 100
  },
  "descendants": [
    {
      "component": "fc.list",
      "dataSource": "inventoryPositions",
      "props": {
        "title": "Inventory Positions",
        "attributes": [
          { "label": "Location", "value": "{{node.locationRef}}" },
          { "label": "Quantity", "value": "{{node.qty}}" },
          { "label": "Status", "value": "{{node.status}}" }
        ]
      }
    }
  ]
}
```

### Example: related orders for a customer

```json
{
  "component": "fc.provider.graphql",
  "props": {
    "query": "query($customerId: [Int]) { orders(customerId: $customerId, first: 10) { edges { node { id ref status createdOn } } } }",
    "variables": { "customerId": "{{customer.id}}" },
    "path": "orders"
  },
  "descendants": [
    {
      "component": "fc.list",
      "dataSource": "orders",
      "props": {
        "title": "Recent Orders",
        "attributes": [
          { "label": "Ref", "value": "{{node.ref}}", "link": "#/orders/{{node.id}}" },
          { "label": "Status", "value": "{{node.status}}" },
          { "label": "Created", "value": "{{dateFormat node.createdOn}}" }
        ]
      }
    }
  ]
}
```

### When to use fc.provider.graphql vs useQuery

| Scenario | Use |
|----------|-----|
| Subtree of OOTB components needs different data | `fc.provider.graphql` (manifest-only, no custom code) |
| Multiple descendants need a second query result | `fc.provider.graphql` or page `dataSources` so the host owns the fetch |
| Custom SDK component needs component-private follow-up data | `useQuery()` in the component, after confirming the page query or provider cannot own it |
| Single component needs follow-up data that is not reusable elsewhere | `useQuery()` in the component |
| Tab panel needs independent data | `fc.provider.graphql` wrapping the tab descendants |

---

## fc.repeater

Iterates over an array or connection and renders its descendants once per item. **Requires `dataSource`** pointing to an array or connection path.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `dataSource` | `string` | Yes | Path to array or connection in the data context |

### Data scoping inside fc.repeater

- The `dataSource` points to an array or connection (e.g., `orderById.items`)
- For each iteration, descendants receive the current item as their data context
- For Relay connections (`edges[].node`), the framework auto-unwraps — each iteration's data is the `node` object
- Inside descendants, fields are directly available without `node.` prefix: `{{ref}}`, `{{quantity}}`

### Example: render a card per order item

```json
{
  "component": "fc.repeater",
  "dataSource": "orderById.items",
  "descendants": [
    {
      "component": "fc.card.attribute",
      "props": {
        "title": "Item {{ref}}",
        "width": "half",
        "attributes": [
          { "label": "Product", "value": "{{product.ref}}" },
          { "label": "Quantity", "value": "{{quantity}}" },
          { "label": "Price", "value": "{{currency totalPrice currency}}" }
        ]
      }
    }
  ]
}
```

### Example: repeater with nested list

```json
{
  "component": "fc.repeater",
  "dataSource": "orderById.fulfilments",
  "descendants": [
    {
      "component": "fc.card.attribute",
      "props": {
        "title": "Fulfilment {{ref}}",
        "attributes": [
          { "label": "Status", "value": "{{status}}" },
          { "label": "Type", "value": "{{type}}" }
        ]
      }
    },
    {
      "component": "fc.list",
      "dataSource": "items",
      "props": {
        "title": "Fulfilment Items",
        "attributes": [
          { "label": "SKU", "value": "{{node.ref}}" },
          { "label": "Qty", "value": "{{node.requestedQuantity}}" }
        ]
      }
    }
  ]
}
```

In this example, each fulfilment iteration's data becomes the context. The `fc.list` with `dataSource: "items"` resolves to the current fulfilment's `items` connection.

### fc.repeater vs fc.list

| Feature | `fc.repeater` | `fc.list` |
|---------|--------------|-----------|
| Purpose | Render arbitrary component tree per item | Render tabular rows with columns |
| Layout | Each iteration renders full descendant tree | Table rows with column headers |
| Pagination | No built-in pagination | Built-in pagination, page size controls |
| Filtering | No built-in filtering | Built-in filter panel |
| Use when | Cards per item, custom layouts per item | Tabular data display |
