# Pure Owl Components

See the [Usage Guide](usage-guide.md) for when to reach for this API versus the static `b-ui` one. This page covers the pure Owl API only.

BaseUI ships a second, additive public API alongside the static `b-ui` markers: real Owl component classes you import, compose with `static components`, and mount as part of your own Owl app. This page covers how to load and use it; see [Component Authoring](component-authoring.md) for how components implement both APIs from one source folder.

As of 1.1.0, every component in the registry (all 65) has a pure Owl export — there are no static-only components left. Compound components export a root class plus sub-components (e.g. `Dialog`/`DialogTrigger`/`DialogContent`/...); check a component's `owlComponents` entry in `dist/baseui.registry.json`, or its doc page under [`docs/components/`](components/), for the exact export list. A few components have a deliberately reshaped pure Owl API versus their static markup-scanning adapter — see [Registry](registry.md#component-export-policy) for which ones and why.

## What you need to load

Everything — the static `b-ui` runtime, the Owl framework, every pure Owl component class, and the theme helper — is one file: `dist/baseui.esm.js` (or `dist/baseui.min.js` as a classic script). There is no separate `baseui.owl.*`/`baseui.components.esm.js`/`baseui.theme.esm.js` to load.

| File | Purpose |
| --- | --- |
| `dist/baseui.min.css` | Bootstrap + BaseUI styling, shared by both APIs. |
| `@base/owl` (`dist/baseui.esm.js`) | The Owl framework — `Component`, `mount`, `xml`, `useState`, etc. Pinned to Owl `2.8.3`, embedded directly in this file. Import your app's own Owl usage from here too, not `@odoo/owl`. |
| `@base/component` (`dist/baseui.esm.js`) | The pure Owl component classes — same file as `@base/owl` above, just a different import-map key for readability. |
| `@base/theme` (`dist/baseui.esm.js`) | Same file again — the theme helper for persisted light/dark toggles, URL overrides, button-label sync, and iframe preview sync. |
| `@base/templates` (`dist/baseui.templates.xml`) | The Owl templates the component classes reference (`static template = "baseui.Button"`, etc). Must be loaded and passed to `mount()` before any BaseUI component tag renders — a component class alone does not carry its template. |

## Browser (import map) usage

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.css">
<script type="importmap">
{
  "imports": {
    "@base/owl": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
    "@base/component": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
    "@base/theme": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.esm.js",
    "@base/templates": "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.templates.xml"
  }
}
</script>
<script type="module">
  import { Component, mount, xml } from "@base/owl";
  import { Button, Card, CardBody, CardHeader, CardTitle } from "@base/component";

  // "@base/templates" is an import-map entry, not a JS module — resolve it
  // to its mapped URL and fetch the text.
  const templates = await fetch(import.meta.resolve("@base/templates")).then((r) => r.text());

  class Root extends Component {
    static components = { Button, Card, CardBody, CardHeader, CardTitle };
    static template = xml`
      <Card>
        <CardHeader><CardTitle>Hello</CardTitle></CardHeader>
        <CardBody>
          <Button variant="'default'" label="'Save'" onClick="() => console.log('saved')"/>
        </CardBody>
      </Card>`;
  }

  await mount(Root, document.getElementById("app"), { templates });
</script>
```

`https://unpkg.com/@thebase/ui@latest/dist/...` mirrors the same files as an alternative CDN if jsDelivr is unreachable — swap the host in each URL above. For a self-hosted build, replace the CDN host with your own served path (e.g. `/dist/baseui.esm.js`). These snippets use `@latest` for readability; pin an exact version (e.g. `@0.0.4`) in production so a new release can't change what your page loads.

No `@odoo/owl` import appears anywhere in that snippet — `@base/owl` is the only Owl entry point BaseUI components and consumer apps need. Because `@base/owl`, `@base/component`, and `@base/theme` all point at the exact same `dist/baseui.esm.js` URL, the browser fetches and evaluates it once and every key shares the same module instance — no separate classic `<script>` needs to load first to set up a shared global.

## npm / bundler usage

```sh
npm install @thebase/ui
```

```js
import { Component, mount, xml, Button, Card } from "@thebase/ui";
import baseuiTemplates from "@thebase/ui/dist/baseui.templates.xml?raw"; // exact loader syntax depends on your bundler

class Root extends Component {
  static components = { Button, Card };
  static template = "Root";
}
```

There's only one package entry now — `@thebase/ui` exports the Owl framework (built from the exact `@odoo/owl@2.8.3` this BaseUI release vendors; you do not need to install `@odoo/owl` yourself), every pure Owl component class, and the theme helper together. Browser import-map examples above still use the `@base/owl`/`@base/component`/`@base/theme` keys for readability, but they're just local aliases to the same file.

When a bundler cannot import XML with `?raw`, load `dist/baseui.templates.xml` as a served asset and pass the fetched string to `mount(Root, target, { templates })`. The template file is part of the runtime contract, not optional documentation.

## Mixing with the static API

Both APIs come from the exact same bundle now, so there's nothing extra to load — just be clear about which mechanism owns which markup:

- `BaseUI.mountAll(root)` enhances static `[b-ui]` and `[b-icon]` markers.
- Owl `mount(Root, target, { templates })` renders component tags such as `<Button/>`.
- Importing the package always also runs `autoMount()` (its `[b-ui]`/`[b-icon]` DOM scan on `DOMContentLoaded`) — harmless if your page has none, but means there's no side-effect-free way to import just the component classes anymore.
- Both APIs share `dist/baseui.min.css`, `@base/theme`, and the same `b-theme` / `data-bs-theme` root attributes.

## Props, events, slots

- Props are camelCase (`variant`, `label`, `disabled`, `className`).
- Almost every component that renders its own root element also accepts `style` (a string or a plain object of CSS properties) alongside `className`. Components that only forward a default slot with no wrapping element of their own (`AlertDialog`, `Pwa`, `Sheet`, `ContextMenu`, `Drawer`) have neither prop. A handful of components that already compute an internal style (`Icon`, `AspectRatio`, `Progress`, `ContextMenuContent`, `SidebarMenuSkeleton`) merge your `style` with their own computed value instead of one overwriting the other.
- Events are callback props (`onClick`, `onChange`, `onOpenChange`), not DOM `CustomEvent`s — that's the static API's contract, not this one.
- Default slot content works as normal Owl children (`<Badge>Draft</Badge>`); compound components use named slots/sub-tags (`<CardHeader>`, `<DialogFooter>`).

See each component's page under [`docs/components/`](components/) for its specific props table and an Owl usage example alongside the static `b-ui` one.

## Common mistakes

- **Rendering a bare `<Button/>` with no template loaded.** `mount(Root, target)` without `{ templates }` (or `app.addTemplates(...)`) throws — the `.xml` file is a real build/runtime input, not documentation.
- **Registering `Button` under the wrong tag name.** `static components = { Button }` must match the tag used in the template (`<Button/>`), same as any other Owl component.
- **Importing from `@odoo/owl`.** Always import from `@base/owl` instead, even in your own app code — it keeps your app on the exact Owl version this BaseUI release was tested against.
- **Adding a `baseui.owl.min.js`/`baseui.owl.esm.js`/`baseui.components.esm.js` script or import-map entry.** None of those files are published anymore — point `@base/owl`/`@base/component`/`@base/theme` all at `dist/baseui.esm.js` instead.
