React component reference
Package: @bootstrapware/onboard
Styles: @bootstrapware/onboard/styles.css
Import styles once near the feature. Use a Client Component in Next.js App Router. Bootstrapware does not authenticate checklist end users. Pass user from your session and workspaceKey for shared steps.
Props
| Prop | Type | Required | Notes |
|---|---|---|---|
| user | { id: string; name?: string } | yes | Opaque stable id. Never invent this in production. Wire the real session id. |
| workspaceKey | string | shared steps | Scopes workspace progress. Bind it on Hosted author tokens when you mint them. |
| adapter | OnboardAdapter | BYO / local | If set, progress stays on your adapter (or createLocalAdapter). Hosted config still loads when flowId + publishableKey are also set. |
| items | OnboardChecklistItem[] | Path A | Local checklist. Hosted/BYO typically load items from published flow config. Optional per-item href and ctaLabel. |
| config | Partial<OnboardFlowConfig> | no | Local merge until hosted config loads. Do not put flowId/items here. |
| flowId | string | Hosted / BYO config | With publishableKey. Prefix flw_. |
| publishableKey | string | hosted config | Browser-safe. Never put secret keys in the client. |
| apiBaseUrl | string | no | Defaults to https://onboard.bootstrapware.co |
| context | OnboardContext | no | Role, plan, flags for eligibility. Evaluated in the widget. Never send this through MCP. |
| facts | Record<string, boolean | "n/a"> | no | Sticky reconciliation on mount. true completes; "n/a" hides as inapplicable; false leaves stored progress. Also call completeStep from the host when the user actually does the work. |
| authorToken | string | live Hosted | Short-lived host-signed assertion from POST /api/v1/author-tokens (secret key). Required when the flow sets requireAuthorToken. Live Hosted without a token is test keys only. Your BFF renews it; the widget never mints. |
| permissions | OnboardTokenPermission[] | no | Defaults to complete, skip, dismiss, snooze. Include reset (and mint it) to show Reset my progress. Reset is personal scope only. |
| onEvent | (event, payload) => void | BYO side effects | Analytics / CRM. BYO does not emit Bootstrapware webhooks. Payload carries ids, not host context. |
| theme | "light" | "dark" | no | Omit for the light cream default. No data-theme and no system theme. |
| presentation | "section" | "modal" | "window" | "launcher" | no | Default section (inline). Modal, window, and launcher start closed unless defaultOpen. Escape, the backdrop, or Hide closes modal and launcher. Escape or Hide closes a window. |
| defaultOpen | boolean | no | Open modal, window, or launcher on first render. Ignored for section. |
| focusedAnchor | string | no | Spotlight the item whose anchor matches. Host opt-in; separate from the automatic in-list next-step highlight. |
| className | string | no | Wrapper class for layout. |
Theme
theme is "light" | "dark". Omit it and the checklist stays the light cream default: the root has no data-theme attribute. theme="light" sets data-theme="light" and keeps those light tokens. theme="dark" sets data-theme="dark" and applies the dark shell. v1 does not follow prefers-color-scheme or the system theme.
Hosts may set these CSS custom properties on the embed root (.bsw-ob). These overrides are the supported API. Do not fight .bsw-* classes. When theme="dark" is set, target .bsw-ob[data-theme="dark"] so your values win over the dark defaults.
- --bsw-ob-bg
- --bsw-ob-panel
- --bsw-ob-fg
- --bsw-ob-muted
- --bsw-ob-line
- --bsw-ob-ok
- --bsw-ob-error
- --bsw-ob-accent
- --bsw-ob-accent-fg
- --bsw-ob-radius
.bsw-ob[data-theme="dark"] {
--bsw-ob-bg: #161714;
--bsw-ob-panel: #22241c;
--bsw-ob-fg: #f4f4ee;
--bsw-ob-muted: #b3b4aa;
--bsw-ob-line: #4a4c42;
--bsw-ob-ok: #8dcea0;
--bsw-ob-error: #ffb4be;
--bsw-ob-accent: #c8ef00;
--bsw-ob-accent-fg: #161714;
--bsw-ob-radius: 12px;
}branding.primaryColor, when set, writes --bsw-ob-accent inline on the root and overrides the theme accent. className is for layout, not a second skin.
Next step and item actions
The first incomplete unblocked item is highlighted as the next step in the list. This is separate from focusedAnchor Spotlight, which stays host opt-in.
- Incomplete unblocked items with
href: the primary control is a button-styled link. Label is optionalctaLabelon the item, otherwise Continue. Mark complete is secondary. - Items without
href: Mark complete stays the primary button. - Blocked items show “Complete … first” and no primary CTA.
- Clicking the href CTA does not mark the step complete. Call
completeStepfrom the host on real actions and passfactsso the widget reconciles on mount. - The widget only follows
http(s), hash (#…), and relative path hrefs.javascript:,data:, and protocol-relative URLs are ignored (Mark complete stays primary).
Resolve order
- Path A — local:
createLocalAdapter(or any adapter) plus optionalitems. Zero keys. Free forever. - Path B — Hosted: omit adapter; pass
flowId+publishableKey. Progress lives on Bootstrapware. Live writes needauthorToken. - Path C — BYO: pass adapter and
flowId+publishableKeyso published config still loads. Progress stays on your backend.
Local / demo
import { Onboard, createLocalAdapter, SAAS_FIRST_RUN_ITEMS } from "@bootstrapware/onboard";
import "@bootstrapware/onboard/styles.css";
<Onboard
user={{ id: session.user.id, name: session.user.name }}
workspaceKey={workspace.id}
context={{ role: membership.role, plan: workspace.plan }}
facts={{ hasBilling: billingConnected }}
items={SAAS_FIRST_RUN_ITEMS}
adapter={createLocalAdapter({ storageKey: "demo-onboard" })}
/>Call completeStep from your app on real actions and pass facts so the widget reconciles on mount. Dismiss hides without 100% progress. Snooze tucks the widget away (default 1 and 7 days).
Hosted
<Onboard
flowId="flw_..."
publishableKey={process.env.NEXT_PUBLIC_BSW_ONBOARD_PUBLISHABLE_KEY}
user={{ id: session.user.id, name: session.user.name }}
workspaceKey={workspace.id}
context={{ role: membership.role, plan: workspace.plan }}
facts={integrationFacts}
authorToken={authorTokenFromBff}
/>Live config fetch needs BYO ($9.99) or Hosted ($19.99). Live Hosted writes need Hosted ($19.99). Test publishable keys work unpaid. Auto-renew authorToken from your BFF when expiresAt is within 5 minutes.
Limits in the UI
32 items per flow. 16 eligibility flags per item. See limits.
Related: Configuration · Modes · Adapter · Identity · Facts · Quickstart