@aomi-labs/react provides runtime logic, state management, hooks, and an API client with zero UI opinions. Use it to build completely custom interfaces or integrate Aomi logic into existing applications.
Use the headless React library when you need a custom layout, branded components, or a non-chat interaction model.
Installation
npm install @aomi-labs/react
Or using pnpm:
pnpm install @aomi-labs/react
Peer Dependencies
{
"@assistant-ui/react": "^0.11.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"wagmi": "^2.0.0",
"viem": "^2.0.0"
}
wagmi and viem are optional peer dependencies. Install them only if your app uses wallet connection; skip them for read only or non-wallet UIs.
What’s Included
Providers
| Export | Description |
|---|
AomiRuntimeProvider | Top-level provider that connects to the Aomi backend and sets up all contexts |
ThreadContextProvider | Thread state management (messages, metadata, thread switching) |
ExtUserProvider | Wallet/user state (address, chain, connection status) |
NotificationContextProvider | Toast notification state |
EventContextProvider | SSE subscription and system event dispatch |
ControlContextProvider | Model/app/API key selection state |
Hooks
| Hook | Description |
|---|
useAomiRuntime | Unified API combining all sub-contexts (threads, messages, user, events, notifications, wallet) |
useUser | Wallet/user state and setters |
useThreadContext | Current thread ID, messages map, metadata map |
useCurrentThreadMessages | Messages for the active thread |
useCurrentThreadMetadata | Metadata (title, status) for the active thread |
useControl | Model/app/API key state and selection |
useNotification | Show, dismiss, and list notifications |
useEventContext | SSE subscription and outbound event dispatch |
useWalletHandler | Handle wallet transaction and signing requests |
useNotificationHandler | Process notification events from the backend |
API Client
| Export | Description |
|---|
AomiClient | Direct HTTP client for the Aomi backend (usable outside React), re-exported from @aomi-labs/client |
Utilities
| Export | Description |
|---|
cn | Class name merger (clsx + tailwind-merge) |
formatAddress | Truncates wallet addresses for display (0xABC...12) |
getNetworkName | Resolves chain ID to human-readable network name |
getChainInfo | Returns chain metadata (name, ticker, explorer URL) |
SUPPORTED_CHAINS | Array of supported chain configurations |
Types
import type {
AomiRuntimeApi,
AomiMessage,
ThreadMetadata,
ThreadControlState,
UserState,
ControlState,
Notification,
NotificationType,
WalletRequest,
WalletTxPayload,
WalletEip712Payload,
InboundEvent,
SSEStatus,
} from "@aomi-labs/react";
Basic Setup
Wrap your app with AomiRuntimeProvider:
import { AomiRuntimeProvider } from "@aomi-labs/react";
function App() {
return (
<AomiRuntimeProvider backendUrl="https://api.aomi.dev">
<YourCustomUI />
</AomiRuntimeProvider>
);
}
AomiRuntimeProvider internally sets up all nested providers (ThreadContext, UserContext, NotificationContext, EventContext, ControlContext), so you get the full runtime with a single wrapper.
Provider Hierarchy
AomiRuntimeProvider wraps your app with a hierarchy of context providers:
AomiRuntimeProvider
└── ThreadContextProvider ← Thread state (messages, metadata, switching)
└── NotificationContextProvider ← Toast notifications
└── ExtUserProvider ← Wallet/user state
└── ControlContextProvider ← Model/app/API key
└── EventContextProvider ← SSE + system events
└── {children}
Each layer provides specific functionality through its own hook:
| Context | Hook | Provides |
|---|
| Thread | useThreadContext | Thread ID, messages, metadata, thread operations |
| User | useUser | Wallet address, chain ID, connection status |
| Notification | useNotification | Show/dismiss notifications |
| Control | useControl | Model selection, app, API key |
| Event | useEventContext | SSE subscription, outbound events |
| Unified | useAomiRuntime | All of the above in one hook |
Runtime Provider
AomiRuntimeProvider is the top-level provider that connects your React app to the Aomi backend. It sets up all contexts needed for chat, threads, user state, notifications, and events.
Props
| Prop | Type | Default | Description |
|---|
backendUrl | string | "http://localhost:8080" | URL of the Aomi backend API |
children | ReactNode | — | Your application components |
Typical Provider Hierarchy
In a Next.js App Router project, place AomiRuntimeProvider inside your wagmi and query providers:
// app/providers.tsx
"use client";
import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AomiRuntimeProvider } from "@aomi-labs/react";
import { wagmiConfig } from "./wagmi-config";
const queryClient = new QueryClient();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AomiRuntimeProvider
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL}
>
{children}
</AomiRuntimeProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Without Wallet Support
If your application does not need wallet functionality, skip the wagmi providers entirely:
import { AomiRuntimeProvider } from "@aomi-labs/react";
export function Providers({ children }) {
return (
<AomiRuntimeProvider backendUrl="https://api.aomi.dev">
{children}
</AomiRuntimeProvider>
);
}
The ConnectButton control and runtime transaction handler will simply be inactive.
Using AomiClient Directly
For non-React code or server-side use, you can use the AomiClient class without any providers. Construct it with an options object, not a bare URL:
import { AomiClient } from "@aomi-labs/react";
const client = new AomiClient({ baseUrl: "https://api.aomi.dev" });
// List threads for a wallet address
const threads = await client.listThreads(sessionId, publicKey);
// Send a message
await client.sendMessage(sessionId, message, { app, apiKey });
// Get available models
const models = await client.getModels(sessionId);
| Scenario | Recommendation |
|---|
| Standard chat interface needed quickly | Widget |
| Existing design system, custom look | Headless |
| Non-Next.js React app | Headless |
| Server-side API calls (no React) | Headless (AomiClient class directly) |
| Want to start fast, customize later | Widget (edit source files) |
| Embedding in an existing complex layout | Headless |
Custom UI Tutorial
Build a complete chat interface from scratch:
- Create a
MessageList component that renders messages using useThreadContext
- Create a
ChatInput component that sends messages via useAomiRuntime
- Create a
ThreadSwitcher to manage multiple conversations
- Compose them into your page layout
The key design principle: state lives in the library, components are pure presentational.
Handler Utilities
Wallet Handler
The useWalletHandler hook processes wallet transaction and signing requests from the backend:
import { useWalletHandler } from "@aomi-labs/react";
function WalletHandler() {
const { pendingRequests, startRequest, resolveRequest, rejectRequest } =
useWalletHandler({ getSession: () => getCurrentSession() });
// Each request has a `kind`: "transaction" | "eip712_sign" | "solana_sign".
// Resolve with a matching result kind, or call rejectRequest(id).
return null; // Renders nothing — drive the requests programmatically
}
See the Hooks Reference for the full useWalletHandler API and result shapes.
Notification Handler
The useNotificationHandler hook processes notification events from the backend:
import { useNotificationHandler } from "@aomi-labs/react";
function NotificationListener() {
const { notifications, unhandledCount, markDone } = useNotificationHandler({
onNotification: (n) => {
// Trigger your own toast or banner UI when a notification arrives
},
});
// notifications: every notification received, newest first
// unhandledCount: how many are not yet marked done
// markDone(id): mark one as handled
return null;
}
Event System Details
Event Types
The event context handles inbound events streamed from the backend over SSE, and lets you send outbound system messages back to the backend:
| Direction | How | Description |
|---|
| Server → Client | subscribe(type, handler) delivers an InboundEvent | System notifications, wallet requests, status updates |
| Client → Server | sendOutboundSystem({ type, sessionId, payload }) | UI state and system commands sent to the backend |
Subscription
import { useEffect } from "react";
import { useEventContext } from "@aomi-labs/react";
function EventSubscriber() {
const { subscribe, sseStatus } = useEventContext();
useEffect(() => {
// subscribe(eventType, handler) returns an unsubscribe function
const unsubscribe = subscribe("wallet_tx_request", (event) => {
console.log(event.payload);
});
return unsubscribe;
}, [subscribe]);
// sseStatus — "connected" | "connecting" | "disconnected"
return (
<div className="text-xs text-white/40">
SSE: {sseStatus}
</div>
);
}
Sending Outbound System Messages
To send a system message back to the backend, use sendOutboundSystem. It takes an object with type, sessionId, and payload:
import { useEventContext } from "@aomi-labs/react";
function useTxComplete(sessionId: string) {
const { sendOutboundSystem } = useEventContext();
const notifyTxComplete = async (txHash: string) => {
await sendOutboundSystem({
type: "wallet:tx_complete",
sessionId,
payload: { txHash, status: "success" },
});
};
return { notifyTxComplete };
}
Next Steps
Last modified on June 4, 2026