[ comments ]
Code
-
Clone
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
- Open with GitHub Desktop
- Download ZIP
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
React for CLIs. Build and test your CLI output using components.
Ink provides the same component-based UI building experience that React offers in the browser, but for command-line apps. It uses Yoga to build Flexbox layouts in the terminal, so most CSS-like props are available in Ink as well. If you are already familiar with React, you already know Ink.
Since Ink is a React renderer, it means that all features of React are supported. Head over to React website for documentation on how to use it. Only Ink's methods will be documented in this readme.
Note: This is documentation for Ink 4. If you're looking for docs on Ink 3, check out this release.
My open source work is supported by the community ❤️
Special thanks to:Add Single Sign-On (and more) in minutes instead of months.
npm install ink react
import React, {useState, useEffect} from 'react'; import {render, Text} from 'ink'; const Counter = () => { const [counter, setCounter] = useState(0); useEffect(() => { const timer = setInterval(() => { setCounter(previousCounter => previousCounter + 1); }, 100); return () => { clearInterval(timer); }; }, []); return <Text color="green">{counter} tests passed</Text>; }; render(<Counter />);
You can also check it out live on repl.it sandbox. Feel free to play around with the code and fork this repl at https://repl.it/@vadimdemedes/ink-counter-demo.
Who's Using Ink?
- GitHub Copilot for CLI - Just say what you want the shell to do.
- Cloudflare's Wrangler - The CLI for Cloudflare Workers.
- Gatsby - Gatsby is a modern web framework for blazing fast websites.
- tap - A Test-Anything-Protocol library for JavaScript.
- Terraform CDK - CDK (Cloud Development Kit) for HashiCorp Terraform.
- Twilio's SIGNAL - CLI for Twilio's SIGNAL conference. Blog post.
- Typewriter - Generates strongly-typed Segment analytics clients from arbitrary JSON Schema.
- Prisma - The unified data layer for modern applications.
- Wallace - Pretty CSS analytics.
- Blitz - The Fullstack React Framework.
-
New York Times - NYT uses Ink
kyt- a toolkit that encapsulates and manages the configuration for web apps. - tink - Next-generation runtime and package manager.
- Inkle - Wordle game.
- loki - Visual regression testing for Storybook.
- Bit - Build, distribute and collaborate on components.
- Remirror - Your friendly, world-class editor toolkit.
- Prime - Open source GraphQL CMS.
- Splash - Observe the splash zone of a change across the Shopify's Polaris component library.
- emoj - Find relevant emojis.
- emma - Find and install npm packages.
- swiff - Multi-environment command line tools for time-saving web developers.
- share - Quickly share files.
- Kubelive - CLI for Kubernetes to provide live data about the cluster and its resources.
- changelog-view - View changelogs.
- cfpush - An interactive Cloud Foundry tutorial.
- startd - Turn your React component into a web app.
- wiki-cli - Search Wikipedia and read summaries.
- garson - Build interactive config-based command-line interfaces.
- git-contrib-calendar - Display a contributions calendar for any git repository.
- gitgud - An interactive command-line GUI for Git.
-
Autarky - Find and delete old
node_modulesdirectories in order to free up disk space. - fast-cli - Test your download and upload speed.
- tasuku - Minimal task runner.
- mnswpr - Minesweeper game.
- lrn - Learning by repetition.
- turdle - Wordle game.
- Shopify CLI - Build apps, themes, and storefronts for Shopify.
- ToDesktop CLI - An all-in-one platform for building Electron apps.
- Getting Started
- Components
- Hooks
- API
- Testing
- Using React Devtools
- Useful Components
- Useful Hooks
- Examples
Getting Started
Use create-ink-app to quickly scaffold a new Ink-based CLI.
npx create-ink-app my-ink-cli
Alternatively, create a TypeScript project:
npx create-ink-app --typescript my-ink-cli
Manual JavaScript setup
Ink requires the same Babel setup as you would do for regular React-based apps in the browser.
Set up Babel with a React preset to ensure all examples in this readme work as expected.
After installing Babel, install @babel/preset-react and insert the following configuration in babel.config.json:
npm install --save-dev @babel/preset-react
{
"presets": ["@babel/preset-react"]
}Next, create a file source.js, where you'll type code that uses Ink:
import React from 'react'; import {render, Text} from 'ink'; const Demo = () => <Text>Hello World</Text>; render(<Demo />);
Then, transpile this file with Babel:
npx babel source.js -o cli.js
Now you can run cli.js with Node.js:
node cli
If you don't like transpiling files during development, you can use import-jsx or @esbuild-kit/esm-loader to import a JSX file and transpile it on the fly.
Ink uses Yoga - a Flexbox layout engine to build great user interfaces for your CLIs using familiar CSS-like props you've used when building apps for the browser.
It's important to remember that each element is a Flexbox container.
Think of it as if each This component can display text, and change its style to make it bold, underline, italic or strikethrough. Note: Type: Change text color.
Ink uses chalk under the hood, so all its functionality is supported. Type: Same as Type: Dim the color (emit a small amount of light). Type: Make the text bold. Type: Make the text italic. Type: Make the text underlined. Type: Make the text crossed with a line. Type: Inverse background and foreground colors. Type: This property tells Ink to wrap or truncate text if its width is larger than container.
If Type: Width of the element in spaces.
You can also set it in percent, which will calculate the width based on the width of parent element. Type: Height of the element in lines (rows).
You can also set it in percent, which will calculate the height based on the height of parent element. Type: Sets a minimum width of the element.
Percentages aren't supported yet, see facebook/yoga#872. Type: Sets a minimum height of the element.
Percentages aren't supported yet, see facebook/yoga#872. Type: Top padding. Type: Bottom padding. Type: Left padding. Type: Right padding. Type: Horizontal padding. Equivalent to setting Type: Vertical padding. Equivalent to setting Type: Padding on all sides. Equivalent to setting Type: Top margin. Type: Bottom margin. Type: Left margin. Type: Right margin. Type: Horizontal margin. Equivalent to setting Type: Vertical margin. Equivalent to setting Type: Margin on all sides. Equivalent to setting Type: Size of the gap between an element's columns and rows. Shorthand for Type: Size of the gap between an element's columns. Type: Size of the gap between element's rows. Type: See flex-grow. Type: See flex-shrink. Type: See flex-basis. Type: See flex-direction. Type: See flex-wrap. Type: See align-items. Type: See align-self. Type: See justify-content. Type: Set this property to Type: Behavior for an element's overflow in horizontal direction. Type: Behavior for an element's overflow in vertical direction. Type: Shortcut for setting Type: Add a border with a specified style.
If Alternatively, pass a custom border style like so: See example in examples/borders. Type: Change border color.
Shorthand for setting Type: Change top border color.
Accepts the same values as Type: Change right border color.
Accepts the same values as Type: Change right border color.
Accepts the same values as Type: Change bottom border color.
Accepts the same values as Type: Change left border color.
Accepts the same values as Type: Dim the border color.
Shorthand for setting Type: Dim the top border color. Type: Dim the bottom border color. Type: Dim the left border color. Type: Dim the right border color. Type: Determines whether top border is visible. Type: Determines whether right border is visible. Type: Determines whether bottom border is visible. Type: Determines whether left border is visible. Adds one or more newline ( Type: Number of newlines to insert. Output: A flexible space that expands along the major axis of its containing layout.
It's useful as a shortcut for filling all the available spaces between elements. For example, using In a vertical flex direction ( It's preferred to use For example, Tap uses Note: See examples/static for an example usage of Type: Array of items of any type to render using a function you pass as a component child. Type: Styles to apply to a container of child elements.
See Type: Function that is called to render every item in Note that Transform a string representation of React components before they are written to output.
For example, you might want to apply a gradient to text, add a clickable link or create some text effects.
These use cases can't accept React nodes as input, they are expecting a string.
That's what Note: Since Type: Function which transforms children output.
It accepts children and must return transformed children too. Type: Output of child components. This hook is used for handling user input.
It's a more convenient alternative to using Type: The handler function that you pass to Type: The input that the program received. Type: Handy information about a key that was pressed. Type: If an arrow key was pressed, the corresponding property will be Type: Return (Enter) key was pressed. Type: Escape key was pressed. Type: Ctrl key was pressed. Type: Shift key was pressed. Type: Tab key was pressed. Type: Backspace key was pressed. Type: Delete key was pressed. Type: If Page Up or Page Down key was pressed, the corresponding property will be Type: Meta key was pressed. Type: Type: Enable or disable capturing of user input.
Useful when there are multiple Type: Exit (unmount) the whole Ink app. Type: Optional error. If passed, Type: Stdin stream passed to Type: A boolean flag determining if the current Type: Type: See Warning: This function will throw unless the current Type: Write any string to stdout, while preserving Ink's output.
It's useful when you want to display some external information outside of Ink's rendering and ensure there's no conflict between the two.
It's similar to Type: Data to write to stdout. See additional usage example in examples/use-stdout. Type: Stderr stream. Write any string to stderr, while preserving Ink's output. It's useful when you want to display some external information outside of Ink's rendering and ensure there's no conflict between the two.
It's similar to Type: Data to write to stderr. Component that uses Type: Auto focus this component, if there's no active (focused) component right now. Type: Enable or disable this component's focus, while still maintaining its position in the list of focusable components.
This is useful for inputs that are temporarily disabled. Type: Set a component's focus ID, which can be used to programmatically focus the component. This is useful for large interfaces with many focusable elements, to avoid having to cycle through all of them. See example in examples/use-focus and examples/use-focus-with-id. This hook exposes methods to enable or disable focus management for all components or manually switch focus to next or previous components. Enable focus management for all components. Note: You don't need to call this method manually, unless you've disabled focus management. Focus management is enabled by default. Disable focus management for all components.
Currently active component (if there's one) will lose its focus. Switch focus to the next focusable component.
If there's no active component right now, focus will be given to the first focusable component.
If active component is the last in the list of focusable components, focus will be switched to the first component. Note: Ink calls this method when user presses Tab. Switch focus to the previous focusable component.
If there's no active component right now, focus will be given to the first focusable component.
If active component is the first in the list of focusable components, focus will be switched to the last component. Note: Ink calls this method when user presses Shift+Tab. Type: Switch focus to the component with the given Returns: Mount a component and render the output. Type: Type: Type: Output stream where app will be rendered. Type: Input stream where app will listen for input. Type: Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
This is needed in case Type: Patch console methods to ensure console output doesn't mix with Ink output.
When any of This functionality is powered by patch-console, so if you need to disable Ink's interception of output but want to build something custom, you can use it. Type: If This is the object that Replace previous root node with a new one or update props of the current root node. Type: Manually unmount the whole Ink app. Returns a promise, which resolves when app is unmounted. Clear output. Measure the dimensions of a particular Note: Type: A reference to a Ink components are simple to test with ink-testing-library.
Here's a simple example that checks how component is rendered: Check out ink-testing-library for more examples and full documentation. Ink supports React Devtools out-of-the-box.
To enable integration with React Devtools in your Ink-based CLI, run it with Then, start React Devtools itself: After it starts up, you should see the component tree of your CLI.
You can even inspect and change the props of components, and see the results immediatelly in the CLI, without restarting it. Note: You must manually quit your CLI via Ctrl+C after you're done testing. The display: flex.
See built-in component below for documentation on how to use Flexbox layouts in Ink.
Note that all text must be wrapped in a component.
import {render, Text} from 'ink';
const Example = () => (
<>
<Text color="green">I am green</Text>
<Text color="black" backgroundColor="white">
I am black on white
</Text>
<Text color="#ffffff">I am white</Text>
<Text bold>I am bold</Text>
<Text italic>I am italic</Text>
<Text underline>I am underline</Text>
<Text strikethrough>I am strikethrough</Text>
<Text inverse>I am inversed</Text>
</>
);
render(<Example />);
allows only text nodes and nested components inside of it. For example, component can't be used inside .color
string<Text color="green">Green</Text>
<Text color="#005cc5">Blue</Text>
<Text color="rgb(232, 131, 136)">Red</Text>
backgroundColor
stringcolor above, but for background.<Text backgroundColor="green" color="white">Green</Text>
<Text backgroundColor="#005cc5" color="white">Blue</Text>
<Text backgroundColor="rgb(232, 131, 136)" color="white">Red</Text>
dimColor
boolean
Default: false<Text color="red" dimColor>
Dimmed Red
</Text>
bold
boolean
Default: falseitalic
boolean
Default: falseunderline
boolean
Default: falsestrikethrough
boolean
Default: falseinverse
boolean
Default: false<Text inverse color="yellow">
Inversed Yellow
</Text>
wrap
string
Allowed values: wrap truncate truncate-start truncate-middle truncate-end
Default: wrapwrap is passed (by default), Ink will wrap text and split it into multiple lines.
If truncate-* is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off.<Box width={7}>
<Text>Hello World</Text>
</Box>
//=> 'Hello\nWorld'
// `truncate` is an alias to `truncate-end`
<Box width={7}>
<Text wrap="truncate">Hello World</Text>
</Box>
//=> 'Hello…'
<Box width={7}>
<Text wrap="truncate-middle">Hello World</Text>
</Box>
//=> 'He…ld'
<Box width={7}>
<Text wrap="truncate-start">Hello World</Text>
</Box>
//=> '…World'
is an essential Ink component to build your layout.
It's like import {render, Box, Text} from 'ink';
const Example = () => (
<Box margin={2}>
<Text>This is a box with margin</Text>
</Box>
);
render(<Example />);
Dimensions
width
number string<Box width={4}>
<Text>X</Text>
</Box>
//=> 'X '
<Box width={10}>
<Box width="50%">
<Text>X</Text>
</Box>
<Text>Y</Text>
</Box>
//=> 'X Y'
height
number string<Box height={4}>
<Text>X</Text>
</Box>
//=> 'X\n\n\n'
<Box height={6} flexDirection="column">
<Box height="50%">
<Text>X</Text>
</Box>
<Text>Y</Text>
</Box>
//=> 'X\n\n\nY\n\n'
minWidth
numberminHeight
numberPadding
paddingTop
number
Default: 0paddingBottom
number
Default: 0paddingLeft
number
Default: 0paddingRight
number
Default: 0paddingX
number
Default: 0paddingLeft and paddingRight.paddingY
number
Default: 0paddingTop and paddingBottom.padding
number
Default: 0paddingTop, paddingBottom, paddingLeft and paddingRight.<Box paddingTop={2}>Top</Box>
<Box paddingBottom={2}>Bottom</Box>
<Box paddingLeft={2}>Left</Box>
<Box paddingRight={2}>Right</Box>
<Box paddingX={2}>Left and right</Box>
<Box paddingY={2}>Top and bottom</Box>
<Box padding={2}>Top, bottom, left and right</Box>
Margin
marginTop
number
Default: 0marginBottom
number
Default: 0marginLeft
number
Default: 0marginRight
number
Default: 0marginX
number
Default: 0marginLeft and marginRight.marginY
number
Default: 0marginTop and marginBottom.margin
number
Default: 0marginTop, marginBottom, marginLeft and marginRight.<Box marginTop={2}>Top</Box>
<Box marginBottom={2}>Bottom</Box>
<Box marginLeft={2}>Left</Box>
<Box marginRight={2}>Right</Box>
<Box marginX={2}>Left and right</Box>
<Box marginY={2}>Top and bottom</Box>
<Box margin={2}>Top, bottom, left and right</Box>
Gap
gap
number
Default: 0columnGap and rowGap.<Box gap={1} width={3} flexWrap="wrap">
<Text>A</Text>
<Text>B</Text>
<Text>C</Text>
</Box>
// A B
//
// C
columnGap
number
Default: 0<Box columnGap={1}>
<Text>A</Text>
<Text>B</Text>
</Box>
// A B
rowGap
number
Default: 0<Box flexDirection="column" rowGap={1}>
<Text>A</Text>
<Text>B</Text>
</Box>
// A
//
// B
Flex
flexGrow
number
Default: 0<Box>
<Text>Label:</Text>
<Box flexGrow={1}>
<Text>Fills all remaining space</Text>
</Box>
</Box>
flexShrink
number
Default: 1<Box width={20}>
<Box flexShrink={2} width={10}>
<Text>Will be 1/4</Text>
</Box>
<Box width={10}>
<Text>Will be 3/4</Text>
</Box>
</Box>
flexBasis
number string<Box width={6}>
<Box flexBasis={3}>
<Text>X</Text>
</Box>
<Text>Y</Text>
</Box>
//=> 'X Y'
<Box width={6}>
<Box flexBasis="50%">
<Text>X</Text>
</Box>
<Text>Y</Text>
</Box>
//=> 'X Y'
flexDirection
string
Allowed values: row row-reverse column column-reverse<Box>
<Box marginRight={1}>
<Text>X</Text>
</Box>
<Text>Y</Text>
</Box>
// X Y
<Box flexDirection="row-reverse">
<Text>X</Text>
<Box marginRight={1}>
<Text>Y</Text>
</Box>
</Box>
// Y X
<Box flexDirection="column">
<Text>X</Text>
<Text>Y</Text>
</Box>
// X
// Y
<Box flexDirection="column-reverse">
<Text>X</Text>
<Text>Y</Text>
</Box>
// Y
// X
flexWrap
string
Allowed values: nowrap wrap wrap-reverse<Box width={2} flexWrap="wrap">
<Text>A</Text>
<Text>BC</Text>
</Box>
// A
// B C
<Box flexDirection="column" height={2} flexWrap="wrap">
<Text>A</Text>
<Text>B</Text>
<Text>C</Text>
</Box>
// A C
// B
alignItems
string
Allowed values: flex-start center flex-end<Box alignItems="flex-start">
<Box marginRight={1}>
<Text>X</Text>
</Box>
<Text>
A
<Newline/>
B
<Newline/>
C
</Text>
</Box>
// X A
// B
// C
<Box alignItems="center">
<Box marginRight={1}>
<Text>X</Text>
</Box>
<Text>
A
<Newline/>
B
<Newline/>
C
</Text>
</Box>
// A
// X B
// C
<Box alignItems="flex-end">
<Box marginRight={1}>
<Text>X</Text>
</Box>
<Text>
A
<Newline/>
B
<Newline/>
C
</Text>
</Box>
// A
// B
// X C
alignSelf
string
Default: auto
Allowed values: auto flex-start center flex-end<Box height={3}>
<Box alignSelf="flex-start">
<Text>X</Text>
</Box>
</Box>
// X
//
//
<Box height={3}>
<Box alignSelf="center">
<Text>X</Text>
</Box>
</Box>
//
// X
//
<Box height={3}>
<Box alignSelf="flex-end">
<Text>X</Text>
</Box>
</Box>
//
//
// X
justifyContent
string
Allowed values: flex-start center flex-end space-between space-around<Box justifyContent="flex-start">
<Text>X</Text>
</Box>
// [X ]
<Box justifyContent="center">
<Text>X</Text>
</Box>
// [ X ]
<Box justifyContent="flex-end">
<Text>X</Text>
</Box>
// [ X]
<Box justifyContent="space-between">
<Text>X</Text>
<Text>Y</Text>
</Box>
// [X Y]
<Box justifyContent="space-around">
<Text>X</Text>
<Text>Y</Text>
</Box>
// [ X Y ]
Visibility
display
string
Allowed values: flex none
Default: flexnone to hide the element.overflowX
string
Allowed values: visible hidden
Default: visibleoverflowY
string
Allowed values: visible hidden
Default: visibleoverflow
string
Allowed values: visible hidden
Default: visibleoverflowX and overflowY at the same time.Borders
borderStyle
string
Allowed values: single double round bold singleDouble doubleSingle classic | BoxStyleborderStyle is undefined (which it is by default), no border will be added.
Ink uses border styles from cli-boxes module.<Box flexDirection="column">
<Box>
<Box borderStyle="single" marginRight={2}>
<Text>single</Text>
</Box>
<Box borderStyle="double" marginRight={2}>
<Text>double</Text>
</Box>
<Box borderStyle="round" marginRight={2}>
<Text>round</Text>
</Box>
<Box borderStyle="bold">
<Text>bold</Text>
</Box>
</Box>
<Box marginTop={1}>
<Box borderStyle="singleDouble" marginRight={2}>
<Text>singleDouble</Text>
</Box>
<Box borderStyle="doubleSingle" marginRight={2}>
<Text>doubleSingle</Text>
</Box>
<Box borderStyle="classic">
<Text>classic</Text>
</Box>
</Box>
</Box>
<Box
borderStyle={{
topLeft: '↘',
top: '↓',
topRight: '↙',
left: '→',
bottomLeft: '↗',
bottom: '↑',
bottomRight: '↖',
right: '←'
}}
>
<Text>Custom</Text>
</Box>
borderColor
stringborderTopColor, borderRightColor, borderBottomColor and borderLeftColor.<Box borderStyle="round" borderColor="green">
<Text>Green Rounded Box</Text>
</Box>
borderTopColor
stringcolor in component.<Box borderStyle="round" borderTopColor="green">
<Text>Hello world</Text>
</Box>
borderRightColor
stringcolor in component.<Box borderStyle="round" borderRightColor="green">
<Text>Hello world</Text>
</Box>
borderRightColor
stringcolor in component.<Box borderStyle="round" borderRightColor="green">
<Text>Hello world</Text>
</Box>
borderBottomColor
stringcolor in component.<Box borderStyle="round" borderBottomColor="green">
<Text>Hello world</Text>
</Box>
borderLeftColor
stringcolor in component.<Box borderStyle="round" borderLeftColor="green">
<Text>Hello world</Text>
</Box>
borderDimColor
boolean
Default: falseborderTopDimColor, borderBottomDimColor, borderLeftDimColor and borderRightDimColor.<Box borderStyle="round" borderDimColor>
<Text>Hello world</Text>
</Box>
borderTopDimColor
boolean
Default: false<Box borderStyle="round" borderTopDimColor>
<Text>Hello world</Text>
</Box>
borderBottomDimColor
boolean
Default: false<Box borderStyle="round" borderBottomDimColor>
<Text>Hello world</Text>
</Box>
borderLeftDimColor
boolean
Default: false<Box borderStyle="round" borderLeftDimColor>
<Text>Hello world</Text>
</Box>
borderRightDimColor
boolean
Default: false<Box borderStyle="round" borderRightDimColor>
<Text>Hello world</Text>
</Box>
borderTop
boolean
Default: trueborderRight
boolean
Default: trueborderBottom
boolean
Default: trueborderLeft
boolean
Default: true\n) characters.
Must be used within components.count
number
Default: 1import {render, Text, Newline} from 'ink';
const Example = () => (
<Text>
<Text color="green">Hello</Text>
<Newline />
<Text color="red">World</Text>
</Text>
);
render(<Example />);
Hello
World
in a with default flex direction (row) will position "Left" on the left side and will push "Right" to the right side.import {render, Box, Text, Spacer} from 'ink';
const Example = () => (
<Box>
<Text>Left</Text>
<Spacer />
<Text>Right</Text>
</Box>
);
render(<Example />);
column), it will position "Top" to the top of the container and push "Bottom" to the bottom of it.
Note, that container needs to be tall to enough to see this in effect.import {render, Box, Text, Spacer} from 'ink';
const Example = () => (
<Box flexDirection="column" height={10}>
<Text>Top</Text>
<Spacer />
<Text>Bottom</Text>
</Box>
);
render(<Example />);
component permanently renders its output above everything else.
It's useful for displaying activity like completed tasks or logs - things that
are not changing after they're rendered (hence the name "Static"). for use cases like these, when you can't know
or control the amount of items that need to be rendered. to display
a list of completed tests. Gatsby uses it
to display a list of generated pages, while still displaying a live progress bar.import React, {useState, useEffect} from 'react';
import {render, Static, Box, Text} from 'ink';
const Example = () => {
const [tests, setTests] = useState([]);
useEffect(() => {
let completedTests = 0;
let timer;
const run = () => {
// Fake 10 completed tests
if (completedTests++ < 10) {
setTests(previousTests => [
...previousTests,
{
id: previousTests.length,
title: `Test #${previousTests.length + 1}`
}
]);
setTimeout(run, 100);
}
};
run();
return () => {
clearTimeout(timer);
};
}, []);
return (
<>
{/* This part will be rendered once to the terminal */}
<Static items={tests}>
{test => (
<Box key={test.id}>
<Text color="green">✔ {test.title}</Text>
</Box>
)}
</Static>
{/* This part keeps updating as state changes */}
<Box marginTop={1}>
<Text dimColor>Completed tests: {tests.length}</Text>
</Box>
</>
);
};
render(<Example />);
only renders new items in items prop and ignores items
that were previously rendered. This means that when you add new items to items
array, changes you make to previous items will not trigger a rerender. component.items
Arraystyle
object for supported properties.<Static items={...} style={{padding: 1}}>
{...}
</Static>
children(item)
Functionitems array.
First argument is an item itself and second argument is index of that item in
items array.key must be assigned to the root component.<Static items={['a', 'b', 'c']}>
{(item, index) => {
// This function is called for every item in ['a', 'b', 'c']
// `item` is 'a', 'b', 'c'
// `index` is 0, 1, 2
return (
<Box key={index}>
<Text>Item: {item}</Text>
</Box>
);
}}
</Static>
component does, it gives you an output string of its child components and lets you transform it in any way. must be applied only to children components and shouldn't change the dimensions of the output, otherwise layout will be incorrect.import {render, Transform} from 'ink';
const Example = () => (
<Transform transform={output => output.toUpperCase()}>
<Text>Hello World</Text>
</Transform>
);
render(<Example />);
transform function converts all characters to upper case, final output that's rendered to the terminal will be "HELLO WORLD", not "Hello World".transform(children)
Functionchildren
stringuseInput(inputHandler, options?)
useStdin and listening to data events.
The callback you pass to useInput is called for each character when user enters any input.
However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as input.
You can find a full example of using useInput at examples/use-input.import {useInput} from 'ink';
const UserInput = () => {
useInput((input, key) => {
if (input === 'q') {
// Exit program
}
if (key.leftArrow) {
// Left arrow key pressed
}
});
return …
};
inputHandler(input, key)
FunctionuseInput receives two arguments:input
stringkey
objectkey.leftArrow
key.rightArrow
key.upArrow
key.downArrow
boolean
Default: falsetrue.
For example, if user presses left arrow key, key.leftArrow equals true.key.return
boolean
Default: falsekey.escape
boolean
Default: falsekey.ctrl
boolean
Default: falsekey.shift
boolean
Default: falsekey.tab
boolean
Default: falsekey.backspace
boolean
Default: falsekey.delete
boolean
Default: falsekey.pageDown
key.pageUp
boolean
Default: falsetrue.
For example, if user presses Page Down, key.pageDown equals true.key.meta
boolean
Default: falseoptions
objectisActive
boolean
Default: trueuseInput hooks used at once to avoid handling the same input several times.useApp is a React hook, which exposes a method to manually exit the app (unmount).exit(error?)
Functionerror
ErrorwaitUntilExit will reject with that error.import {useApp} from 'ink';
const Example = () => {
const {exit} = useApp();
// Exit the app after 5 seconds
useEffect(() => {
setTimeout(() => {
exit();
}, );
}, []);
return …
};
useStdin is a React hook, which exposes stdin stream.stdin
stream.Readable
Default: process.stdinrender() in options.stdin or process.stdin by default.
Useful if your app needs to handle user input.import {useStdin} from 'ink';
const Example = () => {
const {stdin} = useStdin();
return …
};
isRawModeSupported
booleanstdin supports setRawMode.
A component using setRawMode might want to use isRawModeSupported to nicely fall back in environments where raw mode is not supported.import {useStdin} from 'ink';
const Example = () => {
const {isRawModeSupported} = useStdin();
return isRawModeSupported ? (
<MyInputComponent />
) : (
<MyComponentThatDoesntUseInput />
);
};
setRawMode(isRawModeEnabled)
functionisRawModeEnabled
booleansetRawMode.
Ink exposes this function to be able to handle Ctrl+C, that's why you should use Ink's setRawMode instead of process.stdin.setRawMode.stdin supports setRawMode. Use isRawModeSupported to detect setRawMode support.import {useStdin} from 'ink';
const Example = () => {
const {setRawMode} = useStdin();
useEffect(() => {
setRawMode(true);
return () => {
setRawMode(false);
};
});
return …
};
useStdout is a React hook, which exposes stdout stream, where Ink renders your app.stdout
stream.Writable
Default: process.stdoutimport {useStdout} from 'ink';
const Example = () => {
const {stdout} = useStdout();
return …
};
write(data)
, except it can't accept components, it only works with strings.data
stringimport {useStdout} from 'ink';
const Example = () => {
const {write} = useStdout();
useEffect(() => {
// Write a single message to stdout, above Ink's output
write('Hello from Ink to stdout\n');
}, []);
return …
};
useStderr is a React hook, which exposes stderr stream.stderr
stream.Writable
Default: process.stderrimport {useStderr} from 'ink';
const Example = () => {
const {stderr} = useStderr();
return …
};
write(data)
, except it can't accept components, it only works with strings.data
stringimport {useStderr} from 'ink';
const Example = () => {
const {write} = useStderr();
useEffect(() => {
// Write a single message to stderr, above Ink's output
write('Hello from Ink to stderr\n');
}, []);
return …
};
useFocus hook becomes "focusable" to Ink, so when user presses Tab, Ink will switch focus to this component.
If there are multiple components that execute useFocus hook, focus will be given to them in the order that these components are rendered in.
This hook returns an object with isFocused boolean property, which determines if this component is focused or not.options
autoFocus
boolean
Default: falseisActive
boolean
Default: trueid
string
Required: falseimport {render, useFocus, Text} from 'ink';
const Example = () => {
const {isFocused} = useFocus();
return <Text>{isFocused ? 'I am focused' : 'I am not focused'}</Text>;
};
render(<Example />);
enableFocus()
import {useFocusManager} from 'ink';
const Example = () => {
const {enableFocus} = useFocusManager();
useEffect(() => {
enableFocus();
}, []);
return …
};
disableFocus()
import {useFocusManager} from 'ink';
const Example = () => {
const {disableFocus} = useFocusManager();
useEffect(() => {
disableFocus();
}, []);
return …
};
focusNext()
import {useFocusManager} from 'ink';
const Example = () => {
const {focusNext} = useFocusManager();
useEffect(() => {
focusNext();
}, []);
return …
};
focusPrevious()
import {useFocusManager} from 'ink';
const Example = () => {
const {focusPrevious} = useFocusManager();
useEffect(() => {
focusPrevious();
}, []);
return …
};
focus(id)
id
stringid.
If there's no component with that ID, focus will be given to the next focusable component.import {useFocusManager, useInput} from 'ink';
const Example = () => {
const {focus} = useFocusManager();
useInput(input => {
if (input === 's') {
// Focus the component with focus ID 'someId'
focus('someId');
}
});
return …
};
render(tree, options?)
Instancetree
ReactElementoptions
objectstdout
stream.Writable
Default: process.stdoutstdin
stream.Readable
Default: process.stdinexitOnCtrlC
boolean
Default: trueprocess.stdin is in raw mode, because then Ctrl+C is ignored by default and process is expected to handle it manually.patchConsole
boolean
Default: trueconsole.* methods are called (like console.log()), Ink intercepts their output, clears main output, renders output from the console method and then rerenders main output again.
That way both are visible and are not overlapping each other.debug
boolean
Default: falsetrue, each update will be rendered as a separate output, without replacing the previous one.Instance
render() returns.rerender(tree)
tree
ReactElement// Update props of the root node
const {rerender} = render(<Counter count={1} />);
rerender(<Counter count={2} />);
// Replace root node
const {rerender} = render(<OldCounter />);
rerender(<NewCounter />);
unmount()
const {unmount} = render(<MyApp />);
unmount();
waitUntilExit()
const {unmount, waitUntilExit} = render(<MyApp />);
setTimeout(unmount, );
await waitUntilExit(); // resolves after `unmount()` is called
clear()
const {clear} = render(<MyApp />);
clear();
measureElement(ref)
element.
It returns an object with width and height properties.
This function is useful when your component needs to know the amount of available space it has. You could use it when you need to change the layout based on the length of its content.measureElement() returns correct results only after the initial render, when layout has been calculated. Until then, width and height equal to zero. It's recommended to call measureElement() in a useEffect hook, which fires after the component has rendered.ref
MutableRef element captured with a ref property.
See Refs for more information on how to capture references.import {render, measureElement, Box, Text} from 'ink';
const Example = () => {
const ref = useRef();
useEffect(() => {
const {width, height} = measureElement(ref.current);
// width = 100, height = 1
}, []);
return (
<Box width={100}>
<Box ref={ref}>
<Text>This box will stretch to 100 width</Text>
</Box>
</Box>
);
};
render(<Example />);
import React from 'react';
import {Text} from 'ink';
import {render} from 'ink-testing-library';
const Test = () => <Text>Hello World</Text>;
const {lastFrame} = render(<Test />);
lastFrame() === 'Hello World'; //=> true
Using React Devtools
DEV=true environment variable:DEV=true my-cli
npx react-devtools
Useful Components
Useful Hooks
examples directory contains a set of real examples. You can run them with:npm run example examples/[example name]
# e.g. npm run example examples/borders
component.useFocus hook to manage focus between components. to render permanent output.
Sponsor this project
[ comments ]
