Skip to documentation
BoobstrapDocs
Get started

TypeScript

Use first-party declarations for Boobstrap controllers, adapters, and generated design-token artifacts.

v0.7.0 currentStrict mode testedNo separate types package
Type-safe integration

Declarations live with each package

Boobstrap v0.5 adds declarations to the dependency-free core JavaScript entry point and the Alpine adapter. The React and Vue adapters continue to publish declarations beside their headless hooks and composables. The generated JavaScript token artifact also includes a declaration file, so an application can inspect design tokens without maintaining a parallel interface.

Version contract: These declarations ship with v0.7.0 of the core and adapter packages; no separate types package is required. Pin the package version in production and use the installation guide for release-ready commands.
TypeScript declarations provided by Boobstrap package entry points
ImportDeclaration coverageRuntime boundary
@boobstrap/boobstrap/jsAll core controllers, initializers, options, and interaction-contract types.Browser behavior; initialize only where DOM globals exist.
@boobstrap/boobstrap/js/*Typed direct-controller imports for tree-shaken application code.Browser behavior for one controller.
@boobstrap/alpinePlugin, provider factories, provider state, and binding objects.Alpine-owned browser behavior.
@boobstrap/reactHook options, results, prop getters, and adapter transition details.SSR-safe headless hooks; React owns the component subtree.
@boobstrap/vueComposable options, computed results, refs, and prop getters.SSR-safe headless composables; Vue owns the component subtree.
@boobstrap/boobstrap/tokensNamed and default exports for tokens and mode overrides.Generated ESM data; no DOM access.
The root export is CSS, not a JavaScript API. import "@boobstrap/boobstrap"; is a side-effect stylesheet import. It does not expose controllers or TypeScript declarations. Import runtime values and types from /js, a /js/* subpath, an adapter package, or /tokens.

Core controllers in strict mode

The aggregate /js entry point is the convenient choice when an application uses several controllers or initBoobstrap. Controllers expose typed DOM elements, readonly state, lifecycle cleanup, and boolean transition results.

TypeScript · Direct core controller
import {
  Toast,
  type TransitionOptions,
} from "@boobstrap/boobstrap/js";

const element = document.querySelector<HTMLElement>("#saved-toast");

if (!element) {
  throw new Error("Missing #saved-toast");
}

const toast = Toast.getOrCreateInstance(element);
const transition: TransitionOptions = { reason: "profile-saved" };

if (!toast.visible) {
  toast.show(transition);
}

// Remove listeners owned by this controller when its DOM subtree is discarded.
toast.destroy();

Use the aggregate initializer for progressive enhancement across a document or a smaller element boundary. Its result owns every controller it created and provides one teardown method.

TypeScript · Aggregate initializer
import {
  initBoobstrap,
  type InitializerResult,
  type Root,
} from "@boobstrap/boobstrap/js";

function enhance(root: Root): InitializerResult {
  return initBoobstrap(root);
}

const accountArea = document.querySelector<HTMLElement>("#account-area");
const enhancement = enhance(accountArea ?? document);

// Call from your router, island, or application teardown path.
enhancement.destroy();

Prefer a subpath for one controller

Every core controller has a package-export subpath. A direct import keeps intent explicit and lets a bundler include only that controller and its internal dependencies.

TypeScript · Controller subpath
import { Collapse } from "@boobstrap/boobstrap/js/collapse";

const panel = document.querySelector<HTMLElement>("#account-details");

if (panel) {
  const details = Collapse.getOrCreateInstance(panel);
  details.show();
}

Core declarations type controller methods and accepted transition options. They do not currently declare component-specific CustomEvent detail maps. Narrow and validate event.detail in application code instead of assuming an undocumented generic event type.

Alpine provider types

The Alpine package exports its plugin as the default value and exports each provider factory by name. Provider interfaces describe reactive state, methods, and the binding objects returned for x-bind. Register the plugin before Alpine.start(); do not initialize core controllers on the same subtree.

TypeScript · Alpine registration and provider
import boobstrap, {
  accordion,
  type AccordionProvider,
  type AlpineLike,
} from "@boobstrap/alpine";

declare const Alpine: AlpineLike;
boobstrap(Alpine);

const typedFaq = (): AccordionProvider =>
  accordion(["billing"], {
    alwaysOpen: false,
    onOpenIdsChange(ids) {
      console.info("Open FAQ items", ids);
    },
  });

Alpine.data("typedFaq", typedFaq);

The same entry point declares button, collapse, combobox, dialog, dropdown, popover, tabs, toast, and tooltip. Binding objects intentionally use Record<string, unknown> because Alpine interprets their directive-shaped keys at runtime. Alpine itself does not currently publish declarations in its package, so the strict example types the narrow plugin boundary with Boobstrap's AlpineLike interface; keep application-specific Alpine registration in JavaScript or supply an application-owned ambient declaration when broader Alpine typing is required.

React hook types

React hooks infer controlled state, callback detail, and element-specific prop getters. Spread the returned props onto the semantic element they name. The adapter’s transition details include the literal discriminator adapter: "react".

TSX · Controlled collapse
import { useState } from "react";
import { useCollapse } from "@boobstrap/react";

export function AccountDetails() {
  const [open, setOpen] = useState(false);
  const details = useCollapse({
    id: "account-details",
    open,
    onOpenChange(nextOpen, detail) {
      detail.adapter satisfies "react";
      setOpen(nextOpen);
    },
  });

  return (
    <>
      <button className="bs-btn" {...details.getTriggerProps()}>
        Account details
      </button>
      <div className="bs-collapse" {...details.getPanelProps()}>
        Billing and profile settings
      </div>
    </>
  );
}

Vue composable types

Vue composables use default* options for uncontrolled initial state. Pass a Vue ref to a controlled field such as open when the adapter should update that state reactively; a plain controlled value remains fixed until its owner supplies a new value. Public state is returned as ComputedRef, and prop getters return objects ready for v-bind. Adapter transition details use adapter: "vue".

Vue · Typed setup
<script setup lang="ts">
import { ref } from "vue";
import { useCollapse } from "@boobstrap/vue";

const open = ref(false);
const details = useCollapse({
  id: "account-details",
  open,
  onOpenChange(nextOpen, detail) {
    detail.adapter satisfies "vue";
    open.value = nextOpen;
  },
});
</script>

<template>
  <button class="bs-btn" v-bind="details.getTriggerProps()">
    Account details
  </button>
  <div class="bs-collapse" v-bind="details.getPanelProps()">
    Billing and profile settings
  </div>
</template>

Typed design-token artifacts

Import generated token data from @boobstrap/boobstrap/tokens. The module exports tokens, selector-keyed modes, and a default object containing both. Each leaf preserves a source-derived value and the originating custom-property name. Exact CSS var() aliases are normalized to Design Tokens Community Group references such as {brand.500}.

TypeScript · Token inspection
import artifact, {
  modes,
  tokens,
  type DesignToken,
} from "@boobstrap/boobstrap/tokens";

function isDesignToken(value: DesignToken | string): value is DesignToken {
  return typeof value !== "string";
}

const primary = tokens.color.primary;

if (isDesignToken(primary)) {
  console.log(primary.$value);
  console.log(primary.$extensions["org.boobstrap.css-variable"]);
}

for (const [selector, overrides] of Object.entries(modes)) {
  console.log(selector, overrides["--bs-color-primary"]);
}

artifact.tokens === tokens; // true

Use @boobstrap/boobstrap/tokens.json when a build tool requires raw JSON instead of ESM. JSON import syntax and type inference depend on the host toolchain; the first-party DesignToken and TokenGroups declarations belong to the /tokens ESM entry.

Compiler configuration

Boobstrap packages are ESM and use package exports with a types condition. Use a modern module resolver so TypeScript follows the same entry points as the runtime. Core declarations reference browser interfaces including Document, Element, HTMLElement, and Event, so browser projects need the DOM library.

Use bundler resolution with Vite and similar tools that own final module transformation.

JSON · tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": ["ES2022", "DOM"]
  }
}

SSR and hydration boundaries

  • CSS and token data: import them through the normal application or build-tool path. The ESM token artifact performs no DOM initialization.
  • Core and Alpine behavior: call initBoobstrap, create controllers, and start Alpine only in a browser lifecycle where document exists. Type availability does not make a DOM operation server-safe.
  • React and Vue adapters: use the headless hooks and composables during framework rendering. They avoid core-controller initialization; keep IDs and the initial controlled or uncontrolled state consistent between server output and hydration.
  • Behavior ownership: never run core initialization over markup whose state is owned by Alpine, React, or Vue.

Troubleshooting

Common Boobstrap TypeScript problems and corrections
SymptomCorrection
“Cannot find module” for /js or /tokensConfirm the installed package exposes the entry, then use Bundler, Node16, or NodeNext resolution. Legacy node resolution does not understand modern export conditions.
Missing Document, HTMLElement, or EventAdd DOM to compilerOptions.lib for the browser-facing compilation unit.
The root package import has no named exportsThat entry is intentionally the stylesheet. Import values and types from @boobstrap/boobstrap/js or a supported subpath.
React JSX props or Vue refs lose their typesInstall the adapter’s framework peer and its normal TypeScript support, and import from @boobstrap/react or @boobstrap/vue rather than reaching into package source files.
Runtime behavior fires twiceThis is an ownership problem, not a declaration problem. Remove core initialization from an Alpine-, React-, or Vue-owned subtree.
JSON token import is rejectedUse the typed /tokens ESM entry, or enable the JSON-module behavior required by your compiler and runtime.

Use public subpaths

Import only from package exports documented on this page. Internal src/ and dist/ module layouts are not application APIs.

Keep strict mode on

Narrow queried elements and optional values instead of suppressing errors. The framework’s own declaration fixtures compile with strict checking.

Respect runtime ownership

Types describe a behavior layer; they do not coordinate multiple layers. Choose core, Alpine, React, or Vue once per component subtree.