---
title: "Component API"
description: "Public React building blocks for custom agent UI, chat fields, conversation rendering, realtime presence, sharing, progress, and rich editors."
---

# Component API

<Callout tone="info">

**Developer page.** This page is for developers picking layers of the chat UI to build custom agent surfaces. For the end-user experience of working with the agent, see [Using Your Agent](/docs/using-your-agent).

</Callout>

Agent-Native ships a full sidebar, but the sidebar is not the contract. The
contract is the runtime: chat streaming, thread state, actions, context,
attachments, model selection, runs, and SQL-backed sync. Use the stock
components when you can, and drop down a layer when you need custom product UI.

Import browser UI from focused client subpaths:

```tsx
import { AgentSidebar } from "@agent-native/core/client/agent-chat";
import { PromptComposer } from "@agent-native/core/client/composer";
import { AgentConversation } from "@agent-native/core/client/conversation";
import { usePresence } from "@agent-native/core/client/collab";
import { SharedRichEditor } from "@agent-native/core/client/editor";
import { ResourcesPanel } from "@agent-native/core/client/resources";
```

Avoid importing UI components from the bare `@agent-native/core` package. Use
`@agent-native/core/client` or a focused `@agent-native/core/client/*` subpath
so bundlers choose the browser-safe entry.

Use `@agent-native/toolkit` for reusable app-building pieces that are not part
of the foundational runtime contract: shadcn-style UI primitives, shared hooks,
simple app shell helpers, and other template composition modules. Core keeps
compatibility re-exports for migrated internals during the transition.

Reusable packages can import Toolkit modules directly:

```tsx
import { Button } from "@agent-native/toolkit/ui/button";
import { Toaster } from "@agent-native/toolkit/ui/sonner";
import { useToast } from "@agent-native/toolkit/hooks/use-toast";
import { useSetHeaderActions } from "@agent-native/toolkit/app-shell";
```

Inside template apps, prefer app-local adapters so teams can replace primitives
once and have every reused Toolkit surface pick up the local implementation:

```tsx
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
```

<Diagram id="doc-block-u3yfqy" title="Drop down a layer, not out of the framework" summary={"Each layer keeps the same runtime — actions, thread state, and SQL-backed sync — while giving you more control over the chrome."}>

```html
<div class="diagram-layers">
  <div class="diagram-card layer">
    <span class="diagram-pill accent">&lt;AgentSidebar&gt;</span
    ><small class="diagram-muted"
      >Whole sidebar around your app. The 80% case.</small
    >
  </div>
  <div class="diagram-card layer l2">
    <span class="diagram-pill"
      >&lt;AgentPanel&gt; &middot; &lt;AgentChatSurface&gt;</span
    ><small class="diagram-muted"
      >The panel or a chat page in your own layout.</small
    >
  </div>
  <div class="diagram-card layer l3">
    <span class="diagram-pill">&lt;AssistantChat&gt; + runtime</span
    ><small class="diagram-muted"
      >Own the chrome; optionally pass a BYO AgentChatRuntime.</small
    >
  </div>
  <div class="diagram-card layer l4">
    <span class="diagram-pill"
      >&lt;PromptComposer&gt; &middot; &lt;AgentConversation&gt;</span
    ><small class="diagram-muted"
      >Composer and transcript primitives only.</small
    >
  </div>
  <div class="diagram-rail" data-rough>
    Same runtime: actions &middot; thread state &middot; SQL-backed sync
  </div>
</div>
```

```css
.diagram-layers {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.diagram-layers .layer {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 14px;
}
.diagram-layers .l2 {
  margin-inline-start: 24px;
}
.diagram-layers .l3 {
  margin-inline-start: 48px;
}
.diagram-layers .l4 {
  margin-inline-start: 72px;
}
.diagram-layers .diagram-rail {
  margin-top: 6px;
  padding: 10px 14px;
  text-align: center;
}
```

</Diagram>

## Agent And Chat UI {#agent-chat-ui}

