Surfaces, Elements, and Render Sites
An element is a building block of a drawing, such as Box, Text, or Button.
A render site is a part of Claude Code that a mod may redraw, such as the spinner or the prompt hint.
A ui.render hook gets the site in e.component and returns a tree of elements.
The 15 render sites are AskUserQuestion, UserMessage, AssistantMessage, ToolUse, ToolResult, ToolGroup, ToolProgress, CommandOutput, Spinner, TurnDuration, InfoNotice, SessionMode, PromptHint, AbovePrompt, and Pane.
The engine alone draws the permission dialog. A mod adds context to it with $.ui.notice.
Return the engine's tree (call next(e)) to leave a site as it is. JSX needs a .tsx or .jsx file.
Official pages: Draw in the Interface, Mods Overview, Mods Reference. Source: the Mod API type file of the 2.1.312 program (schemas/claude-code.d.ts).
Example
This hook adds a tail to the hint line under the prompt.
on("ui.render", { component: "PromptHint" }, async ($, e, next) =>
next({ ...e, props: { ...e.props, tail: " (mod)" } }),
);
There are no string tags in JSX.
Every tag is an element from $.ui.resolve(e), and JSX compiles a tag to h(Box, props, ...children).
on("ui.render", { component: "Pane" }, async ($, e) => {
const { Box, Text } = $.ui.resolve(e);
return (
<Box>
<Text>Hello</Text>
</Box>
);
});
Surfaces
| Item | Status | What it does | Elements | Where it can be used |
|---|---|---|---|---|
terminal | Documented: An official docs page names this item. | Ink. Draws the whole tree of the hook. | Box, Text, Button, Input, Select, Link, Code, Markdown, Client, Raster, Image. No Svg. | e. |
desktop | Documented: An official docs page names this item. | Claude Code Desktop. Asks over the wire. | Box, Text, Button, Input, Select, Svg, Link, Code, Markdown, Client. | e. |
mobile | Documented: An official docs page names this item. | The mobile app. Asks over the wire. | Box, Text, Button, Svg, Link, Code, Markdown. No Input, Select, or Client yet. The docs name no mobile surface. | e. |
vscode | Differs: The official docs and the program disagree. The row says how. The type file says no surface is attached yet. The docs say the extension draws from 2.1.299 in a session that fetches feature flags. | The editor webview. The same table as desktop without Client. | Box, Text, Button, Input, Select, Svg, Link, Code, Markdown. | e. |
Render Sites and Props
"Read-only" means a rewrite must keep the prop as received. The engine refuses a change.
Many sites have onScreen (type OnScreen \| null, optional, read-only): which of its rows the viewport shows now. It is null while drawn outside the viewport, and absent where the surface does not say.
AskUserQuestion
The dialog the AskUserQuestion tool opens. A tree for it holds the engine's node once. At most 12 rows on the terminal and 48 on other surfaces. Markdown works on other surfaces only.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool | Documented: An official docs page names this item. | The name of the tool whose call opened the dialog. | string. |
questions | Documented: An official docs page names this item. | The tool's questions input, as the dialog draws them. A rewrite must still fit the tool's schema, or the engine draws the original. | unknown[]. |
metadata | Documented: An official docs page names this item. | The call's metadata. (who asked), when the model gave one. Analytics only, never drawn. | string, optional. |
UserMessage
A user-role transcript row: the person's prompt, a background task notification, or a message from another agent, teammate, or session. A rewrite of text changes only what the surface draws. The stored message stays as it was.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
text | Documented: An official docs page names this item. | What the row shows. | string. The engine draws a rewrite up to 100000 characters. A teammate block with several frames keeps the engine's drawing. |
origin | Documented: An official docs page names this item. | Where the stored message came from, as prompt. named it: composer, task notification, peer, channel, or plugin. | Prompt. Read-only. unclassified for a teammate's message and for a message stored before stamps. |
is | Documented: An official docs page names this item. | Whether the view draws the row in full: the ctrl+o transcript, --, or a surface with no ctrl+o. | boolean. Read-only. When false, a message row is one dim line that names the sender. |
task | Documented: An official docs page names this item. | What a notification row (origin. task-) reports on: its background task. | User, optional. Read-only. |
from | Documented: An official docs page names this item. | Who sent the message: another agent of this session, a teammate, another session, or a channel. | User, optional. Read-only. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
AssistantMessage
One block of an assistant reply: its text, or a summary of the text the model wrote between two tool calls. A rewrite changes the drawing and leaves the stored message alone. The engine raises it on every attached surface.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
text | Documented: An official docs page names this item. | The block's text in markdown, as the surface will draw it. | string. |
is | Documented: An official docs page names this item. | True on the block that draws the bullet that opens a reply. | boolean. |
is | Documented: An official docs page names this item. | True on a block that holds a summary of the model's text. | true, optional, read-only. The engine refuses a rewrite that changes it. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
ToolUse
A tool call's row in the transcript, such as Bash(ls -la) and its result. The tool.call event decided the call, so a rewrite here changes the row alone. The engine raises it on every attached surface.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool_ | Documented: An official docs page names this item. | The id tool. carried for this call. Same value as the row's request. | string. Read-only. |
tool | Documented: An official docs page names this item. | Which tool the row draws (Bash, Read, a plugin's tool). | string. |
input | Documented: An official docs page names this item. | The call's input, as the model sent it. | unknown. |
is | Documented: An official docs page names this item. | True while the call is still running. | boolean. |
is | Documented: An official docs page names this item. | True when the call ended in an error. A refusal at the dialog counts. | boolean. |
is | Documented: An official docs page names this item. | True when an abort ended the call: the user's Esc or a plugin's $.. | boolean. The row draws Interrupted. |
output | Documented: An official docs page names this item. | The stored result once the call resolves. Undefined while it runs. | unknown, optional. For a failed, refused, or cut call it is the text the model read. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
ToolResult
The result block under a standalone tool row, which the tool's own result renderer draws from output. The engine checks a rewrite of output against the tool's output schema. A rewrite that does not fit draws nothing. The stored result stays as it was.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool_ | Documented: An official docs page names this item. | The id of the call this result belongs to. | string. Read-only. |
tool | Documented: An official docs page names this item. | Which tool the result belongs to. | string. Read-only. |
output | Documented: An official docs page names this item. | The tool's own result object. The same one Tool carries. A rewrite is drawn. | unknown. |
is | Documented: An official docs page names this item. | True when the call ended in an error. The row then draws the error text, not output. | boolean. Read-only. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
ToolGroup
A run of tool calls that the transcript folds into one count line, such as Read 3 files, ran 2 shell commands. A hook that sets isExpanded unfolds the group. Each unfolded row is a ToolUse. The fullscreen ctrl+o view draws no ToolGroup.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
calls | Documented: An official docs page names this item. | The calls of the group, in the order the model made them. | Readonly. |
is | Documented: An official docs page names this item. | True while the group is the live one. A call in it may still run, and the model's next call may join it. | boolean. |
is | Documented: An official docs page names this item. | Whether each call draws as its own Tool row, or the group draws one line. | boolean. True under -- and in the non-fullscreen ctrl+o transcript. The one prop a rewrite changes on screen. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
CommandOutput
The output row a slash command printed in the transcript, under its echo. A rewrite of text changes only what is drawn. The model still reads the stored row.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
command | Documented: An official docs page names this item. | Which command printed the row, as command. named it (no slash). | string. Read-only. The engine refuses a change or a drop, and draws its own row. |
args | Documented: An official docs page names this item. | The arguments of the run, as the echo shows them. *** for a command that marks its arguments sensitive. | string. Read-only. |
text | Documented: An official docs page names this item. | The text of the row, in markdown. | string. |
is | Documented: An official docs page names this item. | True when the row is the error line of a command that threw. | boolean. Read-only. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
ToolProgress
One row of the live tool-progress region under a running tool call. Today it is the run-in-background pill. A hook matches { props: { kind: "background_hint" } }.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool_ | Documented: An official docs page names this item. | The tool call the row belongs to. | string. Read-only. While several foreground calls share one pill, the first of them. |
kind | Documented: An official docs page names this item. | Which progress row. | 'background_. Read-only. |
hint | Documented: An official docs page names this item. | The text of the pill as the engine draws it, dim. For example (ctrl+b to run in background). | string. The engine draws a rewrite in its place. "" draws nothing. |
Spinner
The line that animates while a turn runs, such as Sauteing... (12s, 300 tokens). On the desktop it is the row that carries the mark of the turn.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
word | Documented: An official docs page names this item. | The animated word (Sauteing), as sampled for this turn. | string. On the desktop it is what the step is doing (Creating notes.), or Working. |
message | Documented: An official docs page names this item. | The text drawn instead of the word while a state overrides it. Otherwise null. | string | null. |
suffix | Documented: An official docs page names this item. | What the engine draws after the word to say the turn is still going: one ellipsis character. | string. Not added to a text that already ends in an ellipsis. |
mode | Documented: An official docs page names this item. | What the turn is doing. | 'requesting' | 'responding' | 'thinking' | 'tool-. The desktop never says tool-. |
TurnDuration
The line that closes a turn in the transcript, such as Baked for 3s. Only the terminal surface raises it. A remote surface draws its own footer.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
word | Documented: An official docs page names this item. | The past-tense word the line drew (Baked). | string. |
duration | Documented: An official docs page names this item. | The duration of the turn in milliseconds. | number. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
InfoNotice
One dim status line under the logo (the model source, an experiment enrollment, a settings hint), with a trailing /command. Terminal only.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
text | Documented: An official docs page names this item. | The text of the notice, flattened to one string. | string. |
command | Documented: An official docs page names this item. | The slash command appended after the text. | string | null. |
on | Documented: An official docs page names this item. | Which rows are in the viewport. | See above. |
SessionMode
The dim mode labels at the right of the prompt footer (focus, memory paused), joined by &. One instance. Terminal and desktop only. A hook adds a mode by rewriting modes, removes one by filtering, or draws its own tree.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
modes | Documented: An official docs page names this item. | The labels the footer shows, in order. Empty when there are none. | readonly string[]. |
PromptHint
The dim hint line under the prompt (? for shortcuts, esc to interrupt, and the pills beside them). One instance.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
is | Documented: An official docs page names this item. | True while the prompt holds typed text. | boolean. Read-only. |
is | Documented: An official docs page names this item. | True while a model turn is running. | boolean. Read-only. |
hint | Documented: An official docs page names this item. | The text of the line as the engine draws it, one string. A rewrite replaces the line. | string. |
tail | Documented: An official docs page names this item. | Text a hook adds after the line. | string, optional. The terminal draws it dim. The terminal leaves it out when fewer than four columns would show. |
AbovePrompt
The band directly above the prompt input, where surveys draw. The engine draws a [-] beside the tree and an n more row under it. A hook draws one tree or passes. The person collapses it (ctrl+x ctrl+a) or focuses it (a click, ctrl+x tab).
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
has | Documented: An official docs page names this item. | True while a survey holds the band. A hook yields to it. | boolean. Read-only. |
is | Documented: An official docs page names this item. | True while a model turn is running. | boolean. Read-only. |
max | Documented: An official docs page names this item. | Rows the band may take. In fullscreen, what remains of the bottom slot above the prompt. Otherwise the terminal's height. | number. Read-only. The slot has a cap of half the terminal's rows. A taller tree scrolls in a window of scroll.. |
body | Documented: An official docs page names this item. | Cells the tree is laid out in. | number. Read-only. Size a table to this value, not to viewport.. |
scroll | Documented: An official docs page names this item. | The window over a tree taller than max. Engine-owned. The wheel and the person's keys move it while the band has focus. | Site. body is max less the n more row. |
view | Documented: An official docs page names this item. | Which transcript is on screen above the band: the main conversation or one agent's. | Site. Read-only. A switch of view re-runs the hook. |
Pane
The framed region a plugin opened with $.ui.open({ id }). There is one instance for each id (requestId). The body is the hook's tree. One pane shows, the rest are tabs. The surface places it (docked in fullscreen, otherwise above the prompt). The person closes it, which raises ui.close with origin person, and a hook may refuse.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
title | Documented: An official docs page names this item. | The label of its tab while more than one pane is open. | string. Read-only here. Another $., or a hook on ui., retitles it. |
is | Documented: An official docs page names this item. | True while the pane holds the keyboard, which the person gave it. Tab walks its elements, a Button's hotkey presses it, and the arrows scroll. | boolean. Read-only. |
body | Documented: An official docs page names this item. | Cells across the body, inside the frame. | number. Read-only. |
placement | Documented: An official docs page names this item. | Where the surface seated the pane: dock beside the transcript (the terminal in fullscreen from 110 columns), or inline above the prompt. | 'dock' | 'inline'. Read-only. The engine refuses a change. |
scroll | Documented: An official docs page names this item. | The window of the body over the tree. Engine-owned. | Site. body is the room the frame may use. Inline, the frame fits the tree. |
view | Documented: An official docs page names this item. | Which transcript is on screen beside the pane. | Site. The pane stays open across a switch of view. |
JSX and Globals
| Item | Status | What it does | How it works | Where it can be used |
|---|---|---|---|---|
h(tag, props, ... | Documented: An official docs page names this item. | The JSX factory (classic runtime). Returns a plain-data element from a string tag or a component. | JSX compiles to bare h(... calls. A module must not declare, import, or take as a parameter anything named h or Fragment where it writes JSX. The engine adds the pragma. | Hooks modules and surface modules. |
Fragment | Documented: An official docs page names this item. | <>.... A column Box around the children. | The same naming rule as h. | Hooks modules and surface modules. |
JSX namespace | Documented: An official docs page names this item. | Element is Render. Intrinsic is empty, so there are no string tags. Intrinsic allows key. | Every tag is a constructor from $., as in const { Box, Text } = $.. | Files named . or .. |
| Web globals | Documented: An official docs page names this item. | URL, URLSearch, Text, Text, Abort, Abort, atob, btoa, structured, crypto., crypto., crypto., and performance.. | There is no DOM, no Node, no require, and no import(). eval and new Function over a string throw. There is no Web. A surface module also has console and no timers. | Hooks modules and surface modules. |
| Module rules | Documented: An official docs page names this item. | A file ends in ., ., ., ., ., ., ., or .. It is an ES module whatever its suffix. | A module imports a file of the plugin with an import declaration. A module with import() does not load. on, $, $. names, and $. references must be spelled literally. | Hooks modules. |
Related Pages
- Mod API Overview
- UI Methods
- UI Events
- UI Types (element props and results)