# Host a native button in Pibbl
Mount and update native HTML inside an HtmlBox, preserving native clicks and focus.

## APIs demonstrated

- [HtmlBox](/reference/components/html-box/) from `@pibbl/core`
- [HtmlBoxProps](/reference/types/html-box/) from `@pibbl/core`
- [HtmlBoxStyle](/reference/types/html-box/) from `@pibbl/core`
- [HtmlMountCallback](/reference/types/html-box/) from `@pibbl/core`
- [HtmlUpdateFunction](/reference/types/html-box/) from `@pibbl/core`
- [HtmlBoxFocusState](/reference/types/html-box/) from `@pibbl/core`

## Complete example

[Plain source](/minimal/lifecycle/html.tsx). Run as a browser module after the document body exists. TSX uses `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`.

```tsx
import {
  pibbl,
  HtmlBox,
  Rectangle,
  signal,
  type HtmlBoxProps,
  type HtmlBoxStyle,
  type HtmlMountCallback,
  type HtmlUpdateFunction,
  type HtmlBoxFocusState,
} from "@pibbl/core";

const count = signal(0);
const output = document.createElement("output");
const canvas = document.createElement("canvas");
canvas.width = 360;
canvas.height = 180;
const host = document.createElement("div");
host.style.cssText = "position:relative;width:360px;max-width:100%";
const overlay = document.createElement("div");
overlay.style.cssText = "position:absolute;inset:0;pointer-events:none";
canvas.style.display = "block";
host.append(canvas, overlay);
document.body.append(host, output);
type Data = { label: string };
const mount: HtmlMountCallback<Data> = (element, initial, abort) => {
  const button = document.createElement("button");
  button.textContent = initial.label;
  button.addEventListener("click", () => count.update((n) => n + 1), {
    signal: abort,
  });
  element.append(button);
  const update: HtmlUpdateFunction<Data> = (data) => {
    button.textContent = data.label;
  };
  return update;
};
const style: HtmlBoxStyle = { left: 30, top: 50, width: 270, height: 70 };
function Scene() {
  const props: HtmlBoxProps<Data> = {
    data: { label: "Native clicks: " + count.get() },
    mount,
    onFocusChange(state: Readonly<HtmlBoxFocusState>) {
      output.textContent = state.isFocusWithin
        ? "HTML focused"
        : "HTML unfocused";
    },
  };
  return (
    <>
      <Rectangle style={{ width: 360, height: 180, fill: "#e5f2ef" }} />
      <HtmlBox {...props} style={style} />
    </>
  );
}
const controller = pibbl(canvas, <Scene />, { htmlOverlay: overlay });
window.addEventListener("pagehide", () => controller.dispose(), { once: true });

```

## Usage notes

- HtmlBox hosts live DOM alongside Canvas; Canvas filters cannot capture it. The abort signal releases listeners on unmount; keep mount callback identity stable.

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.
