Skip to documentation
BoobstrapDocs
Interactivity

React adapter

Compose Boobstrap’s visual system with SSR-safe headless hooks while React owns state, markup, refs, and lifecycle.

React 18–19Headless hooksv0.7.0 current
React integration

Install and import

Install the core framework, React adapter, and React peer together. Import the compiled stylesheet once at your application entry point, then import only the hooks used by a component.

Terminal · React packages
npm install @boobstrap/boobstrap @boobstrap/react react react-dom
JSX · Application entry
import "@boobstrap/boobstrap/dist/boobstrap.css";
import { useCollapse } from "@boobstrap/react";
Use one behavior owner. Do not call initBoobstrap on React-owned markup. The hook supplies state, ARIA attributes, event handlers, and refs; your JSX supplies the semantic elements and bs-* classes.

Compose a component

Prop getters merge consumer handlers with the adapter contract. The preview is the rendered result of the JSX directly below it: the hook supplies the button type, relationship, expanded state, panel ID, visibility, and public state while your component supplies the content and classes.

Profile and security settings.
JSX · Collapse hook
import { useCollapse } from "@boobstrap/react";

export function Details() {
  const details = useCollapse({ id: "account-details", defaultOpen: true });

  return (
    <>
      <button className="bs-btn bs-btn-secondary"
        {...details.getTriggerProps()}>
        Account details
      </button>
      <div className="bs-collapse bs-card bs-mt-4"
        {...details.getPanelProps()}>
        <div className="bs-card-body">Profile and security settings.</div>
      </div>
    </>
  );
}

Controlled and uncontrolled state

Use defaultOpen, defaultLoading, or defaultSelectedId when the hook should own state. Pass the corresponding controlled value and change callback when application state is authoritative.

JSX · Controlled dialog
import { useState } from "react";
import { useDialog } from "@boobstrap/react";

export function AccountDialog() {
  const [open, setOpen] = useState(false);
  const dialog = useDialog({
    id: "account-dialog",
    open,
    onOpenChange: setOpen,
  });

  return (
    <>
      <button className="bs-btn" {...dialog.getTriggerProps()}>Edit account</button>
      <dialog className="bs-dialog" aria-labelledby="account-dialog-title"
        {...dialog.getDialogProps()}>
        <header className="bs-dialog-header">
          <h2 className="bs-dialog-title" id="account-dialog-title">Account</h2>
          <button className="bs-dialog-close" aria-label="Close account"
            {...dialog.getDismissProps()}>&times;</button>
        </header>
        <div className="bs-dialog-body">Account settings.</div>
      </dialog>
    </>
  );
}

Events, cancelation, and SSR

Hooks emit the same cancelable before-events and completed lifecycle events as core controllers. Change callbacks receive the next value plus a detail object containing adapter: "react", a transition reason, and the source event when one exists.

  • Prevent a bs:*:show, bs:*:hide, bs:tabs:change, or button before-event to cancel its transition.
  • Hooks avoid DOM reads during render. Stable generated IDs come from React’s useId, so server output can hydrate without replacing relationships.
  • DOM work such as native dialog synchronization, focus restoration, outside-click handling, and floating placement runs after mounting and cleans up with the component.
JSX · Observe transition detail
const menu = useDropdown({
  onOpenChange(nextOpen, detail) {
    console.log(nextOpen, detail.adapter, detail.reason);
  },
});

One owner

Use React hooks or core controllers on a subtree, never both. Hooks do not initialize or search the document.

SSR

Render-time work is DOM-free. Effects synchronize dialogs, focus, listeners, timers, and floating placement after mount.

State

Choose uncontrolled default* options for local ownership or controlled values plus change callbacks for application ownership.

Props

Pass consumer handlers into a prop getter so the adapter can compose them. Preserve the returned refs, IDs, ARIA attributes, and keyboard handlers.

Component guides

Use the adapter reference for shared ownership rules, then use each component guide for copy-ready JSX and its component-specific semantics.

Hook API

HookPublic contract
useAccordionOpen ID collection, single-open or always-open coordination, item options, and root props.
useButtonLoading state, start/stop/toggle methods, busy semantics, and composed button props.
useCollapseOpen state, stable panel ID, show/hide/toggle methods, and trigger/panel props.
useDialogNative modal synchronization, dismissal, Escape handling, focus restoration, and dialog props.
useDropdownMenu state, outside dismissal, action-menu keyboard behavior, focus restoration, and root/trigger/menu props.
useComboboxQuery, selection, filtered options, active index, keyboard behavior, and input/listbox/option props.
useTabsSelected tab state, activation, roving focus, and synchronized tablist/tab/panel props.
useToastOpen state, pause-aware autohide, trigger/dismiss props, and polite live-region defaults.
useTooltipHover/focus visibility, Escape dismissal, placement, and descriptive relationships.
usePopoverActivation state, outside/Escape dismissal, placement, and trigger/panel relationships.