@dodomain/react
The React bindings — a useDoDomainConnect hook (and a drop-in button) around the @dodomain/connect modal widget.
npm install @dodomain/reactReact bindings for the embeddable widget. The widget's own API is imperative — showDoDomain() opens a modal iframe and returns a close handle — so this package's shape is a hook that owns that handle's lifecycle, not a pseudo-declarative modal component. It ships dual ESM + CJS builds with self-contained type declarations, requires React 18+ (peer dependency), and its only runtime dependency is @dodomain/connect itself — the widget is not bundled twice, and both packages stay free of any other runtime dependency.
import { useDoDomainConnect, MOUNT_BLOCKED } from "@dodomain/react";
function ConnectDomainCard({ token }: { token: string }) {
// token comes from your server: sessions.create() via @dodomain/node
const { open, isOpen } = useDoDomainConnect({
token,
onVerified: ({ domain }) => refetchDomains(),
onClose: ({ state }) => {
if (state !== "verified") keepThePromptVisible();
},
onError: (err) => {
// The iframe never mounted (host CSP, network, content blocker):
// send the user to the same session full-page instead.
if (err.code === MOUNT_BLOCKED) location.assign(err.hostedUrl);
},
});
return (
<button onClick={open} disabled={isOpen}>
Connect your domain
</button>
);
}The session token is the only credential involved — the widget never sees your dd_sk_... key, exactly as with plain @dodomain/connect.
What the hook adds
Beyond forwarding the call to showDoDomain(), the hook carries three guarantees a hand-rolled click handler tends to get wrong:
- Latest-props callbacks. The widget receives stable wrapper callbacks that read the current render's handlers at fire time, so a re-render while the modal is open never strands a stale closure. Inline arrow handlers are fine — no
useCallbackneeded. - One modal at a time.
open()while the modal is already open is a no-op, so a double-click cannot stack two backdrops. - Unmount cleanup. If the owning component unmounts while the modal is open, the modal is torn down through the widget's own close path —
onClosestill fires with the last-known state — instead of orphaning a full-screen iframe over the page. StrictMode's dev-only double-mount cycle is handled.
useDoDomainConnect(options)
options is exactly the widget's ShowDoDomainOptions — the same fields, semantics and defaults documented on the widget page:
| Option | Type | Notes |
|---|---|---|
token | string — required | Session token (dd_sess_...) your server minted. |
baseUrl | string | DoDomain origin. Defaults to https://app.dodomain.io. |
theme | "light" | "dark" | Pass the theme your page is rendering so the sheet matches it. |
onVerified | (detail: { domain?: string }) => void | Fires when the domain verifies. |
onClose | (detail: DoDomainCloseDetail) => void | Fires on every dismissal, with the session's last-known state. |
onError | (detail: DoDomainWidgetError) => void | Fires when the flow fails to load or reports a session error. |
loadTimeoutMs | number | Load-handshake timeout before the embed counts as failed. Default 15000. |
Returns:
| Field | Type | Notes |
|---|---|---|
open | () => void | Opens the modal with the current render's options. No-op while open. Browser-only — call it from an event handler. |
close | () => void | Dismisses programmatically — fires onClose with the last-known state, exactly like the widget's own handle.close(). |
isOpen | boolean | Whether the modal is currently mounted. |
The onVerified callback remains a UI cue: the signed connection.verified webhook is the source of truth, as everywhere else.
<DoDomainConnectButton />
When all you need is a trigger, the drop-in — a plain unstyled <button type="button"> wired to the hook:
import { DoDomainConnectButton } from "@dodomain/react";
<DoDomainConnectButton token={token} onVerified={() => refetchDomains()} className="btn">
Connect your domain
</DoDomainConnectButton>;It takes every hook option plus children (label, defaults to "Connect your domain"), className and disabled, and disables itself while the modal is open. For anything beyond a button that opens the flow, use the hook directly.
Re-exported vocabulary
MOUNT_BLOCKED and the widget's types — ShowDoDomainOptions, DoDomainWidgetError, DoDomainCloseDetail, DoDomainSessionState, DoDomainHandle — are re-exported, so a React app needs only this one import. Their semantics (origin-checked messages, the MOUNT_BLOCKED hosted-URL fallback, the close-state table, the frame-src https://app.dodomain.io CSP requirement) are documented on the widget page and apply unchanged.
dodomain-sdk (Python)
The Python server SDK — sync and async twins over the same /api/v1 surface, typed models, an exception hierarchy, and the webhook verifier.
Testing your integration
Drive the hosted connect flow from your own Playwright suite — the supported automation path, what needs real DNS, and how to assert the result without one.