---
title: "@domainkit/react"
description: The React namespaces, the flow hook and what it returns, the controllers and their states, the message catalog, and the read-only contract.
sidebar:
  label: "@domainkit/react"
seo:
  title: DomainKit React API reference
---

```tsx
import { Domain, DomainKit } from "@domainkit/react";
```

The package entry carries `"use client"`. It exports hooks and helpers and renders no element of its
own; the styled composition is the [registry](/components/registry).

{/* reference-inventory:start */}

## Namespaces

| Namespace   | Carries                                                                                                                                                                                                                                                                                                          |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DomainKit` | `Root`, `ReadOnly`, `useDomainKit`, `useTransport`, `useMessages`, `useCapabilities`, `useReadOnly`                                                                                                                                                                                                              |
| `Domain`    | `useFlow`, and the `Flow` and `FlowState` shapes it returns                                                                                                                                                                                                                                                      |
| `Connect`   | `useController`, `useRecovery`, `useZones`, `useAccounts`, `useDomainField`, `describeMethods`, `rejectedField`, `attempted`, `offering`, `holdsConnection`, `reconnect`, `hostProvider`, `providerOf`, `displayName`, `reusableConnections`, `answeredInPlace`, `placementOf`, `suggestionsFor`, `completionOf` |
| `Provision` | `useController`, `State`, `pendingPlan`, `planOf`                                                                                                                                                                                                                                                                |
| `Cleanup`   | The same set over a plan built from an apply receipt                                                                                                                                                                                                                                                             |
| `Verify`    | `useController`, `State`, `valuesOf`, `summary`, `Supplied`, and the readiness shapes it reports                                                                                                                                                                                                                 |
| `Records`   | `standingOf`, `useCopy`, `copyText`, `toZoneFile`, `downloadZoneFile`, `identity`, `requirementsKey`                                                                                                                                                                                                             |
| `Outcome`   | `describe(error, catalog)` and `useDescribe()`, which binds the catalog the root holds                                                                                                                                                                                                                           |
| `Messages`  | `Catalog`, `english`, `merge`, `outcome(error, catalog)`, and `failure(error, catalog)`                                                                                                                                                                                                                          |
| `Testing`   | `transport`, re-exported from `domainkit/testing` for UI tests                                                                                                                                                                                                                                                   |

## Top-level exports

| Export     | Carries                                                                                                             |
| ---------- | ------------------------------------------------------------------------------------------------------------------- |
| `Event`    | What a customer finished doing: `Connected`, `Applied`, `Cleaned`, `Declined`, `Failed`, `Detached`, `Disconnected` |
| `Listener` | The `onEvent` signature `DomainKit.Root` takes                                                                      |
| `Failure`  | The failed member every controller state shares, carrying the `DomainKit.Error`                                     |

{/* reference-inventory:end */}

## Root

<Snippet
  file="examples/react/domain-settings.tsx"
  region="flow"
  title="The provider and the flow"
/>

`DomainKit.Root` renders a context provider and no element.

| Prop        | Effect                                                                               |
| ----------- | ------------------------------------------------------------------------------------ |
| `transport` | The transport, by value; its identity survives re-renders                            |
| `revision`  | Bump to re-inspect every mounted domain after a change the UI did not make           |
| `messages`  | Overrides on `Messages.Catalog`                                                      |
| `navigate`  | Where an interactive provider flow sends the customer. Defaults to `window.location` |
| `onEvent`   | A `Listener` for finished steps                                                      |
| `readOnly`  | Report the state without the commands that change it                                 |

## The flow

`Domain.useFlow({ domain, requirements, connect?, onApplied?, onCleaned?, returnTo?, readOnly?, verification? })`
runs the four controllers together, attaches nothing the customer has not granted, and plans as soon
as the domain is attached with nothing applied. An apply observes the domain again, and an applied
domain plans again once an observation made after the apply reads a record back missing or wrong, so
records deleted at the provider are offered again.

| Field                     | Carries                                                              |
| ------------------------- | -------------------------------------------------------------------- |
| `state`                   | `FlowState`, below                                                   |
| `connection`              | The connect controller                                               |
| `provisioning`, `cleanup` | The plan, approve, apply machine for adding and for removing         |
| `verification`            | The observer, with the readiness it last read                        |
| `plan`                    | The plan still awaiting its apply, which is what the rows report     |
| `readiness`               | The last observation, which is what the rows report after an apply   |
| `capabilities`            | The groups the transport declares, so nothing offers a step it lacks |
| `requirements`, `domain`  | What the host asked for, and for which domain                        |
| `invitation`              | What the flow was told to offer when discovery names no host         |

## Controllers

Each takes one options object and returns a controller whose `state` is a tagged union.

| Hook                      | Options                                           | State tags                                                                                                       |
| ------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `Connect.useController`   | `{ domain, returnTo?, readOnly? }`                | `Loading`, `Disconnected`, `Connected`, `Reconnect`, `Submitting`, `Redirecting`, `SelectionRequired`, `Failure` |
| `Connect.useZones`        | `{ provider? }`                                   | `Loading`, `Ready`, `Failure`                                                                                    |
| `Connect.useAccounts`     | `{ returnTo?, readOnly? }`                        | `Loading`, `Ready`, `Submitting`, `Redirecting`, `Failure`                                                       |
| `Provision.useController` | `{ domain, requirements, onApplied?, readOnly? }` | `Idle`, `Planning`, `Planned`, `Approving`, `Applying`, `Applied`, `Rejecting`, `Rejected`, `Failure`            |
| `Cleanup.useController`   | `{ domain, receiptId?, onCleaned?, readOnly? }`   | The same set                                                                                                     |
| `Verify.useController`    | `{ domain, polling?, requirements?, supplied? }`  | `Idle`, `Observing`, `Observed`, `Failure`                                                                       |

<Snippet file="examples/react/controllers.tsx" region="hooks" title="Composing the flow yourself" />

## Callback recovery

`Connect.useRecovery()` returns `Connect.Recovery`: an `outcome` of
`CallbackRecovery.Model | null` and `dismiss()`. It reads the current URL through the
[shared codec](/docs/reference/transport#callback-recovery) and renders no markup. Server rendering
and the initial hydration render return `null`; the browser supplies the hint after hydration.

<Snippet
  file="examples/react/callback-recovery.tsx"
  region="recovery"
  title="Offer a fresh connection attempt"
/>

`dismiss()` removes all recovery parameters with `history.replaceState`, preserving the host's
history state, unrelated query parameters, and fragment without navigation. Mounted readers update
together. Browser history and hash navigation update the reader; a host router that changes history
without emitting an event must rerender the consumer.

For `cancelled` and `restart`, start a fresh attempt through the existing controller's
`connect({ provider, method })`. When `outcome.connectionId` is present, call
`reconnect({ connectionId, provider, method })` instead so the existing account and its domains
keep their connection. Both `Connect.useController` and `Connect.useAccounts` expose this command;
`Connect.ReconnectInput` names the domain controller input. Dismiss removes the visible hint. Controllers also strip all recovery parameters when deriving
the default `returnTo` from the current URL, including when another control starts the attempt.
The current page and host-supplied return destinations are unchanged.
For `inspect`, check the account list through the authenticated transport before offering further
action: persistence can be partial. The hint neither retries nor replays a provider grant.

`Messages.Catalog` supplies `recoveryCancelled(provider)`, `recoveryRestart(provider)`, and
`recoveryInspect(provider)`, each with a title and description, plus `dismissRecovery` and `retry`.
The [domain flow](/components/registry/domain-flow) and
[domain field](/components/registry/domain-field) render these alerts with your kit.

## The domain field

`Connect.useDomainField({ zones, value, onChange, onResolve?, id? })` carries the combobox
semantics: `inputProps`, `listboxProps`, and `optionProps(zone)`, the `suggestions` the typed value
reaches for, the `highlighted` one, the `found` placement, `complete(zone)`, and `close()`.

`Connect.suggestionsFor` offers the zones that already hold the typed name, matched on whole labels
and listed deepest first. Otherwise it offers a zone the value is still typing.
`Connect.completionOf` keeps the typed subdomain in front of the chosen zone, including when that
zone already holds the name.

<Snippet file="examples/react/add-domain.tsx" region="field" title="The zone picker" />

## Flow state

| Field                    | Carries                                                                       |
| ------------------------ | ----------------------------------------------------------------------------- |
| `connection`             | `Connect.State["_tag"]`                                                       |
| `provisioning`           | `Provision.State["_tag"]`                                                     |
| `connected` / `offering` | Whether DomainKit holds a connection, and whether it is offering one          |
| `provider`               | Who holds the connection, or whose nameservers serve the zone                 |
| `label`                  | The account the records go to, from the attachment; `null` until there is one |
| `receiptId` / `applied`  | The apply receipt this domain produced, and whether there is one              |
| `readOnly`               | The customer may read this domain but not write to it                         |

## Read-only

`readOnly` refuses commands that change the domain: connect, reconnect, attach, detach, disconnect,
plan, approve, decline, apply, and provisioning or cleanup `retry`. The refusal is in the controller,
so rendering a control anyway cannot send those commands to the transport. Connection `retry`
re-inspects instead of repeating a write; verification `observe` and `retry` stay available.

The connection, account, provisioning, and cleanup controllers accept `readOnly`. The verification
controller has no `readOnly` option. Its `supplied` option takes host-owned readiness and an optional
`observe` callback, which both `observe` and `retry` call; without that callback, both do nothing.
The flow accepts the same value as `verification`; see
[readiness the host holds](/docs/react/composition#readiness-the-host-holds).

| Export                  | Answers                                                          |
| ----------------------- | ---------------------------------------------------------------- |
| `DomainKit.useReadOnly` | Whether this surface may offer a control that changes the domain |
| `DomainKit.ReadOnly`    | Narrows one subtree, overriding the root for its children        |

<Snippet file="examples/react/read-only.tsx" region="hook" title="Reading the mode" />

It is not capability gating. A group the transport never declares is absent from
`flow.capabilities`; `readOnly` covers the authorization a transport cannot express, such as a
member who reaches the same routes and would get a 403 from any write. Where a connect control would
be, the catalog's `administratorConnects(provider)` sentence takes its place.

## Words

`Messages.Catalog` holds every user-visible string, including a title and a description per
`DomainKit.Error` reason. `Outcome.useDescribe()` binds the merged catalog and returns that pair for
one error; `Outcome.describe(error, catalog, context?)` is the same answer without a React context.

<Snippet file="examples/react/controllers.tsx" region="failure" title="Rendering a failure" />
