---
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 package subpaths:

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

Import reusable Toolkit UI from its focused `@agent-native/toolkit/app/*` or
`@agent-native/toolkit/ui/*` entry. Import Core runtime hooks from
`@agent-native/core/client/*`. Old Core UI import paths that moved to Toolkit
are deprecated tombstones, not compatibility re-exports.

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>

## Prompt Home And Template Libraries {#prompt-home}

`PromptHome`, `PromptHomeLibrary`, `TemplateLibraryGrid`, and `TemplateLibraryCard` are exported from `@agent-native/toolkit/app-shell`. They share home spacing, a centered 700px composer, and a responsive four-column library. The app shell keeps its main landmark. Apps supply real data, localized labels, previews, actions, and routing.

`PromptHome` accepts `title`, `composer`, optional `mobileToolbar`, `connection`, `quickActions`, and library `children`. `PromptHomeLibrary` takes controlled `value` (`templates` or `recent`), `onValueChange`, `labels`, `showRecent`, `templates`, and optional `recent`, `browseAll`, and `recentActions`. With no recents it displays templates without a fake tab. `browseAll` is a real link supplied by the app. The semantic `Tabs.headerActions` slot keeps active actions beside the tabs.

```tsx
import { PromptHome, PromptHomeLibrary } from "@agent-native/toolkit/app-shell";
import { Link } from "react-router";

<PromptHome
  title={labels.create}
  composer={composer}
  quickActions={quickActions}
>
  <PromptHomeLibrary
    value={tab}
    onValueChange={setTab}
    labels={labels.library}
    showRecent={hasOwnedWork}
    browseAll={<Link to="/templates">{labels.browseAll}</Link>}
    templates={templateGrid}
    recent={recentItems}
  />
</PromptHome>;
```

`TemplateLibraryGrid` accepts typed `items` with `id`, `title`, optional stored `description` and `disabled`, plus `renderPreview`. It supports `selectedId`, `pendingId`, `disabled`, `loading`, `error`, `onRetry`, optional `empty`, and injected `labels` for loading, empty, and retry. Choose `onSelect(item, element)` or `renderLink(item, children)`, never both. Image and caption share one activation target. `renderActions` stays outside the activation target, inline beside the title and optional description. Cards share fixed preview and caption dimensions; direct activation uses or copies the template, while Preview belongs only in the caption menu. Loaded items remain visible after refresh errors. `TemplateLibraryCard` exposes the same behavior for one `item`, with `preview`, optional `actions`, `selected`, `pending`, and `disabled`.

```tsx
import { TemplateLibraryGrid } from "@agent-native/toolkit/app-shell";

<TemplateLibraryGrid
  items={templates}
  labels={labels.grid}
  renderPreview={renderTemplatePreview}
  onSelect={selectTemplate}
  selectedId={selectedId}
  pendingId={pendingId}
  loading={loading}
  error={error}
  onRetry={reloadTemplates}
/>;
```

`TemplatePreviewDialog`, exported from the same entry point, uses the semantic `Dialog` with `size="viewport"` by default or `size="large"`. It takes controlled `open`, `onOpenChange`, `title`, and app-rendered `children`; the header has no use action. Optional `thumbnails` contain `{ id, title, preview, disabled? }` and require `selectedId` plus `onSelectedIdChange`. The rail becomes horizontal on narrow screens and supports arrow keys, Home, and End.

The `viewport` size is an inset, centered modal: 90dvh tall, capped at 96rem wide, with 1rem mobile side margins and 1.5rem on wider screens. It preserves the standard dialog border, radius, shadow, and backdrop. Caption menus appear on card hover or keyboard focus on hover-capable devices and remain visible on touch devices; their reserved space never changes.

Pass localized `labels` (`close`, `loading`, `empty`, `retry`, `thumbnails`), optional `loading`, `error`, `empty`, and `onRetry` for exclusive readiness states. `initialFocusRef` and `restoreFocusRef` control focus. Open the viewer from the caption menu after its close handoff; keep reads, cancellation, authorization, and safe isolated rendering in the app.

