React component reference

Package: @bootstrapware/feedback
Styles: @bootstrapware/feedback/styles.css

Import styles once near the feature. Use a Client Component in Next.js App Router. Bootstrapware does not authenticate board end users, pass user from your session.

Props

PropTypeRequiredNotes
userFeedbackAuthor | nullrecommendedOpaque stable id. Optional name, email, avatarUrl for display.
adapterFeedbackAdapterBYO / localIf set, wins over Hosted. Use createLocalAdapter or your own API.
boardIdstringHosted / BYO configWith publishableKey for Hosted. Defaults to brd_local for local adapter.
publishableKeystringHostedBrowser-safe. Never put secret keys in the client.
apiBaseUrlstringnoDefaults to https://feedback.bootstrapware.co
postsFeedbackPost[]noSeed posts for createLocalAdapter / demos.
configPartial<FeedbackBoardConfig>noOverrides toggles / empty state until Hosted config loads.
theme"light" | "dark"noOmit for the light cream default. No data-theme and no system theme.
presentation"section" | "modal" | "window" | "launcher"noDefault section (inline). Modal, window, and launcher start closed unless defaultOpen. Escape closes them. The backdrop closes modal and launcher.
defaultOpenbooleannoOpen modal, window, or launcher on first render. Ignored for section.
focusPostIdstringnoPin and focus this post on load. Also honors #bsw-fb-post-<id> on the host page. Fetches via listPosts({ postId }) when the post is not on the first page.
authorTokenstringnoShort-lived host-signed assertion from POST /api/v1/author-tokens (secret key). Required when the board sets requireAuthorToken.
classNamestringnoWrapper class for layout.

Theme

theme is "light" | "dark". Omit it and the board 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-fb). These overrides are the supported API. Do not fight .bsw-* classes. When theme="dark" is set, target .bsw-fb[data-theme="dark"] so your values win over the dark defaults.

  • --bsw-fb-bg
  • --bsw-fb-panel
  • --bsw-fb-fg
  • --bsw-fb-muted
  • --bsw-fb-line
  • --bsw-fb-accent
  • --bsw-fb-accent-fg
  • --bsw-fb-radius
.bsw-fb[data-theme="dark"] {
  --bsw-fb-bg: #161714;
  --bsw-fb-panel: #22241c;
  --bsw-fb-fg: #f4f4ee;
  --bsw-fb-muted: #b3b4aa;
  --bsw-fb-line: #4a4c42;
  --bsw-fb-accent: #c8ef00;
  --bsw-fb-accent-fg: #161714;
  --bsw-fb-radius: 12px;
}

branding.primaryColor, when set, writes --bsw-fb-accent inline on the root and overrides the theme accent. className is for layout, not a second skin.

Resolve order

  1. If adapter is passed, use it (BYO or createLocalAdapter).
  2. Else if both boardId and publishableKey are set, use Hosted (createHostedAdapter internally).
  3. Else fall back to createLocalAdapter (demo / free).

Local / demo

import { Feedback, createLocalAdapter } from "@bootstrapware/feedback";
import "@bootstrapware/feedback/styles.css";

<Feedback
  user={{ id: "user_1", name: "Ada" }}
  adapter={createLocalAdapter({ storageKey: "demo-feedback" })}
/>

Hosted

<Feedback
  boardId="brd_..."
  publishableKey="bsw_live_pub_..."
  user={currentUser}
/>

Live config fetch needs BYO ($9.99) or Hosted ($19.99). Live Hosted writes need Hosted ($19.99). Test publishable keys work unpaid.

Limits in the UI

Title max 200 characters. Body max 5000. Statuses: open | planned | in_progress | shipped | declined. The list is 50 posts per page; Load more appears when the adapter returns nextCursor. See limits.

Similar requests

When the compose title is at least 4 characters, the widget looks for similar posts. Hosted and BYO debounce listPosts({ q }) so matches are not limited to the currently loaded page. Local mode uses in-memory substring/token overlap (no network search). Up to 3 clickable results jump to the existing post and focus Vote. Not embeddings or fuzzy NLP.

Author edit

When the adapter implements optional updatePost, the author sees Edit on their own post while its status is open. Save sends the corrected title and body with the same user.id and authorToken as submit. After staff moves the status, Edit is hidden and the text stays locked. Adapters that omit updatePost hide Edit. The control does not change status. On Local and BYO, that form also shows Remove image and Replace image. Omit imageUrl and the screenshot stays. Null clears it. A jpeg, png, or webp data URL, or an http(s) URL, replaces it (data URLs, 4 MB). Hosted hides those controls and ignores imageUrl.

When the adapter implements optional withdrawPost, the author sees Withdraw on their own open request, then Confirm withdraw. That archives the row (archivedAt). It is not a hard delete. Default lists hide it. The Hosted inbox can still restore it. Omit withdrawPost and Withdraw stays hidden. A different author, a non-open status, and an archived or merged post are rejected.

Related: Modes · Adapter · Identity · Quickstart · Limits