Composition
Compose DomainKit React parts with Base UI render props, built-in provider marks, host overrides, and CSS tokens that match your product.
Complete flows are conveniences over semantic parts. Every public part accepts Base UI’s render prop plus state-aware className and style values. Connection.Flow and Connection.ConnectTrigger are the styled recipes. Connection.Trigger only opens the dialog; its required children supply the label and render uses a host button.
<Connection.OAuthAction
controller={controller}
label="Connect"
render={<MyButton variant="primary" />}
/>
<Connection.Root status={state._tag}>
<HostCard>
<Provider.Mark provider={snapshot.provider} />
<p>Manages DNS for this domain.</p>
<BaseDialog.Root>
<Connection.Trigger render={<HostButton />}>
Connect {snapshot.provider.name}
</Connection.Trigger>
<Connection.Dialog controller={controller} snapshot={snapshot} />
</BaseDialog.Root>
</HostCard>
</Connection.Root>
Theming
<DomainKit.Root
colorScheme="inherit"
icons={{ copy: <Copy />, copied: <Check />, download: <Download /> }}
marks={{ internaldns: <InternalDnsMark /> }}
messages={{ connectProvider: (name) => `Connect ${name}` }}
theme={{ accent: "var(--brand-primary)", radius: "var(--radius-md)" }}
transport={transport}
>
{children}
</DomainKit.Root>
The opt-in stylesheet reads --domainkit-* variables. Theme props set those variables without replacing the host font, palette, or icon system.
Cloudflare and Vercel use bundled, theme-aware marks sourced from SVGL. Other recognized providers
load a remote mark and fall back to the provider’s first letter when unavailable. Use marks when
your product needs to replace a built-in mark or supply branding for a private provider.
Host notifications
DomainKit emits structured lifecycle events after user-initiated mutations. Keep notification UI in the host application and map the events to Sonner, another toast system, telemetry, or nothing at all:
<DomainKit.Root
onEvent={(event) => {
switch (event._tag) {
case "ConnectionEstablished":
toast.success(`${event.connection.provider.name} connected`);
break;
case "RecordsApplied":
toast.success("DNS records added");
break;
case "RecordsCleaned":
toast.success("DNS records removed");
break;
case "DomainDisconnected":
toast.success("Domain disconnected");
break;
}
}}
transport={transport}
>
{children}
</DomainKit.Root>
Partial apply and cleanup results have their own events, so hosts can use warning notifications instead of reporting partial work as success. Initial inspection and passive verification do not emit notification events.
The default mutation dialogs close immediately after a complete success. Partial results and failures remain in context because they require user attention; transient success feedback belongs to the host notification surface.
Records from parts
<Records.Root>
<Records.Header>
<Records.Row>
<Records.Head scope="col">Type</Records.Head>
<Records.Head scope="col">Name</Records.Head>
<Records.Head scope="col">Value</Records.Head>
</Records.Row>
</Records.Header>
<Records.Body>
{records.map((record) => (
<Records.Row key={record.id}>
<Records.Cell>{record.type}</Records.Cell>
<Records.Cell>
<Records.CopyValue value={record.name} />
</Records.Cell>
<Records.Cell>
<Records.CopyValue value={record.value} />
</Records.Cell>
</Records.Row>
))}
</Records.Body>
</Records.Root>
Presentational record parts accept browser-safe records directly and do not require DomainKit.Root or a transport.
Effect Atom lifecycle models
Every stateful lifecycle exposes the atoms used by its packaged controller and flow:
const model = Provisioning.useModel(connection, records);
const state = useAtomValue(model.state);
const command = useAtomSet(model.command);
if (state._tag === "Review") {
return (
<Operations.Root lifecycle="provisioning">
{state.plan.operations.map((operation) => (
<Operations.Item key={operation.id} operation={operation} />
))}
<button onClick={() => command(Provisioning.Command.Apply())}>Apply approved plan</button>
</Operations.Root>
);
}
The same model seam is available as Connection.useModel, Verification.useModel, and
Cleanup.useModel. Operations.List is the complete neutral operation recipe; its exported parts
support Base UI render elements, forwarded refs, state-aware classes, and host event handlers.