```tsx
import { TemplatePreviewDialog } from "@agent-native/toolkit/app-shell";

<TemplatePreviewDialog
  open={previewOpen}
  onOpenChange={setPreviewOpen}
  title={template.title}
  labels={labels.preview}
  loading={previewLoading}
  error={previewError}
  empty={!pages.length}
  onRetry={reloadPreview}
  restoreFocusRef={menuTriggerRef}
  thumbnails={pages.map((page) => ({
    id: page.id,
    title: page.title,
    preview: renderThumbnail(page),
  }))}
  selectedId={selectedPageId}
  onSelectedIdChange={setSelectedPageId}
>
  {renderSelectedPage(selectedPageId)}
</TemplatePreviewDialog>;
```

Toolkit styles include the shared geometry. `@agent-native/toolkit/app-shell/prompt-home.css` is the focused stylesheet export. The existing `toolkit/app-shell` ejection unit includes the layout and stylesheet. Data loading, authorization, import workflows, and composer state stay app-owned.

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

| API                                  | Import path                            | Use when                                                                                         |
| ------------------------------------ | -------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `<AgentSidebar>`                     | `@agent-native/toolkit/app/chat`       | You want the complete sidebar around your app.                                                   |
| `<AgentToggleButton>`                | `@agent-native/toolkit/app/chat`       | You render your own header button for the sidebar.                                               |
| `<AgentPanel>`                       | `@agent-native/toolkit/app/chat`       | You want the full panel in your own layout, route, dialog, or side column.                       |
| `<AgentChatSurface>`                 | `@agent-native/toolkit/app/chat`       | You want chat in panel or page mode without the sidebar wrapper.                                 |
| `<AssistantChat>`                    | `@agent-native/toolkit/app/chat`       | You want to own surrounding chrome while keeping the standard conversation and composer runtime. |
| `<MultiTabAssistantChat>`            | `@agent-native/toolkit/app/chat`       | You want the framework's thread tabs without `AgentPanel` chrome.                                |
| `createHttpAgentChatRuntime()`       | `@agent-native/core/client/agent-chat` | You have a BYO agent endpoint that streams normalized chat events.                               |
| `createOpenAIAgentsChatRuntime()`    | `@agent-native/core/client/agent-chat` | You have an OpenAI Agents SDK stream and want the standard chat UI around it.                    |
| `createOpenAIResponsesChatRuntime()` | `@agent-native/core/client/agent-chat` | You have an OpenAI Responses event stream and want it normalized into the chat UI.               |
| `createAgUiChatRuntime()`            | `@agent-native/core/client/agent-chat` | You have an AG-UI event stream and want it normalized into the chat UI.                          |
| `createClaudeAgentChatRuntime()`     | `@agent-native/core/client/agent-chat` | You have a Claude Agent SDK stream and want it normalized into the chat UI.                      |
| `createVercelAiChatRuntime()`        | `@agent-native/core/client/agent-chat` | You have a Vercel AI SDK stream and want it normalized into the chat UI.                         |
| `useChatThreads()`                   | `@agent-native/core/client/agent-chat` | You need a custom thread list, history picker, or scoped chat UI.                                |
| `sendToAgentChat()`                  | `@agent-native/core/client/agent-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/toolkit/app/chat";

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

For custom chrome around the standard runtime:

```tsx
import { AssistantChat } from "@agent-native/toolkit/app/chat";
import { useChatThreads } from "@agent-native/core/client/agent-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/toolkit/app/chat/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/toolkit/app/chat/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/toolkit/app/chat/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.

### Staged composer context {#composer-context}

`PromptComposer` and `TiptapComposer` accept `submissionDisabled` to block sending while leaving draft, file, and context staging enabled. With `requireAgentEngine`, missing provider setup closes `PromptComposer` to typing while a setup card is on screen, and a click on it points at the card. With no card on screen, it blocks submission only. `disabled` still disables the full composer.

