Breaking
- Require Node.js 22 19b5316
- Require React 19.2+ cfaebbb
- Ink now uses
useEffectEventinternally to avoid re-subscribing input handlers on every render
- Ink now uses
- Pressing Backspace now correctly sets
key.backspaceinstead ofkey.delete(#634) 321a2e8- Most terminals send the same byte for Backspace as the Delete key, which caused Ink to misreport it. If you were checking
key.deleteto handle backspace, switch tokey.backspace
- Most terminals send the same byte for Backspace as the Delete key, which caused Ink to misreport it. If you were checking
key.metais no longer set totruewhen Escape is pressed e195912- Previously a backward-compat shim made plain Escape set both
key.escapeandkey.meta. Now onlykey.escapeistrue.key.metais reserved for actual Alt/Meta modifier combinations
- Previously a backward-compat shim made plain Escape set both
New
- Add
usePastehook for handling clipboard paste events fbbeb23- Automatically enables bracketed paste mode so pasted text arrives as a single string, never misinterpreted as individual key presses by
useInput
- Automatically enables bracketed paste mode so pasted text arrives as a single string, never misinterpreted as individual key presses by
- Add
useWindowSizehook 7047795- Returns
{columns, rows}and re-renders automatically on terminal resize
- Returns
- Add
useBoxMetricshook for measuring box dimensions at runtime (#433) 88731d1 - Add
useAnimationhook for built-in animation support (#142) ada019c- Provides a frame counter that increments at a configurable interval, with pause/resume support and automatic cleanup on unmount
- Add
alternateScreenoption torender()(#263) 5a60eb9- Renders into the terminal's alternate screen buffer (like vim or less), restoring the previous terminal content on exit
- Add
interactiveoption torender()(#888) 02490f6- Override automatic interactive-mode detection for environments where the built-in heuristic doesn't fit
- Add
activeIdtouseFocusManager()(#661) eb2f470- Returns the ID of the currently focused component, or
undefinedif nothing is focused
- Returns the ID of the currently focused component, or
- Add
borderBackgroundColor(and per-side variants) to<Box>(#906) d3c6d14- Set a background color on borders independently from the box content background
- Add
wrap="hard"to<Text>(#925) 2b1e3a6- Fills each line to the full column width, breaking words mid-word as needed
- Add
maxWidthandmaxHeightprops to<Box>(#713) 9291794 - Add
aspectRatio,alignContent,position="static", andtop/right/bottom/leftlayout props to<Box>c2f4b86 - Kitty keyboard protocol: query all terminals in auto mode instead of a hardcoded allowlist (#895) 3e672b5
Fixes
- Fix incremental rendering for trailing newline (#910) c32da0b
- Fix
useInputcrash on unmapped key codes (#902) 969a4f1 - Fix CJK text truncation exceeding
<Box>width b5f3e3a - Fix splitting wide characters (emoji, CJK) when overlapping writes occur (#930) 06d53f4
- Fix dangling
staticNodereference (#905) 0dc4dfa
Migration guide
key.backspace vs key.delete
// Before — physical backspace was reported as key.delete
useInput((input, key) => {
if (key.delete) { /* was catching physical backspace key */ }
});
// After
useInput((input, key) => {
if (key.backspace) { /* physical backspace key (0x7F) */ }
if (key.delete) { /* actual Delete key (e.g. Fn+Backspace) */ }
});key.meta no longer set on plain Escape
// Before — key.meta was true for both Escape AND Alt+key
useInput((input, key) => {
if (key.meta) { /* fired on plain Escape too */ }
});
// After — check key.escape explicitly for the Escape key
useInput((input, key) => {
if (key.escape) { /* plain Escape */ }
if (key.meta) { /* Alt/Meta combos only */ }
});