> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getflowpilot.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom components

> Reference a native component built in your app from inside a flow. You define its inputs and outputs in the editor; the SDK renders the real native view.

A custom component lets a flow use a piece of UI that the flow engine does not provide: a native date picker, a chart, a map, a camera preview, anything you build in your app. You **define its contract** in the editor (a name, the inputs it accepts, and the outputs it can emit), and the SDK **renders the real native view** at runtime (both the [iOS](/ios-sdk/custom-components) and [Expo](/expo-sdk/custom-components) SDKs support this). In the editor itself, a custom component shows as a placeholder shell, never the real UI.

Custom components are app-scoped: a component you define is available across every flow in that app.

<Note>
  Custom components are an **Advanced mode** feature. They are not in the Simple [Element Library](/editor/element-library), and a flow that uses one shows it as locked when opened in [Simple mode](/editor/editing-modes). Switch to Advanced to add or edit custom components.
</Note>

<Warning>
  The editor side and the native side must agree. The flow references your component by **key** and **version**; the SDK matches it to the handler you register natively. If the key, version, input keys, or output keys do not line up, the component will not render or receive data. This is the single most common source of bugs. See the native side for [iOS](/ios-sdk/custom-components) or [Expo](/expo-sdk/custom-components).
</Warning>

## Define a custom component

<Steps>
  <Step title="Open the Custom tab">
    In the left mini-sidebar, click **Custom** (the puzzle icon). This opens the **App Components** panel, which lists every custom component defined for the app.
  </Step>

  <Step title="Create a new definition">
    Click **New** (or **Create Component** on the empty state). The **New Custom Component** dialog opens.
  </Step>

  <Step title="Set the basics">
    * **Display Name**, for example "Weight Picker".
    * **Component Key** is auto-generated from the name (lowercased, only `a-z`, `0-9`, and `_`). This is the identifier used in code, and **it cannot be changed after creation**.
    * **Description** (optional).
  </Step>

  <Step title="Define Inputs">
    In the **Inputs** section, add each value your component accepts. Every input has a **Key** (lowercased, `a-z0-9_`), a **Type** (Text, Number, Boolean, Color, or Image URL), an optional **Description**, and a **Required** toggle.
  </Step>

  <Step title="Define Outputs">
    In the **Outputs** section, add each event your component can emit. Every output has an **Output Name** (label), a **Key**, an optional **Description**, and optional **Payload Fields**. A payload field has a key and a type (Text, Number, Boolean, or List). An output with no payload fires without data.
  </Step>

  <Step title="Create the component">
    Click **Create Component**. It is saved as a **draft**.
  </Step>
</Steps>

The input types map to the schema type `CustomComponentInputType` (`string`, `number`, `boolean`, `color`, `image`). The dialog calls them Text, Number, Boolean, Color, and Image URL.

## Status: draft, active, deprecated

A custom component moves through three statuses, managed from the App Components panel:

* **Draft**: the default for a new component. Drafts can be edited and deleted freely.
* **Active**: promote a draft with **Activate**. Once active, "schema changes will create new versions and the component cannot be deleted". The Delete action becomes "Cannot delete (not draft)".
* **Deprecated**: retire an active component with **Deprecate**. Deprecated components are hidden from the editor but remain valid in flows that already use them.

Because of this, the [reference](/reference/component-reference#custom) to a component carries a **version** (`CustomComponentRef` is `{ key, version }`). Editing the inputs or outputs of an **active** component bumps the version, and the save button reads **Save & Create New Version**.

Row actions in the App Components panel: **Edit**, **Duplicate**, **Activate** (drafts), **Deprecate** (active), and **Delete** (drafts only).

## Use it in a flow

Once defined, a custom component appears at the bottom of the **Insert > Components** palette under a **Custom** group, alongside the built-in primitives. Drag it onto a screen (or click to add) like any other component.

A custom component is a leaf node: it **cannot have children**. On the canvas it renders as a dashed placeholder shell with a puzzle icon, the component's name, the subtitle "(Custom Component)", and its input and output counts. If a required input is unbound, a "missing" badge appears on the placeholder.

### Bind inputs and map outputs

Select a placed custom component to open its properties panel. It has only four sections (there are no styling sections, because styling is the SDK's job):

* **Overview**: the component name, a visibility toggle, duplicate, and delete.
* **Inputs**: bind each defined input to either a **static value** or a **variable**. Inputs are value- or variable-bound only; there are no expressions or `{{ }}` interpolation here.
* **Interactions**: map each **output** to one or more [actions](/editor/interactions-and-actions) (for example `goNext`, `setVariable`, `closeFlow`, `navigate`, `openUrl`, `haptic`, `trackEvent`). If an output carries a payload, you can bind a payload field to a variable; that binding is stored as a `setVariable` action using `{{payload.fieldKey}}`.
* **Size**: Width (content, fill, or a fixed number) and Height (content or a fixed number), with optional min and max constraints.

## The editor and SDK contract

This is the contract both sides must share:

| Editor defines                  | SDK must match                                   |
| ------------------------------- | ------------------------------------------------ |
| Component **key** (immutable)   | The key you register natively                    |
| Schema **version**              | The version your registered component implements |
| Input **keys** and **types**    | The inputs your native view reads                |
| Output **keys** and **payload** | The events your native view emits                |

The flow stores a `CustomComponentRef` of `{ key, version }`. At runtime the SDK looks up the native handler you registered under that key and hands it the bound input values; when your native view emits an output, the SDK runs the actions you mapped to it. See the native registration for [iOS](/ios-sdk/custom-components) or [Expo](/expo-sdk/custom-components).

<Note>
  **Custom screens are different.** This page covers custom *components* (a native view embedded inside a flow screen). Both SDKs also support custom *screens* (a whole native screen registered with `registerCustomScreen`). The editor has no separate "custom screen" surface; only custom components are defined here. See custom screens for [iOS](/ios-sdk/custom-screens) or [Expo](/expo-sdk/custom-screens).
</Note>

## Common mistakes

* **Key or version mismatch.** The native handler must register the same key and a matching version. A mismatch means the SDK cannot find your component.
* **Input or output key typos.** The SDK only delivers values for keys it recognizes. A typo means an input never arrives or an output never fires. Required inputs that are unbound show a "missing" badge in the editor.
* **Expecting the editor to show the real UI.** The editor always renders a placeholder shell. The real native view appears only in your app through the SDK.
* **Trying to style it in the editor.** There are no background, border, padding, or typography controls. Styling is handled inside your native implementation.
* **Trying to nest components inside it.** Custom components are black boxes with no children.
* **Deleting an active component.** Not allowed. Deprecate it instead, which hides it from the editor but keeps existing flows working.

## Related pages

* [iOS SDK: Custom components](/ios-sdk/custom-components)
* [Expo SDK: Custom components](/expo-sdk/custom-components)
* [iOS SDK: Custom screens](/ios-sdk/custom-screens)
* [Expo SDK: Custom screens](/expo-sdk/custom-screens)
* [Interactions and actions](/editor/interactions-and-actions)
* [Variables](/editor/variables)
* [Components](/editor/components)
* [Component reference](/reference/component-reference)