`PromptComposer`, `TiptapComposer`, and `AgentKitComposer` accept `contextItems` and `contextMenuItems`. Apps own source reads and localized menu labels.

Each `AgentChatContextItem` has `key`, `title`, and `context`. Optional `status` is `ready`, `pending`, or `error`. `statusMessage` explains failures. `onRemoveContextItem`, `onInspectContextItem`, and `onRetryContextItem` receive the item key. `removable: false` hides removal. Pending or failed items block submission unless `blocksSubmission: false` explicitly keeps them informational.

A host can stage already-authorized context and receive the frozen snapshot on submission:

```tsx
<PromptComposer
  contextItems={contextItems}
  onRemoveContextItem={removeContext}
  onSubmit={(text, files, references, options) =>
    submitPrompt({
      text,
      files,
      references,
      contextItems: options.contextItems,
    })
  }
/>
```

Context sources register `picker` on a `ComposerContextMenuItem`. `ComposerContextMenu` owns connected shadcn/Radix cascading submenus, not replacement panels: the root offers file upload, then Add context → host categories → source pickers. Add context is omitted when there are no host entries. Arrow keys and Escape navigate the menu hierarchy. Back within a link picker only returns from results to link entry in the same flyout. The shared picker owns search, list rows, loading, empty and error states, retry, pagination, and link entry. Apps provide authorized data and selection callbacks, not their own picker JSX.

The picker configuration accepts:

- `searchPlaceholder` and optional `emptyMessage` as localized app labels. `selectedIds` marks selected rows. Items have `id`, `title`, optional `disabled`, and optional `url`.
- Either local `items` with optional `loading`, `error`, and `onRetry()`, or remote `load(request)`. These modes cannot be combined.
- `load(request)` receives `search`, one-based `page`, optional `cursor` and `url`, and an `AbortSignal`. It returns `{ items, hasMore?, nextCursor? }`. Failures must reject, not return an empty result.
- `onSelect(item, request)` stages the source. It may be asynchronous. Returning `false` keeps the picker open. Throwing shows an error with retry. Optional `clearSelection` supplies a localized `label` and an `onSelect()` callback.
- Optional `link` supplies localized `placeholder` and `submitLabel` strings. `validate(url)` may return an error string. The shared form owns link input and navigation.
- `scopeKey` resets all picker state when identity or access scope changes. `refreshKey` invalidates the current query when a source/version token changes without resetting the URL. Search changes return to page one. Apps pass refreshed tokens when agent writes change their source data.

An app can register already-authorized choices. Here, `labels` contains localized app copy and `stageReference` updates the host's staged context:

```tsx
import type { ComposerContextMenuItem } from "@agent-native/toolkit/app/chat/composer";
import { Link } from "react-router";

const contextMenuItems: ComposerContextMenuItem[] = [
  {
    id: "references",
    label: labels.references,
    picker: {
      searchPlaceholder: labels.searchReferences,
      items: authorizedReferences,
      selectedIds,
      onSelect: stageReference,
      footerAction: {
        label: labels.createReference,
        renderLink: (children) => (
          <Link to={createReferenceHref}>{children}</Link>
        ),
      },
    },
  },
];
```

For a searchable or linked source, the app supplies its authenticated loader. `loadAuthorizedReferences` implements the request/result contract above. It preserves access checks and forwards the abort signal to its client helper:

```tsx
const contextMenuItems: ComposerContextMenuItem[] = [
  {
    id: "references",
    label: labels.references,
    picker: {
      searchPlaceholder: labels.searchReferences,
      scopeKey: accessScope,
      refreshKey: sourceVersion,
      load: loadAuthorizedReferences,
      presentation: {
        type: "dialog",
        mode: "multiple",
        onAttach: stageReferences,
      },
      link: {
        label: labels.referenceUrl,
        placeholder: labels.referenceLink,
        submitLabel: labels.findReferences,
        validate: validateReferenceLink,
      },
    },
  },
];
```