| API                                  | Import path                                   | Use when                                                                                         |
| ------------------------------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `<AgentSidebar>`                     | `@agent-native/core/client` or `/client/chat` | You want the complete sidebar around your app.                                                   |
| `<AgentToggleButton>`                | `@agent-native/core/client` or `/client/chat` | You render your own header button for the sidebar.                                               |
| `<AgentPanel>`                       | `@agent-native/core/client` or `/client/chat` | You want the full panel in your own layout, route, dialog, or side column.                       |
| `<AgentChatSurface>`                 | `@agent-native/core/client` or `/client/chat` | You want chat in panel or page mode without the sidebar wrapper.                                 |
| `<AssistantChat>`                    | `@agent-native/core/client` or `/client/chat` | You want to own surrounding chrome while keeping the standard conversation and composer runtime. |
| `<MultiTabAssistantChat>`            | `@agent-native/core/client` or `/client/chat` | You want the framework's thread tabs without `AgentPanel` chrome.                                |
| `createHttpAgentChatRuntime()`       | `@agent-native/core/client` or `/client/chat` | You have a BYO agent endpoint that streams normalized chat events.                               |
| `createOpenAIAgentsChatRuntime()`    | `@agent-native/core/client` or `/client/chat` | You have an OpenAI Agents SDK stream and want the standard chat UI around it.                    |
| `createOpenAIResponsesChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have an OpenAI Responses event stream and want it normalized into the chat UI.               |
| `createAgUiChatRuntime()`            | `@agent-native/core/client` or `/client/chat` | You have an AG-UI event stream and want it normalized into the chat UI.                          |
| `createClaudeAgentChatRuntime()`     | `@agent-native/core/client` or `/client/chat` | You have a Claude Agent SDK stream and want it normalized into the chat UI.                      |
| `createVercelAiChatRuntime()`        | `@agent-native/core/client` or `/client/chat` | You have a Vercel AI SDK stream and want it normalized into the chat UI.                         |
| `createAgentChatRuntimeAdapter()`    | `@agent-native/core/client` or `/client/chat` | You need to adapt an `AgentChatRuntime` into assistant-ui yourself.                              |
| `createAgentChatAdapter()`           | `@agent-native/core/client` or `/client/chat` | You need the built-in Agent-Native SSE transport as a low-level assistant-ui adapter.            |
| `useChatThreads()`                   | `@agent-native/core/client` or `/client/chat` | You need a custom thread list, history picker, or scoped chat UI.                                |
| `sendToAgentChat()`                  | `@agent-native/core/client` or `/client/chat` | A product action should hand work to the agent chat.                                             |

`AgentChatRuntime` is the BYO-agent contract for the standard chat shell. Pass
`runtime` to `<AssistantChat>` when an external agent should power the
conversation while Agent-Native keeps the composer, transcript, tool cards, and
native widget rendering. The connectors above are the API surface; the runtime
contract and event shapes are taught in
[Native Chat UI — BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes).
If you are choosing between headless agents, rich chat, embedded sidecar, and
full app shapes, see [Agent Surfaces](/docs/agent-surfaces).

The shortest custom route is still a pre-wired surface:

```tsx
import { AgentChatSurface } from "@agent-native/core/client/chat";

export default function ChatRoute() {
  return <AgentChatSurface mode="page" className="h-screen" />;
}
```

For custom chrome around the standard runtime:

```tsx
import { AssistantChat, useChatThreads } from "@agent-native/core/client/chat";

function CustomChat({ projectSlug }: { projectSlug: string }) {
  const threads = useChatThreads(undefined, projectSlug);
  const threadId = threads.activeThreadId ?? undefined;

  return (
    <section className="grid h-full grid-cols-[260px_1fr]">
      <ThreadList
        threads={threads.threads}
        activeThreadId={threadId}
        onSelect={threads.switchThread}
      />
      <AssistantChat threadId={threadId} />
    </section>
  );
}
```

For a bring-your-own agent endpoint, build an `AgentChatRuntime` with one of the
connectors above and pass it to `<AssistantChat runtime={...} />`. See
[Native Chat UI — BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes)
for the connector usage, the normalized event stream, and when to reach for
`createHttpAgentChatRuntime()` versus a protocol-specific connector.

## Chat Field And Composer {#composer}

Use `@agent-native/core/client/composer` when you need to place the same chat
field used by the sidebar inside custom UI.

Toolkit owns the shared Tiptap and composer UI implementation. The
`@agent-native/core/client/composer` entry is its framework-wired wrapper: it
binds the Core runtime adapters used by attachments, mentions, skills, voice,
and model selection. Reusable packages that need portable bare UI can import
`@agent-native/toolkit/composer` instead, but must render it inside
`ComposerRuntimeAdaptersProvider` and supply the required framework services.

| API                               | Use when                                                                                                                                                            |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<PromptComposer>`                | You need a ready-to-submit chat field with attachments, slash commands, references, pasted-text handling, draft persistence, voice input, and submission semantics. |
| `<AgentComposerFrame>`            | You want the standard visual shell around a custom composer body.                                                                                                   |
| `<TiptapComposer>`                | You need the lowest-level rich chat field. It must be rendered inside an assistant-ui `ThreadPrimitive.Root` / composer runtime.                                    |
| `buildPromptComposerSubmission()` | You need the same attachment and pasted-text normalization before calling your own submit handler.                                                                  |
| `formatPromptWithAttachments()`   | You need to render hidden attachment metadata into a prompt string.                                                                                                 |

