# @copilotkit/channels-ui A pure **JSX runtime + intermediate representation (IR) + cross-platform component vocabulary** for authoring rich bot messages. No React, no agent runtime, no Slack — `@copilotkit/channels-ui` depends on nothing in the repo except `@copilotkit/shared` (for `StandardSchemaV1` types). That's what lets a platform adapter (e.g. `@copilotkit/channels-slack`) translate the same UI into Block Kit, while keeping the component layer tree-shakeable and testable in isolation. You author UI as JSX, it normalizes to one serializable IR (`BotNode[]`), and behavior props (`onClick` / `onSelect` / `onSubmit`) ride along on the nodes for the engine (`@copilotkit/channels`) to bind. ## Install ```sh pnpm add @copilotkit/channels-ui ``` To author components as JSX, point the TypeScript JSX factory at this package in the consuming project's `tsconfig.json`: ```jsonc { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@copilotkit/channels-ui", }, } ``` This package ships `@copilotkit/channels-ui/jsx-runtime` (and `/jsx-dev-runtime`) exporting `jsx` / `jsxs` / `Fragment`. Author component files as `.tsx`. ## Example ```tsx import { Message, Header, Section, Actions, Button, renderToIR, } from "@copilotkit/channels-ui"; function Greeting({ name }: { name: string }) { return (
Hello {name}
Pick an option — **bold** and `code` work too.
); } const ir = renderToIR(); // ir is BotNode[] — hand it to an adapter, or let @copilotkit/channels post it. ``` `renderToIR(ui: Renderable): BotNode[]` recursively invokes any component function (passing its props) until only intrinsic string-typed nodes remain; strings in children become `{ type: "text", props: { value } }`; `Fragment` flattens its children. Components must be **pure functions of serializable props** — same props in, same tree out — which is what makes content-stable action binding and re-render rehydration possible in `@copilotkit/channels`. `Renderable` also accepts a `{ raw }` escape hatch, which `renderToIR` passes through as `{ type: "raw", props: { value } }` for adapters that want to short-circuit to a native payload. ## Component vocabulary Each component is a thin function returning a `BotNode` with a stable intrinsic `type` string. An adapter maps these to native primitives. Every component has a fully-typed prop interface (`MessageProps`, `ButtonProps`, …, all exported), and the package ships its own `JSX` namespace (resolved via `jsxImportSource: "@copilotkit/channels-ui"`). So JSX is statically checked: unknown attributes, wrong prop values, and bad children are compile-time errors — `
` or `; ``` `bind(handler, args)` returns a tagged handler; the action registry stores `args` so a cold-path dispatch passes them back via `ctx.action.value`. Keep `args` small — it's the only handler-specific state that survives a restart. ## Exports Runtime: `renderToIR`, `Fragment`, `bind`, and the vocabulary (`Message`, `Header`, `Section`, `Markdown`, `Field`, `Fields`, `Context`, `Actions`, `Button`, `Select`, `Input`, `Image`, `Divider`). Types: `BotNode`, `BotChildren`, `ComponentFn`, `Renderable`, `Thread`, `InteractionContext`, `PlatformUser`, `IncomingMessage`, `MessageRef`, `ClickHandler`, and the per-component prop types (`MessageProps`, `ButtonProps`, `SelectProps`, `TableProps`, `TableColumn`, …).