`presentation` defaults to `"submenu"`. With `{ type: "dialog", mode: "multiple", onAttach }`, an ordinary menu item opens a shared dialog titled by the entry label. `link.label` labels its URL input. Continue validates an HTTP(S) URL without credentials before loading results. Blank or invalid submission never calls the provider. `link.validate()` can enforce provider-specific rules. Dialog buttons use shared localized labels, not `link.submitLabel`.

`presentation.onAttach(items, request)` receives one frozen batch of newly checked items. This mode excludes `onSelect`. Choices survive search and pagination, and `selectedIds` are checked, disabled, and omitted from the batch. IDs must distinguish different source files. Returning `false` preserves the dialog. A rejection displays a retryable error. Cancellation or a `scopeKey` change aborts pending work and ignores late completion. Reopening starts with a fresh URL. Hosts must honor `request.signal` before asynchronous callbacks commit changes. `footerAction` and `clearSelection` remain submenu controls.

`{ type: "dialog", mode: "url" }` uses Attach directly, without a results loader. Its `onSelect(item, request)` receives `{ id: url, title: url, url }` and `request.url`:

```tsx
const websiteContextItem: ComposerContextMenuItem = {
  id: "website",
  label: labels.attachWebsite,
  picker: {
    searchPlaceholder: labels.searchReferences,
    scopeKey: accessScope,
    presentation: { type: "dialog", mode: "url" },
    link: {
      label: labels.websiteUrl,
      placeholder: labels.websiteUrl,
      submitLabel: labels.attach,
      validate: validateWebsiteUrl,
    },
    onSelect: stageWebsiteReference,
  },
};
```

`readComposerWebsiteSource(url, extractCallback)` from `@agent-native/core/server` builds website references through the host's existing SSRF-safe extractor. `composerSourceRequestSchema` from `@agent-native/core/shared` accepts `source: "website"` only with `operation: "read"` and a credential-free HTTP(S) `url` of at most 2,048 characters. The helper returns `{ id, title, url, context }` with a stable URL-hash ID and at most 20,000 context characters. Extraction status, warnings, rendering provenance, and any `[truncated]` marker remain in the context text. Partial extraction stays explicit. Failed extraction or missing usable `designMd` throws a typed failure. Authorization still belongs in the host's [action](/docs/actions-overview).

Picker entries do not also define `render` or entry-level `onSelect`. Categories use `children`. The root and every category keep a searchable header. Categories can supply a localized `searchPlaceholder`; without it they use the shared context-search placeholder. `picker.searchPlaceholder` labels the source list's search input. Registration does not grant source access. The shared picker ignores superseded load responses.

Optional `footerAction` stays below the results even during loading, errors, or an empty list. It accepts a localized `label`, optional `icon` and `disabled`, and either `onSelect()` or `renderLink(children)`. Use a callback for a modal or another stateful workflow: `false` keeps the menu open, `true` or `void` closes it, and rejection displays an error with retry. For pure navigation, return a real React Router `Link` or native anchor from `renderLink`; the shared menu supplies the row through `asChild`, preserving its `href` and browser navigation semantics. Wire existing authorized workflows, not placeholder actions.

Passing `addAttachment` to `ComposerContextMenu` adds the standard upload action first and preserves the host's file callback.

`PromptComposer` and `AgentKitComposer` accept `attachmentAdapter`, using the public `AttachmentAdapter` type from `@assistant-ui/react`. Supply the host's file-acceptance, size-validation, and staging policy through this adapter; omit it to retain the default adapters. The shared composer still owns the upload control, file picker, context picker, and attachment chips. Do not replace those controls to customize file staging.

`inlineTextAttachments` defaults to `true` on `PromptComposer` and `AgentKitComposer`. Set it to `false` when the host already extracts ordinary text files: the composer passes the original files to the host without reading and appending their contents to the prompt. Synthetic pasted-text attachments still become prompt text. This opt-out does not change attachment acceptance or the shared controls.

