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 program (types-2.1.304.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 | Ink. Draws the whole tree of the hook. | Box, Text, Button, Input, Select, Link, Code, Markdown, Client, Raster, Image. No Svg. | e. |
desktop | Documented | Claude Code Desktop. Asks over the wire. | Box, Text, Button, Input, Select, Svg, Link, Code, Markdown, Client. | e. |
mobile | Documented | 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 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 carry the prop on as received. A change is refused.
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 | The name of the tool whose call opened the dialog. | string. |
questions | Documented | The tool's questions input, as the dialog draws them. A rewrite must still fit the tool's schema, or the original is drawn. | unknown[]. |
metadata | Documented | 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 is drawn. The stored message stays as it was.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
text | Documented | What the row shows. | string. A rewrite is drawn up to 100000 characters. A teammate block with several frames keeps the engine's drawing. |
origin | Documented | 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 | 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 | What a notification row (origin. task-) reports on: its background task. | User, optional. Read-only. |
from | Documented | Who sent the message: another agent of this session, a teammate, another session, or a channel. | User, optional. Read-only. |
on | Documented | 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. It is raised on every attached surface.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
text | Documented | The block's text in markdown, as the surface will draw it. | string. |
is | Documented | True on the block that draws the bullet that opens a reply. | boolean. |
is | Documented | True on a block that holds a summary of the model's text. | true, optional, read-only. A rewrite that changes it is refused. |
on | Documented | 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 call was decided by tool.call, so a rewrite here changes the row alone. Raised on every attached surface.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool_ | Documented | The id tool. carried for this call. Same value as the row's request. | string. Read-only. |
tool | Documented | Which tool the row draws (Bash, Read, a plugin's tool). | string. |
input | Documented | The call's input, as the model sent it. | unknown. |
is | Documented | True while the call is still running. | boolean. |
is | Documented | True when the call ended in an error. A refusal at the dialog counts. | boolean. |
is | Documented | True when an abort ended the call: the user's Esc or a plugin's $.. | boolean. The row draws Interrupted. |
output | Documented | The stored result once the call has resolved. Undefined while it runs. | unknown, optional. For a failed, refused, or cut call it is the text the model read. |
on | Documented | 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. A rewrite of output is checked against the tool's output schema. One that does not fit draws nothing. The stored result is untouched.
| Item | Status | What it is for | Shape and limits |
|---|---|---|---|
tool_ | Documented | The id of the call this result belongs to. | string. Read-only. |
tool | Documented | Which tool the result belongs to. | string. Read-only. |
output | Documented | The tool's own result object. The same one Tool carries. A rewrite is drawn. | unknown. |
is | Documented | True when the call ended in an error. The row then draws the error text, not output. | boolean. Read-only. |
on | Documented | 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 | The calls of the group, in the order the model made them. | Readonly. |
is | Documented | 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 | 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 | 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 | Which command printed the row, as command. named it (no slash). | string. Read-only. A change or a drop is refused, and the engine draws its own row. |
args | Documented | The arguments of the run, as the echo shows them. *** for a command that marks its arguments sensitive. | string. Read-only. |
text | Documented | The text of the row, in markdown. | string. |
is | Documented | True when the row is the error line of a command that threw. | boolean. Read-only. |
on | Documented | 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 | The tool call the row belongs to. | string. Read-only. While several foreground calls share one pill, the first of them. |
kind | Documented | Which progress row. | 'background_. Read-only. |
hint | Documented | The text of the pill as the engine draws it, dim. For example (ctrl+b to run in background). | string. A rewrite is drawn 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 | 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 | The text drawn instead of the word while a state overrides it. Otherwise null. | string | null. |
suffix | Documented | 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 | 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 | The past-tense word the line drew (Baked). | string. |
duration | Documented | The duration of the turn in milliseconds. | number. |
on | Documented | 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 | The text of the notice, flattened to one string. | string. |
command | Documented | The slash command appended after the text. | string | null. |
on | Documented | 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 | 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 | True while the prompt holds typed text. | boolean. Read-only. |
is | Documented | True while a model turn is running. | boolean. Read-only. |
hint | Documented | The text of the line as the engine draws it, one string. A rewrite replaces the line. | string. |
tail | Documented | Text a hook adds after the line. | string, optional. The terminal draws it dim. It is left 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 | True while a survey holds the band. A hook yields to it. | boolean. Read-only. |
is | Documented | True while a model turn is running. | boolean. Read-only. |
max | Documented | Rows the band may take. In fullscreen, what the bottom slot has left above the prompt. Otherwise the terminal's height. | number. Read-only. The slot is capped at half the terminal's rows. A taller tree scrolls in a window of scroll.. |
body | Documented | Cells the tree is laid out in. | number. Read-only. Size a table to this value, not to viewport.. |
scroll | Documented | The window over a tree taller than max. Engine-owned. Moved by the wheel and by the person's keys while the band is focused. | Site. body is max less the n more row. |
view | Documented | 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 | 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 | True while the person has given the pane the keyboard. Tab walks its elements, a Button's hotkey presses it, and the arrows scroll. | boolean. Read-only. |
body | Documented | Cells across the body, inside the frame. | number. Read-only. |
placement | Documented | 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. A change is refused. |
scroll | Documented | 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 | 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 | 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 | <>.... A column Box around the children. | The same naming rule as h. | Hooks modules and surface modules. |
JSX namespace | Documented | 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 | 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 | A file ends in ., ., ., ., ., ., ., or .. It is an ES module whatever its suffix. | A file of the plugin is imported 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)