> ## Documentation Index
> Fetch the complete documentation index at: https://aomi.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Customization

> Adapt AomiWidget with props, themes, source components, or the headless React library while preserving its runtime behavior.

Start with `AomiWidget`, then take ownership of more of the interface only when your product needs it.

## Choose your customization level

| Level | You own | Aomi provides |
| - | - | - |
| Widget props | Placement and visible controls | Complete UI, runtime, and wallets |
| Themes | Brand colors and visual tokens | Component structure and behavior |
| `AomiFrame` | Component composition | Chat, threads, and wallet components |
| shadcn source | The copied component source | A production starting point |
| Headless library | The complete interface | State, runtime, and React hooks |

## Configure AomiWidget

Use presentation props to fit the widget into a page, panel, or modal.

```tsx theme={null}
<AomiWidget
  applicationId="123"
  apiUrl="https://chat.aomi.dev"
  auth={{ kind: "browser_wallet" }}
  width="100%"
  height="min(780px, calc(100dvh - 160px))"
  showSidebar
  showHeader
  walletPosition="footer"
/>
```

| Prop | Purpose |
| - | - |
| `width`, `height` | Size the widget container |
| `className`, `style` | Add application-specific layout styles |
| `showSidebar` | Show or hide thread navigation |
| `showHeader` | Show or hide the widget header |
| `walletPosition` | Place the wallet control in the header or footer; pass `null` to hide it |
| `wallets` | Limit presented wallets, chains, and networks |
| `persistThread` | Restore the active thread after a reload |

These props change presentation. The Application ID selects the deployed App; action review and wallet execution remain visible client responsibilities.

## Apply themes and styles

Import the default stylesheet once:

```tsx theme={null}
import "@aomi-labs/widget-lib/styles.css";
```

Then override its CSS variables in your application stylesheet. Keep transaction warnings, fees, owner addresses, and confirmation states visually distinct when changing colors.

## Compose with AomiFrame

Use the compound API when the standard widget layout is close to what you need, but you want to rearrange its sections.

```tsx theme={null}
import { AomiFrame } from "@aomi-labs/widget-lib";

<AomiFrame.Root
  backendUrl="https://chat.aomi.dev"
  applicationId="123"
  height="720px"
  showSidebar={false}
>
  <AomiFrame.Header showSidebarTrigger={false} />
  <AomiFrame.Composer
    withControl
    welcomeTitle="How can I help?"
  />
</AomiFrame.Root>;
```

`AomiFrame` provides the chat surface and runtime. If you use it outside `AomiWidget`, your application must supply the surrounding authentication and account integration.

## Own the source with shadcn

Copy the widget components into your repository when you need to change markup or component behavior:

```bash theme={null}
npx shadcn add https://aomi.dev/r/aomi-widget.json
```

Copied components become application code. Review registry updates before overwriting local changes.

## Build a headless UI

Choose the headless library when props, themes, composition, and copied components are not enough. Aomi continues to provide thread, chat, user, event, notification, and wallet-request state. You provide every visible interaction.

### Install the library

```bash theme={null}
npm install @aomi-labs/react @assistant-ui/react
```

### Add the runtime provider

```tsx theme={null}
import { AomiRuntimeProvider } from "@aomi-labs/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <AomiRuntimeProvider
      backendUrl="https://chat.aomi.dev"
      applicationId="123"
    >
      {children}
    </AomiRuntimeProvider>
  );
}
```

### Your responsibilities

A headless interface must provide:

* Thread navigation and message rendering.
* Composer, streaming, cancellation, and error states.
* Wallet connection and network selection.
* Transaction-call and fee disclosure.
* Explicit approval and rejection controls.
* Owner and network validation before signing.
* Submitted, confirmed, expired, and failed operation states.

<Warning>
  Returning a generic transaction result is not enough. Your interface must narrow each action by `action.request.kind`, show the exact request, and return the result that kind expects. Prefer `AomiWidget` unless you need to own the full approval experience.
</Warning>

## Hooks

All hooks must run beneath `AomiRuntimeProvider`.

### Chat and threads

| Hook | Purpose |
| - | - |
| `useAomiRuntime` | Unified chat, thread, user, notification, event, and wallet API |
| `useThreadContext` | Low-level thread state and operations |
| `useCurrentThreadMetadata` | Metadata for the active thread |

Read active-thread messages with `useAomiRuntime().getMessages()`.

### User and wallet controls

| Hook | Purpose |
| - | - |
| `useUser` | Canonical user and wallet state |
| `useControl` | Combined App, model, API-key, and secret controls |
| `useApiKey` | API-key state and actions |
| `useAuthEndpoints` | Available Apps and models |
| `usePerThreadControl` | Per-thread App and model selection |

### Events and notifications

| Hook | Purpose |
| - | - |
| `useNotification` | Notice, success, wallet, and error messages |

Read ordered backend events from `useAomiRuntime().events` and the active lifecycle from `turnState`. `pendingActions` contains canonical Actions; narrow on `action.request.kind` before reading its payload. Use the exported TypeScript types as the source of truth for complete signatures and payloads.

## Complete custom UI example

This starter owns message rendering, the composer, cancellation, and the visible wallet-request count:

```tsx theme={null}
"use client";

import { useState, type FormEvent } from "react";
import { useAomiRuntime } from "@aomi-labs/react";

export function CustomChat() {
  const [input, setInput] = useState("");
  const {
    currentThreadId,
    getMessages,
    sendMessage,
    isRunning,
    cancelGeneration,
    pendingActions,
  } = useAomiRuntime();

  const messages = getMessages(currentThreadId);

  async function submit(event: FormEvent) {
    event.preventDefault();
    const text = input.trim();
    if (!text || isRunning) return;
    setInput("");
    await sendMessage(text);
  }

  return (
    <main>
      <section aria-live="polite">
        {messages.map((message, index) => (
          <article key={index} data-role={message.role}>
            <strong>{message.role}</strong>
          </article>
        ))}
      </section>

      <form onSubmit={submit}>
        <input
          value={input}
          onChange={(event) => setInput(event.target.value)}
          disabled={isRunning}
        />
        {isRunning ? (
          <button type="button" onClick={cancelGeneration}>Stop</button>
        ) : (
          <button type="submit">Send</button>
        )}
      </form>

      <p>{pendingActions.length} actions need review</p>
    </main>
  );
}
```

Add an approval component for every action kind your App can produce. Show the calls, signatures, owner, network, simulation evidence, and terminal status available on the request.