```tsx
<PromptComposer
  contextItems={contextItems}
  contextMenuItems={contextMenuItems}
  attachmentAdapter={hostAttachmentAdapter}
  onRetryContextItem={retryContext}
  onSubmit={submitPrompt}
/>
```

The snapshot preserves `undefined` versus an explicit empty array. It rejects more than 32 items or 64 KiB of serialized UTF-8 data without truncation. `AgentKitComposer.beforeSend(submission)` is awaited before send or queue and receives frozen context, references, uploaded attachments, and run options. Rejection preserves the draft. Context is included in message text and metadata.

`composerRef.current.submitWithText(text)` uses the same submission path with current files and model settings without replacing the editor first. It resolves to `true` after acceptance or `false` when blocked or rejected. Context selection is not an access grant. Apps must resolve and recheck source access through their authenticated actions.

## Conversation Rendering {#conversation}

Use `@agent-native/toolkit/app/chat/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. |

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. `<AgentConversation>` already keeps itself pinned to the
bottom while streaming — do not add your own scroll handling on top of it.

## 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/widgets` 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/toolkit/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/toolkit/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/toolkit/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/toolkit/app/resources` for the reusable panel, tree, and editor. Use
`@agent-native/core/client/resources` for resource data hooks and mutations.

| 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/toolkit/app/resources";

<ResourcesPanel />;
```

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

```tsx
import { useState } from "react";
import {
  ResourceEditor,
  ResourceTree,
} from "@agent-native/toolkit/app/resources";
import {
  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                                        |
| --------------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
| Agent page      | `<AgentTabsPage>`                                                              | `@agent-native/toolkit/app/agent-page`             |
| Blocks UI       | `BlockView`, `SchemaBlockEditor`, `BlockRegistryProvider`                      | `@agent-native/toolkit/app/blocks`                 |
| Canvas          | `DrawOverlay`, `CanvasCommentPins`                                             | `@agent-native/toolkit/canvas-annotations`         |
| Chat history    | `ChatHistoryList`, `ChatHistoryRail`                                           | `@agent-native/toolkit/chat-history`               |
| Owned chat rail | `useOwnershipChatHistory()`, `OwnershipChatHistoryRail`                        | `@agent-native/toolkit/app/chat/ownership-history` |
| Data grid       | `<DataGrid>`                                                                   | `@agent-native/toolkit/data-grid`                  |
| Dashboard       | `DataTable`, `DateRangePicker`, `GenericChartPanel`, `MetricCard`, `StatsCard` | `@agent-native/toolkit/dashboard`                  |
| Sharing         | `<ShareButton>`, `<ShareDialog>`                                               | `@agent-native/toolkit/app/sharing`                |
| Sharing UI      | `<VisibilityBadge>`                                                            | `@agent-native/toolkit/sharing`                    |
| Notifications   | `<NotificationsBell>`                                                          | `@agent-native/toolkit/app/notifications`          |
| Progress        | `<RunsTray>`                                                                   | `@agent-native/toolkit/app/progress`               |
| Run registry    | Run types and server registry functions                                        | `@agent-native/core/progress`                      |
| Onboarding      | `useOnboarding()`, onboarding panel hooks                                      | `@agent-native/core/client/onboarding`             |
| Observability   | `<ObservabilityDashboard>`, `<ThumbsFeedback>`                                 | `@agent-native/toolkit/app/observability`          |
| Resources UI    | `<ResourcesPanel>`, `<ResourceTree>`, `<ResourceEditor>`                       | `@agent-native/toolkit/app/resources`              |
| Resource hooks  | Resource data hooks                                                            | `@agent-native/core/client/resources`              |
| Rich editor     | `<SharedRichEditor>`, slash commands, block node hooks                         | `@agent-native/toolkit/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-overview) — `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
