Ink, Yoga, and Reactive UI System
Claude Code uses a terminal-based reactive UI system built with Ink, Yoga, and React. The system renders rich, interactive components with responsive layouts in a text-based environment, showing how modern UI paradigms can work in terminal applications.
Core UI Architecture
The UI architecture applies React component patterns to terminal rendering through the Ink library. This approach enables composition, state management, and declarative UIs in text-based interfaces.
Entry Points and Initialization
The main entry point is in anon-kode/src/entrypoints/cli.tsx
, which initializes the application:
// Main render entry point
render(
<SentryErrorBoundary>
<App persistDir={persistDir} />
</SentryErrorBoundary>,
{
// Prevent Ink from exiting when no active components are rendered
exitOnCtrlC: false,
}
)
The application then mounts the REPL (Read-Eval-Print Loop) component, which serves as the primary container for the UI.
Component Hierarchy
The UI component hierarchy follows this structure:
- REPL (
src/screens/REPL.tsx
) - Main container- Logo - Branding display
- Message Components - Conversation rendering
- AssistantTextMessage
- AssistantToolUseMessage
- UserTextMessage
- UserToolResultMessage
- PromptInput - User input handling
- Permission Components - Tool use authorization
- Various dialogs and overlays
State Management
The application uses React hooks extensively for state management:
- useState for local component state (messages, loading, input mode)
- useEffect for side effects (terminal setup, message logging)
- useMemo for derived state and performance optimization
- Custom hooks for specialized functionality:
useTextInput
- Handles cursor and text entryuseArrowKeyHistory
- Manages command historyuseSlashCommandTypeahead
- Provides command suggestions
Ink Terminal UI System
Ink allows React components to render in the terminal, enabling a component-based approach to terminal UI development.
Ink Components
The application uses these core Ink components:
- Box - Container with flexbox-like layout properties
- Text - Terminal text with styling capabilities
- Static - Performance optimization for unchanging content
- useInput - Hook for capturing keyboard input
Terminal Rendering Challenges
Terminal UIs face unique challenges addressed by the system:
- Limited layout capabilities - Solved through Yoga layout engine
- Text-only interface - Addressed with ANSI styling and borders
- Cursor management - Custom
Cursor.ts
utility for text input - Screen size constraints -
useTerminalSize
for responsive design - Rendering artifacts - Special handling for newlines and clearing
Terminal Input Handling
Input handling in the terminal requires special consideration:
function useTextInput({
value: originalValue,
onChange,
onSubmit,
multiline = false,
// ...
}: UseTextInputProps): UseTextInputResult {
// Manage cursor position and text manipulation
const cursor = Cursor.fromText(originalValue, columns, offset)
function onInput(input: string, key: Key): void {
// Handle special keys and input
const nextCursor = mapKey(key)(input)
if (nextCursor) {
setOffset(nextCursor.offset)
if (cursor.text !== nextCursor.text) {
onChange(nextCursor.text)
}
}
}
return {
onInput,
renderedValue: cursor.render(cursorChar, mask, invert),
offset,
setOffset,
}
}
Yoga Layout System
Yoga provides a cross-platform layout engine that implements Flexbox for terminal UI layouts.
Yoga Integration
Rather than direct usage, Yoga is integrated through:
- The
yoga.wasm
WebAssembly module included in the package - Ink's abstraction layer that interfaces with Yoga
- React components that use Yoga-compatible props
Layout Patterns
The codebase uses these core layout patterns:
- Flexbox Layouts - Using
flexDirection="column"
or"row"
- Width Controls - With
width="100%"
or pixel values - Padding and Margins - For spacing between elements
- Borders - Visual separation with border styling
Styling Approach
Styling is applied through:
- Component Props - Direct styling on Ink components
- Theme System - In
theme.ts
with light/dark modes - Terminal-specific styling - ANSI colors and formatting
Performance Optimizations
Terminal rendering requires special performance techniques:
Static vs. Dynamic Rendering
The REPL component optimizes rendering by separating static from dynamic content:
<Static key={`static-messages-${forkNumber}`} items={messagesJSX.filter(_ => _.type === 'static')}>
{_ => _.jsx}
</Static>
{messagesJSX.filter(_ => _.type === 'transient').map(_ => _.jsx)}
Memoization
Expensive operations are memoized to avoid recalculation:
const messagesJSX = useMemo(() => {
// Complex message processing
return messages.map(/* ... */)
}, [messages, /* dependencies */])
Content Streaming
Terminal output is streamed using generator functions:
for await (const message of query([...messages, lastMessage], /* ... */)) {
setMessages(oldMessages => [...oldMessages, message])
}
Integration with Other Systems
The UI system integrates with other core components of Claude Code.
Tool System Integration
Tool execution is visualized through specialized components:
- AssistantToolUseMessage - Shows tool execution requests
- UserToolResultMessage - Displays tool execution results
- Tool status tracking using ID sets for progress visualization
Permission System Integration
The permission system uses UI components for user interaction:
- PermissionRequest - Base component for authorization requests
- Tool-specific permission UIs - For different permission types
- Risk-based styling with different colors based on potential impact
State Coordination
The REPL coordinates state across multiple systems:
- Permission state (temporary vs. permanent approvals)
- Tool execution state (queued, in-progress, completed, error)
- Message history integration with tools and permissions
- User input mode (prompt vs. bash)
Applying to Custom Systems
Ink/Yoga/React creates powerful terminal UIs with several advantages:
- Component reusability - Terminal UI component libraries work like web components
- Modern state management - React hooks handle complex state in terminal apps
- Flexbox layouts in text - Yoga brings sophisticated layouts to text interfaces
- Performance optimization - Static/dynamic content separation prevents flicker
Building similar terminal UI systems requires:
- React renderer for terminals (Ink)
- Layout engine (Yoga via WebAssembly)
- Terminal-specific input handling
- Text rendering optimizations
Combining these elements enables rich terminal interfaces for developer tools, CLI applications, and text-based programs.