Most custom UIs should start with `PromptComposer`:

```tsx
import { PromptComposer } from "@agent-native/core/client/composer";

<PromptComposer
  placeholder="Ask the agent..."
  onSubmit={async (text, files, references, options) => {
    await sendMessageToYourRuntime({ text, files, references, options });
  }}
/>;
```

Use `TiptapComposer` only if you are already wiring assistant-ui primitives
yourself. It is the field, not the whole chat runtime.

## Conversation Rendering {#conversation}

Use `@agent-native/core/client/conversation` for transcript-style rendering
outside the full agent runtime.

| API                                             | Use when                                                         |
| ----------------------------------------------- | ---------------------------------------------------------------- |
| `<AgentConversation>`                           | Render a list of normalized agent messages.                      |
| `<AgentConversationMessageView>`                | Render one normalized message.                                   |
| `normalizeCodeAgentTranscriptForConversation()` | Convert code-agent transcript events into conversation messages. |
| `useNearBottomAutoscroll()`                     | Keep a custom transcript pinned to the bottom while streaming.   |

This layer is intentionally data-first: you own where messages come from, and
the renderer owns consistent markdown, attachments, notices, artifacts, and
tool-call display.

## Native Tool Widgets {#native-tool-widgets}

Use native tool widgets when an action result should render as app-quality UI
inside chat instead of plain JSON. You don't import or mount a widget
component yourself — an action builds its result with a server-safe helper
(`createDataTableWidgetResult()`, `createDataChartWidgetResult()`) from
`@agent-native/core/data-widgets`, and the runtime picks the matching built-in
React renderer automatically. `DataTableWidget`, `DataChartWidget`, and
`DataWidgetResult` are the **result-shape types** — exported from
`@agent-native/core/client/chat` and the root client entry for typing action
results, not renderable components. See
[Native Chat UI](/docs/native-chat-ui#action-declared-widgets) for the full action result
contract and a worked example.

| API                              | Use when                                                                                 |
| -------------------------------- | ---------------------------------------------------------------------------------------- |
| `createDataTableWidgetResult()`  | Your action returns rows and columns that should render as a native table in chat.       |
| `createDataChartWidgetResult()`  | Your action returns series data that should render as a native bar, line, or area chart. |
| `DataWidgetResult`               | You need the typed union shape for `"data-table"`, `"data-chart"`, or `"data-insights"`. |
| `registerActionChatRenderer()`   | You need an action-declared renderer selected by exact `chatUI.renderer`.                |
| `registerToolRenderer()`         | You need a product-specific native renderer for a non-core tool result.                  |
| `registerReservedToolRenderer()` | Framework code needs a reserved renderer that wins before template renderers.            |

## Realtime Collab And Presence {#collab-presence}

Use `@agent-native/core/client/collab` for Liveblocks-style presence and
collaborative document hooks. Use `@agent-native/toolkit/collab-ui` for the
presentational overlays and avatar chrome; core keeps compatibility re-exports
for existing apps.

| API                                                 | Use when                                                                                    |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `useCollaborativeDoc()`                             | Bind a rich text editor or custom Yjs surface to `/_agent-native/collab`.                   |
| `usePresence()`                                     | Publish and render arbitrary awareness fields: cursors, selections, viewport, mode.         |
| `<PresenceBar>`                                     | Show active human and agent collaborators.                                                  |
| `<LiveCursorOverlay>`                               | Render remote cursor labels over a positioned container.                                    |
| `<RemoteSelectionRings>`                            | Render remote selection outlines over DOM elements.                                         |
| `useFollowUser()`                                   | Follow another participant's viewport or selection.                                         |
| `useCollaborativeMap()` / `useCollaborativeArray()` | Experiment with structured Y.Map/Y.Array state when rich-text body collab is the wrong fit. |
| `dedupeCollabUsersByEmail()`                        | Build a custom avatar stack without duplicate tabs for the same user.                       |

<Diagram id="doc-block-8mu09e" title="Presence: humans and the agent share one awareness layer" summary={"useCollaborativeDoc owns the awareness instance; client hooks publish cursors and selections; server helpers let an agent action appear as a live participant."}>

```html
<div class="diagram-presence">
  <div class="diagram-col">
    <div class="diagram-node">
      Humans<br /><small class="diagram-muted"
        >usePresence &middot; cursors, selection</small
      >
    </div>
    <div class="diagram-node diagram-accent">
      Agent action<br /><small class="diagram-muted"
        >agentUpdateSelection()</small
      >
    </div>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-panel center" data-rough>
    <span class="diagram-pill accent">useCollaborativeDoc</span
    ><small class="diagram-muted">awareness layer</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-box">
    &lt;PresenceBar&gt; &middot; &lt;LiveCursorOverlay&gt;<br /><small
      class="diagram-muted"
      >render everyone, agent included</small
    >
  </div>
</div>
```

```css
.diagram-presence {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.diagram-presence .diagram-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.diagram-presence .center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px;
}
.diagram-presence .diagram-arrow {
  font-size: 22px;
  line-height: 1;
}
```

</Diagram>

Server-side agent actions that want to appear as a live participant use the
lower-level `@agent-native/core/collab` agent presence helpers:

```ts
import {
  agentEnterDocument,
  agentLeaveDocument,
  agentUpdateSelection,
} from "@agent-native/core/collab";
```

## Rich Editor {#rich-editor}

Use `@agent-native/core/client/editor` when you need the shared markdown editor
surface used by plans, content, resources, and collaborative document
experiences.

| API                              | Use when                                                                                             |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `<SharedRichEditor>`             | You need the current, configurable editor with markdown serialization, optional Yjs, and app extras. |
| `<RichMarkdownEditor>`           | You need the backwards-compatible alias for the shared rich editor.                                  |
| `createSharedEditorExtensions()` | You are building your own Tiptap editor but want the framework schema and markdown dialects.         |
| `<SlashCommandMenu>`             | You need the shared slash-command UI for a custom Tiptap surface.                                    |
| `<BubbleToolbar>`                | You need the shared selection toolbar for marks, links, and custom inline actions.                   |
| `createRegistryBlockNode()`      | You need registry-backed block nodes inside a rich editor.                                           |
| `uploadEditorImage()`            | You want the framework upload-image action behind the editor's shared image block.                   |
| `useCollabReconcile()`           | You are binding a custom editor surface to a Yjs doc while preserving markdown as saved state.       |

The basic controlled editor is just markdown in and markdown out:

```tsx
import { SharedRichEditor } from "@agent-native/core/client/editor";

<SharedRichEditor
  value={markdown}
  onChange={setMarkdown}
  placeholder="Write notes..."
  features={{ tables: true, tasks: true, link: true }}
/>;
```

For realtime editing, pair it with the collab subpath:

```tsx
import {
  emailToColor,
  useCollaborativeDoc,
} from "@agent-native/core/client/collab";
import { SharedRichEditor } from "@agent-native/core/client/editor";

const editorUser = {
  name: user.name,
  email: user.email,
  color: emailToColor(user.email),
};
const collab = useCollaborativeDoc({
  docId,
  user: editorUser,
});

<SharedRichEditor
  value={markdown}
  onChange={setMarkdown}
  ydoc={collab.ydoc}
  awareness={collab.awareness}
  user={editorUser}
/>;
```

## Agent Resources {#resources}

Use `@agent-native/core/client/resources` when you want to expose the same
agent resource model that powers the agent panel's Resources tab.

| API                                                                   | Use when                                                                |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `<ResourcesPanel>`                                                    | You want the complete Resources tab as a page, drawer, or custom panel. |
| `<ResourceTree>`                                                      | You want to render your own resource browser around framework data.     |
| `<ResourceEditor>`                                                    | You want the framework editor for a selected resource.                  |
| `useResourceTree()`                                                   | You need a scoped tree for personal, shared, or workspace resources.    |
| `useResource()`                                                       | You need the content and metadata for one selected resource.            |
| `useCreateResource()` / `useUpdateResource()` / `useDeleteResource()` | You need custom controls around the resource lifecycle.                 |
| `useUploadResource()`                                                 | You need file upload into the framework resource store.                 |

The complete panel needs no props:

```tsx
import { ResourcesPanel } from "@agent-native/core/client/resources";

<ResourcesPanel />;
```

For custom resource chrome, keep the hooks and primitives together:

```tsx
import { useState } from "react";
import {
  ResourceEditor,
  ResourceTree,
  useResource,
  useResourceTree,
  useUpdateResource,
} from "@agent-native/core/client/resources";

function WorkspaceResources() {
  const tree = useResourceTree("workspace");
  const updateResource = useUpdateResource();
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const resource = useResource(selectedId);

  return (
    <div className="grid h-full grid-cols-[260px_1fr]">
      <ResourceTree
        tree={tree.data ?? []}
        selectedId={selectedId}
        onSelect={(item) => setSelectedId(item.id)}
        onCreateFile={() => {}}
        onCreateFolder={() => {}}
        onDelete={() => {}}
        onRename={() => {}}
        onDrop={() => {}}
      />
      {resource.data ? (
        <ResourceEditor
          resource={resource.data}
          onSave={(content) =>
            updateResource.mutate({ id: resource.data.id, content })
          }
        />
      ) : null}
    </div>
  );
}
```

## Other Public UI {#other-ui}

| Area          | APIs                                                   | Import path                               |
| ------------- | ------------------------------------------------------ | ----------------------------------------- |
| Sharing       | `<ShareButton>`, `<ShareDialog>`                       | `@agent-native/core/client/sharing`       |
| Sharing UI    | `<VisibilityBadge>`                                    | `@agent-native/toolkit/sharing`           |
| Notifications | `<NotificationsBell>`                                  | `@agent-native/core/client/notifications` |
| Progress      | `<RunsTray>`, progress hooks and types                 | `@agent-native/core/client/progress`      |
| Onboarding    | `useOnboarding()`, onboarding panel hooks              | `@agent-native/core/client/onboarding`    |
| Observability | `<ObservabilityDashboard>`, `<ThumbsFeedback>`         | `@agent-native/core/client/observability` |
| Resources     | `<ResourcesPanel>`, `<ResourceTree>`, resource hooks   | `@agent-native/core/client/resources`     |
| Rich editor   | `<SharedRichEditor>`, slash commands, block node hooks | `@agent-native/core/client/editor`        |

## One-Off Text Completion {#one-off-text-completion}

If you truly need raw text-in/text-out, keep it server-side and use
`completeText()` from `@agent-native/core/server`. Wrap user-facing usage in an
action so the UI and agent share the same capability.

<Callout id="doc-block-11d6llv" tone="warning">

`completeText()` is the escape hatch, not the default. Reach for it only for true text-in/text-out (a label, a one-line rewrite). Anything needing tools, state, auditability, or steering belongs in an action plus `sendToAgentChat({ background: true })`.

</Callout>

```ts
import { defineAction } from "@agent-native/core/action";
import { completeText } from "@agent-native/core/server";
import { z } from "zod";

export default defineAction({
  description: "Classify a short message",
  schema: z.object({ body: z.string() }),
  run: async ({ body }) => {
    const result = await completeText({
      systemPrompt: "Return exactly one label.",
      input: body,
      maxOutputTokens: 12,
      temperature: 0,
    });
    return { label: result.text.trim() };
  },
});
```

Use `sendToAgentChat({ background: true, openSidebar: false })` instead when
the work needs tools, state, auditability, user steering, or multi-step
reasoning.

## What's next {#whats-next}

- [**Drop-in Agent**](/docs/drop-in-agent) — mount `<AgentSidebar>` and `<AgentPanel>` with a tutorial-first walkthrough
- [**Native Chat UI**](/docs/native-chat-ui) — the action result contract behind data widgets and custom renderers
- [**Actions**](/docs/actions) — `defineAction()`, schemas, and the action surface these components call
- [**Real-Time Collaboration**](/docs/real-time-collaboration) — the collab hooks and presence components in depth
- [**Sharing**](/docs/sharing) — `<ShareButton>` and `<ShareDialog>` wiring for ownable resources
- [**Toolkit piece: Toolkit UI Primitives**](/docs/toolkit-ui) — the shared shadcn-style building blocks and app-adapter pattern behind these exports
