Ink Target
Compile KERN to Ink (React for the terminal). Full React component model with hooks, input handling, and flexbox layout.
What it generates
KERN input:
screen name=Dashboard
state name=count initial=0
on event=key key=up
handler <<<
setCount(prev => prev + 1);
>>>
box {color:blue,borderStyle:round}
text value="Count:" {fw:bold}
text value={{count}} {color:green}Compiled output:
import React, { useState } from 'react';
import { Box, Text, useInput } from 'ink';
export default function Dashboard() {
const [count, setCount] = useState(0);
useInput((input, key) => {
if (!(key.upArrow)) return;
setCount(prev => prev + 1);
});
return (
<Box flexDirection="column">
<Box borderStyle="round" borderColor="blue">
<Text bold>{"Count:"}</Text>
<Text color="green">{count}</Text>
</Box>
</Box>
);
}Supported nodes
The Ink transpiler maps KERN nodes to Ink components and React hooks:
UI nodes
| KERN Node | Ink Output | Description |
|---|---|---|
| screen | React function component | Root component (export default) |
| text | <Text> | Styled text with bold, color, dimColor, italic, backgroundColor |
| box | <Box> | Flexbox container with borderStyle, borderColor, padding, width, flexGrow |
| separator | <Text dimColor> | Horizontal line |
| table | <Box flexDirection="column"> | Table with header and row components |
| scoreboard | <Box flexDirection="column"> | Results display with metric rows |
| spinner | <Spinner /> (ink-spinner) | Animated loading indicator |
| progress | <Box> with bar characters | Progress bar with percentage, supports expressions |
| gradient | <Text> per character | Per-character color gradient |
| input-area | <Box borderStyle="single"> | Persistent input region at bottom |
| output-area | <Box flexGrow={1}> | Scrollable output region |
| text-input | <TextInput /> (ink-text-input) | Text input with bind, placeholder, onSubmit, history |
| select-input | <SelectInput /> (ink-select-input) | Selection list with items and onSelect |
| each | .map() iteration | List rendering with key support |
| conditional | {condition && (...)} | Conditional rendering |
Hook nodes
| KERN Node | React Hook | Description |
|---|---|---|
| state | useState | Reactive state with auto-generated setter (setX) |
| ref | useRef | Mutable ref (xRef) |
| on event=key | useInput | Keyboard input handler with key filtering |
| stream | useEffect + async generator | Streaming data with auto-cancellation |
| logic | useEffect | Side-effect code with dependency array |
| callback | useCallback | Memoized function with params and deps |
| machine | useReducer | State machine with reducer pattern |
Component props
Screen nodes accept a props attribute for typed component parameters:
screen name=StatusBar props="label:string, active:boolean"
text value={{label}} {fw:bold}
conditional if={{active}}
text value="ON" {color:green}Compiles to:
export default function StatusBar({ label, active }: { label: string; active: boolean }) {
// ...
}Key handling
The on event=key node maps to Ink's useInput hook. Supported key names: return, escape, tab, up, down, left, right, backspace, delete, or any single character.
New in v3.1.9
The Ink transpiler received a major upgrade to production-ready status:
- @inkjs/ui components — Spinner, TextInput, SelectInput, MultiSelect, ConfirmInput, PasswordInput, StatusMessage, Alert, OrderedList, UnorderedList migrated from legacy ink-* packages
- Safe setters — __inkSafe wrapper auto-bridges microtask to macrotask on all state setters, eliminating repaint bugs
- Channel streams — stream mode=channel for AsyncGenerator patterns with AbortController cleanup
- Multi-screen files — Named and default exports, cross-screen imports with screen-embed from=
- Layout primitives — layout-row, layout-col, layout-stack, spacer
- Entry-point artifact — Auto-generates index.tsx with render + waitUntilExit boilerplate
- Lazy useState — IIFEs and constructors wrapped as useState(() => expr)
Dependencies
The Ink target requires these npm packages:
npm install react ink @inkjs/ui@inkjs/ui components are imported automatically when their corresponding nodes are used.
Configuration
kern dev app.kern --target=ink