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

# Custom Steps

> Reference for the custom step configuration and the postMessage API for injecting custom UI into the Dromo import flow.

Custom steps allow you to insert your own UI between any two built-in steps in the Dromo import flow. See the [Custom Steps guide](/guides/custom-steps) for a walkthrough and examples.

## Configuration

<ResponseField name="id" type="string" required>
  Unique identifier for this step. Used as a fallback label in the stepper progress bar if `label` is omitted.
</ResponseField>

<ResponseField name="insertAfter" type="&#x22;HEADER_SELECT&#x22; | &#x22;COLUMN_MATCH&#x22; | &#x22;SELECT_MATCH&#x22;" required>
  Which built-in step this custom step is inserted after.

  | Value             | Inserted after                            |
  | ----------------- | ----------------------------------------- |
  | `"HEADER_SELECT"` | Header row detection                      |
  | `"COLUMN_MATCH"`  | Column mapping                            |
  | `"SELECT_MATCH"`  | Select field matching, just before Review |
</ResponseField>

<ResponseField name="label" type="string">
  Text shown in the stepper progress bar. Defaults to `id` if omitted.
</ResponseField>

<ResponseField name="url" type="string">
  URL of the page to render inside an `<iframe>` within Dromo's modal.
</ResponseField>

<ResponseField name="onMessage" type="(uploader, payload: unknown) => void | Promise<void>">
  Called when the iframe posts `{ type: "DROMO_MESSAGE", payload }`. The `uploader` argument exposes the same mutation methods available in [step hooks](/reference/hooks#the-dromo-uploader-instance).

  All uploader methods return `Promise<void>` — await them when order matters.
</ResponseField>

***

## Registering custom steps

### JavaScript / TypeScript

```typescript theme={null}
dromo.registerCustomStep(config): void
```

Call `registerCustomStep` on the `DromoUploader` instance before `open()`. Multiple calls at the same `insertAfter` position are presented in registration order.

### React

Pass an array of config objects to the `customSteps` prop:

```tsx theme={null}
<DromoUploader
  customSteps={[
    {
      id: "terms",
      insertAfter: "SELECT_MATCH",
      label: "Terms & Conditions",
      url: "https://your-app.com/terms-step.html",
    },
  ]}
  {/* other required props */}
>
  Import
</DromoUploader>
```

### Angular

Bind a config array to the `[customSteps]` input on `lib-dromo-uploader`:

```typescript theme={null}
customSteps = [
  {
    id: "terms",
    insertAfter: "SELECT_MATCH",
    label: "Terms & Conditions",
    url: "https://your-app.com/terms-step.html",
  },
];
```

```html theme={null}
<lib-dromo-uploader [customSteps]="customSteps" ...>
  Import with Dromo!
</lib-dromo-uploader>
```

***

## postMessage API

Your iframe page communicates with Dromo by posting messages to `window.parent`.

### Navigation messages

<ResponseField name="{ type: &#x22;DROMO_NEXT&#x22; }">
  Advance to the next step.
</ResponseField>

<ResponseField name="{ type: &#x22;DROMO_BACK&#x22; }">
  Return to the previous step.
</ResponseField>

<ResponseField name="{ type: &#x22;DROMO_MESSAGE&#x22;, payload: any }">
  Trigger the `onMessage` callback with the uploader instance and `payload`. Does not automatically navigate — post `DROMO_NEXT` separately when ready.
</ResponseField>

***

## Uploader methods in `onMessage`

The `uploader` instance passed to `onMessage` exposes the following methods. These are the same mutation methods available in [step hooks](/reference/hooks#the-dromo-uploader-instance).

<ResponseField name="addField(field, position?) => Promise<void>">
  Dynamically add a field to the import schema. See [`instance.addField`](/reference/hooks#instanceaddfield).
</ResponseField>

<ResponseField name="removeField(key) => Promise<void>">
  Remove a field from the schema. See [`instance.removeField`](/reference/hooks#instanceremovefield).
</ResponseField>

<ResponseField name="addRows(rows) => Promise<string[]>">
  Inject rows into the dataset. Returns the IDs of the added rows. See [`instance.addRows`](/reference/hooks#instanceaddrows).
</ResponseField>

<ResponseField name="removeRows(ids) => Promise<void>">
  Remove rows from the dataset by ID. See [`instance.removeRows`](/reference/hooks#instanceremoverows).
</ResponseField>

<ResponseField name="setUser(user) => Promise<void>">
  Update the current user associated with the import.
</ResponseField>
