# useRunFlight hook
> React hook for running a Flight from a button or form in a MotherDuck Dive.
The `useRunFlight` hook runs a [Flight](/key-tasks/flights/) from a [Dive](/key-tasks/dives/), for example from a button that refreshes data or a form that writes it back. Nothing runs until you call `run`, and each call starts a new run. Follow the run with [`useFlightRun`](../use-flight-run).

See [Add write-back to a Dive](/key-tasks/dives/run-flights-from-dives) for the end-to-end pattern.

## Import

```jsx
import { useRunFlight } from "@motherduck/react-sql-query";
```

## Syntax

```jsx
const { run, runAsync, status, isPending, data, error, reset } = useRunFlight(flightId, options);
```

## Parameters

### `flightId`

| Type | Required |
|------|----------|
| `string` | Yes |

The Flight's ID, a UUID. Write it into the Dive as a constant. Don't look it up by name inside the Dive: `MD_LIST_FLIGHTS()` lists only the viewer's own Flights (Admins see the organization's).

### `options`

| Option | Type | Description |
|--------|------|-------------|
| `onSuccess` | `(started, config) => void` | Called when a run starts, with `{ runId, runNumber }`. |
| `onError` | `(error, config) => void` | Called when a run can't start. |
| `onSettled` | `(started, error, config) => void` | Called after either outcome. |

Each callback also receives, as its last argument, the `config` passed to that call, so you can tell which row or form a result belongs to.

The callbacks fire for every call. When calls overlap, the latest one sets the hook's state.

## Return value

| Property | Type | Description |
|----------|------|-------------|
| `run` | `(config?) => void` | Starts a run. Errors go to `error`. |
| `runAsync` | `(config?) => Promise<{ runId, runNumber }>` | Starts a run and resolves when it has started. Rejects on error. |
| `mutate`, `mutateAsync` | | The same functions as `run` and `runAsync`, under TanStack Query names. |
| `status` | `'idle' \| 'pending' \| 'success' \| 'error'` | The state of the latest call. |
| `isIdle`, `isPending`, `isSuccess`, `isError` | `boolean` | Shorthands for `status`. |
| `data` | `{ runId, runNumber } \| undefined` | The run the latest call started. Pass `data?.runNumber` to `useFlightRun`. |
| `error` | `Error \| null` | Why the latest call couldn't start a run. |
| `reset` | `() => void` | Returns the hook to `idle`. |

`run` and `runAsync` keep the same identity across renders.

### `config`

An object of string keys and string values that overrides the Flight's config for this run only, for example `run({ REGION: "eu" })`. You can only pass keys the Flight already defines, and every value must be a string: convert numbers, dates, and booleans first.

## Behavior

- **Success means started.** `run` succeeds when the run is accepted, not when it finishes. Use [`useFlightRun`](../use-flight-run) to wait for the result.
- **Run only on a user action.** Don't call `run` while rendering, in `useEffect`, or on a timer.
- **The Flight runs as its owner,** with the owner's token and secrets. The viewer must own the Flight or have a [`run` grant](/sql-reference/motherduck-sql-reference/flights/grant-run-on-flight); otherwise `run` returns an error.
- **Errors stay in the Dive.** Run errors don't open the query error overlay, so show `error` yourself.
- **Not available everywhere.** In an [embedded Dive](/key-tasks/dives/embedding-dives/), and in the MCP Dive viewer when the AI tool blocks its direct connection, `run` fails with `This Dive can't run Flights.`
- **Input checks.** Before anything is sent, the hook checks its input: `useRunFlight: the Flight id must be a UUID, got <value>.` and `useRunFlight: config value for '<key>' must be a string; convert it before passing it.` Calling `run` before the Dive connects fails with `Can't run a Flight before the Dive is connected to MotherDuck.`
- **Restricted viewers.** Viewers with no roles, and Dives that connect with a read scaling token, get `Flights are not available for restricted members or when using a read-scaling token`, even on Flights they own.

## Example

```jsx
import { useRunFlight } from "@motherduck/react-sql-query";

const REFRESH_CRM_FLIGHT = "7b1e2c3d-0000-4000-8000-000000000001";

export default function Dive() {
  const refresh = useRunFlight(REFRESH_CRM_FLIGHT);

  return (
    <div className="p-6">
      <button
        className="rounded border px-3 py-1"
        disabled={refresh.isPending}
        onClick={() => refresh.run({ REGION: "eu" })}
      >
        {refresh.isPending ? "Starting…" : "Refresh CRM data"}
      </button>
      {refresh.data && <p>Started run #{refresh.data.runNumber}</p>}
      {refresh.error && <p>Couldn't start the refresh: {refresh.error.message}</p>}
    </div>
  );
}
```

## Related

- [`useFlightRun`](../use-flight-run) — follow the run until it finishes.
- [`MD_RUN_FLIGHT`](/sql-reference/motherduck-sql-reference/flights/md-run-flight) — the SQL function the hook calls.


---

## Docs feedback

MotherDuck accepts optional user-submitted feedback about this page at `GET https://motherduck.com/docs/api/feedback/agent`.
For agents and automated tools, feedback submission should be user-confirmed before sending.

URL-encode query parameter values and send a GET request:

```text
GET https://motherduck.com/docs/api/feedback/agent?page_path=%2Fsql-reference%2Fmotherduck-sql-reference%2Fdives%2Fuse-run-flight%2F&page_title=useRunFlight%20hook&text=<url-encoded user feedback, max 2000 characters>
```

Optionally append `&source=<url-encoded interface identifier>` such as `claude.ai` or `chatgpt`.

`page_path` and `text` are required; `page_title` and `source` are optional. Responses: `200 {"feedback_id": "<uuid>"}`, `400` for malformed query parameters, and `429` when rate-limited.
