🌈 React for interactive command-line apps

Overview



Ink


React for CLIs. Build and test your CLI output using components.

Build Status npm

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 3. If you're looking for docs on Ink 2, check out this release. There's also a migration guide from Ink 2 available.

Install

$ npm install ink react

Usage

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?

  • Gatsby - Gatsby is a modern web framework for blazing fast websites.
  • tap - A Test-Anything-Protocol library for JavaScript.
  • Yarn 2 - Fast, reliable, and secure dependency management 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 on the CLI.
  • 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.
  • 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 emoji on the command-line.
  • emma - Terminal assistant to find and install npm packages.
  • swiff - Multi-environment command line tools for time-saving web developers.
  • share - Quickly share files from your command line.
  • Kubelive - CLI for Kubernetes to provide live data about the cluster and its resources.
  • changelog-view - Tool view changelog in console.
  • cfpush - An interactive Cloud Foundry tutorial in your terminal.
  • startd - Turn your React component into a web app from the command-line.
  • wiki-cli - Search Wikipedia and read summaries directly in your terminal.
  • 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 - An interactive CLI to find and delete old node_modules directories in order to free up disk space.
  • fast-cli - Test your download and upload speed.

Contents

Getting Started

Use create-ink-app to quickly scaffold a new Ink-based CLI.

$ mkdir my-ink-cli
$ cd my-ink-cli
$ npx create-ink-app

Alternatively, create a TypeScript project:

$ npx create-ink-app --typescript
Manual 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",
		[
			"@babel/preset-env",
			{
				"targets": {
					"node": true
				}
			}
		]
	]
}

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 to require() 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 <div> in the browser had display: flex. See <Box> built-in component below for documentation on how to use Flexbox layouts in Ink. Note that all text must be wrapped in a <Text> component.

Components

<Text>

This component can display text, and change its style to make it bold, underline, italic or strikethrough.

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 />);

Note: <Text> allows only text nodes and nested <Text> components inside of it. For example, <Box> component can't be used inside <Text>.

color

Type: string

Change text color. Ink uses chalk under the hood, so all its functionality is supported.

<Text color="green">Green</Text>
<Text color="#005cc5">Blue</Text>
<Text color="rgb(232, 131, 136)">Red</Text>

backgroundColor

Type: string

Same as color 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

Type: boolean
Default: false

Dim the color (emit a small amount of light).

<Text color="red" dimColor>
	Dimmed Red
</Text>

bold

Type: boolean
Default: false

Make the text bold.

italic

Type: boolean
Default: false

Make the text italic.

underline

Type: boolean
Default: false

Make the text underlined.

strikethrough

Type: boolean
Default: false

Make the text crossed with a line.

inverse

Type: boolean
Default: false

Inverse background and foreground colors.

<Text inverse color="yellow">
	Inversed Yellow
</Text>

wrap

Type: string
Allowed values: wrap truncate truncate-start truncate-middle truncate-end
Default: wrap

This property tells Ink to wrap or truncate text if its width is larger than container. If wrap 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'

<Box>

<Box> is an essential Ink component to build your layout. It's like <div style="display: flex"> in the browser.

import {render, Box, Text} from 'ink';

const Example = () => (
	<Box margin={2}>
		<Text>This is a box with margin</Text>
	</Box>;
);

render(<Example />);

Dimensions

width

Type: number string

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.

<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

Type: number string

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.

<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

Type: number

Sets a minimum width of the element. Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.

minHeight

Type: number

Sets a minimum height of the element. Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.

Padding

paddingTop

Type: number
Default: 0

Top padding.

paddingBottom

Type: number
Default: 0

Bottom padding.

paddingLeft

Type: number
Default: 0

Left padding.

paddingRight

Type: number
Default: 0

Right padding.

paddingX

Type: number
Default: 0

Horizontal padding. Equivalent to setting paddingLeft and paddingRight.

paddingY

Type: number
Default: 0

Vertical padding. Equivalent to setting paddingTop and paddingBottom.

padding

Type: number
Default: 0

Padding on all sides. Equivalent to setting paddingTop, 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

Type: number
Default: 0

Top margin.

marginBottom

Type: number
Default: 0

Bottom margin.

marginLeft

Type: number
Default: 0

Left margin.

marginRight

Type: number
Default: 0

Right margin.

marginX

Type: number
Default: 0

Horizontal margin. Equivalent to setting marginLeft and marginRight.

marginY

Type: number
Default: 0

Vertical margin. Equivalent to setting marginTop and marginBottom.

margin

Type: number
Default: 0

Margin on all sides. Equivalent to setting marginTop, 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>

Flex

flexGrow

Type: number
Default: 0

See flex-grow.

<Box>
	<Text>Label:</Text>
	<Box flexGrow={1}>
		<Text>Fills all remaining space</Text>
	</Box>
</Box>
flexShrink

Type: number
Default: 1

See flex-shrink.

<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

Type: number string

See flex-basis.

<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

Type: string
Allowed values: row row-reverse column column-reverse

See flex-direction.

<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
alignItems

Type: string
Allowed values: flex-start center flex-end

See align-items.

<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

Type: string
Default: auto
Allowed vales: auto flex-start center flex-end

See align-self.

<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

Type: string
Allowed values: flex-start center flex-end space-between space-around

See justify-content.

<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

Type: string
Allowed values: flex none
Default: flex

Set this property to none to hide the element.

Borders

borderStyle

Type: string
Allowed values: single double round bold singleDouble doubleSingle classic

Add a border with a specified style. If borderStyle 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>

See example in examples/borders.

borderColor

Type: string

Change border color. Accepts the same values as color in <Text> component.

<Box borderStyle="round" borderColor="green">
	<Text>Green Rounded Box</Text>
</Box>

<Newline>

Adds one or more newline (\n) characters. Must be used within <Text> components.

count

Type: number
Default: 1

Number of newlines to insert.

import {render, Text, Newline} from 'ink';

const Example = () => (
	<Text>
		<Text color="green">Hello</Text>
		<Newline />
		<Text color="red">World</Text>
	</Text>
);

render(<Example />);

Output:

Hello
World

<Spacer>

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 <Spacer> in a <Box> 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 />);

In a vertical flex direction (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 />);

<Static>

<Static> 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").

It's preferred to use <Static> for use cases like these, when you can't know or control the amount of items that need to be rendered.

For example, Tap uses <Static> 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 />);

Note: <Static> 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.

See examples/static for an example usage of <Static> component.

items

Type: Array

Array of items of any type to render using a function you pass as a component child.

style

Type: object

Styles to apply to a container of child elements. See <Box> for supported properties.

<Static items={...} style={{padding: 1}}>
	{...}
</Static>

children(item)

Type: Function

Function that is called to render every item in items array. First argument is an item itself and second argument is index of that item in items array.

Note that 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>

<Transform>

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 <Transform> component does, it gives you an output string of its child components and lets you transform it in any way.

Note: <Transform> must be applied only to <Text> 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 />);

Since 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)

Type: Function

Function which transforms children output. It accepts children and must return transformed children too.

children

Type: string

Output of child components.

Hooks

useInput(inputHandler, options?)

This hook is used for handling user input. It's a more convenient alternative to using 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)

Type: Function

The handler function that you pass to useInput receives two arguments:

input

Type: string

The input that the program received.

key

Type: object

Handy information about a key that was pressed.

key.leftArrow
key.rightArrow
key.upArrow
key.downArrow

Type: boolean
Default: false

If an arrow key was pressed, the corresponding property will be true. For example, if user presses left arrow key, key.leftArrow equals true.

key.return

Type: boolean
Default: false

Return (Enter) key was pressed.

key.escape

Type: boolean
Default: false

Escape key was pressed.

key.ctrl

Type: boolean
Default: false

Ctrl key was pressed.

key.shift

Type: boolean
Default: false

Shift key was pressed.

key.tab

Type: boolean
Default: false

Tab key was pressed.

key.backspace

Type: boolean
Default: false

Backspace key was pressed.

key.delete

Type: boolean
Default: false

Delete key was pressed.

key.pageDown
key.pageUp

Type: boolean
Default: false

If Page Up or Page Down key was pressed, the corresponding property will be true. For example, if user presses Page Down, key.pageDown equals true.

key.meta

Type: boolean
Default: false

Meta key was pressed.

options

Type: object

isActive

Type: boolean
Default: true

Enable or disable capturing of user input. Useful when there are multiple useInput hooks used at once to avoid handling the same input several times.

useApp()

useApp is a React hook, which exposes a method to manually exit the app (unmount).

exit(error?)

Type: Function

Exit (unmount) the whole Ink app.

error

Type: Error

Optional error. If passed, waitUntilExit will reject with that error.

import {useApp} from 'ink';

const Example = () => {
	const {exit} = useApp();

	// Exit the app after 5 seconds
	useEffect(() => {
		setTimeout(() => {
			exit();
		}, 5000);
	}, []);

	return 
};

useStdin()

useStdin is a React hook, which exposes stdin stream.

stdin

Type: stream.Readable
Default: process.stdin

Stdin stream passed to render() 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

Type: boolean

A boolean flag determining if the current stdin 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)

Type: function

isRawModeEnabled

Type: boolean

See setRawMode. 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.

Warning: This function will throw unless the current 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()

useStdout is a React hook, which exposes stdout stream, where Ink renders your app.

stdout

Type: stream.Writable
Default: process.stdout

import {useStdout} from 'ink';

const Example = () => {
	const {stdout} = useStdout;

	return 
};

write(data)

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 <Static>, except it can't accept components, it only works with strings.

data

Type: string

Data to write to stdout.

import {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 
};

See additional usage example in examples/use-stdout.

useStderr()

useStderr is a React hook, which exposes stderr stream.

stderr

Type: stream.Writable
Default: process.stderr

Stderr stream.

import {useStderr} from 'ink';

const Example = () => {
	const {stderr} = useStderr();

	return 
};

write(data)

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 <Static>, except it can't accept components, it only works with strings.

data

Type: string

Data to write to stderr.

import {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(options?)

Component that uses 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

Type: boolean
Default: false

Auto focus this component, if there's no active (focused) component right now.

isActive

Type: boolean
Default: true

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.

import {render, useFocus, Text} from 'ink';

const Example = () => {
	const {isFocused} = useFocus();

	return <Text>{isFocused ? 'I am focused' : 'I am not focused'}</Text>;
};

render(<Example />);

See example in examples/use-focus.

useFocusManager()

This hook exposes methods to enable or disable focus management for all components or manually switch focus to next or previous components.

enableFocus()

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.

import {useFocusManager} from 'ink';

const Example = () => {
	const {enableFocus} = useFocusManager();

	useEffect(() => {
		enableFocus();
	}, []);

	return 
};

disableFocus()

Disable focus management for all components. Currently active component (if there's one) will lose its focus.

import {useFocusManager} from 'ink';

const Example = () => {
	const {disableFocus} = useFocusManager();

	useEffect(() => {
		disableFocus();
	}, []);

	return 
};

focusNext()

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.

import {useFocusManager} from 'ink';

const Example = () => {
	const {focusNext} = useFocusManager();

	useEffect(() => {
		focusNext();
	}, []);

	return 
};

focusPrevious()

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.

import {useFocusManager} from 'ink';

const Example = () => {
	const {focusPrevious} = useFocusManager();

	useEffect(() => {
		focusPrevious();
	}, []);

	return 
};

API

render(tree, options?)

Returns: Instance

Mount a component and render the output.

tree

Type: ReactElement

options

Type: object

stdout

Type: stream.Writable
Default: process.stdout

Output stream where app will be rendered.

stdin

Type: stream.Readable
Default: process.stdin

Input stream where app will listen for input.

exitOnCtrlC

Type: boolean
Default: true

Configure whether Ink should listen to Ctrl+C keyboard input and exit the app. This is needed in case process.stdin is in raw mode, because then Ctrl+C is ignored by default and process is expected to handle it manually.

patchConsole

Type: boolean
Default: true

Patch console methods to ensure console output doesn't mix with Ink output. When any of console.* 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.

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.

debug

Type: boolean
Default: false

If true, each update will be rendered as a separate output, without replacing the previous one.

Instance

This is the object that render() returns.

rerender(tree)

Replace previous root node with a new one or update props of the current root node.

tree

Type: 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()

Manually unmount the whole Ink app.

const {unmount} = render(<MyApp />);
unmount();
waitUntilExit()

Returns a promise, which resolves when app is unmounted.

const {unmount, waitUntilExit} = render(<MyApp />);

setTimeout(unmount, 1000);

await waitUntilExit(); // resolves after `unmount()` is called
clear()

Clear output.

const {clear} = render(<MyApp />);
clear();

measureElement(ref)

Measure the dimensions of a particular <Box> 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.

Note: 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

Type: MutableRef

A reference to a <Box> 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 />);

Testing

Ink components are simple to test with ink-testing-library. Here's a simple example that checks how component is rendered:

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

Check out ink-testing-library for more examples and full documentation.

Using React Devtools

Ink supports React Devtools out-of-the-box. To enable integration with React Devtools in your Ink-based CLI, run it with DEV=true environment variable:

$ DEV=true my-cli

Then, start React Devtools itself:

$ npx react-devtools

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.

Useful Components

Useful Hooks

Examples

Maintainers

Comments
  • Fix setRawMode with non-TTY stdin and expose isRawModeSupported on StdinContext

    Fix setRawMode with non-TTY stdin and expose isRawModeSupported on StdinContext

    This pull request tries to implement the fixes discussed in https://github.com/vadimdemedes/ink/issues/166

    There are not yet any tests, as I'd like to get feedback on whether this is the route to go 👍

    A thing that is concerning is that it would be nice to have useful errors for the situation in https://github.com/vadimdemedes/ink-text-input/issues/25, while an application that supports running in CI might want to just ignore setRawMode.

    Todo

    • [x] Should a helpful error be thrown if setRawMode isn't supported, with a configuration option to fall back to doing nothing?
    • [x] Write tests
    opened by eweilow 21
  • Add support for keypress events

    Add support for keypress events

    To be able to have the component rerendered on keypresses and get the keys.

    Ink would need to do readline.emitKeypressEvents(stdin); in https://github.com/vadimdemedes/ink/blob/master/src/components/App.js#L61 And it requires raw-mode.

    After enabling the above, Ink could do something like this internally:

    process.stdin.on('keypress', (str, key) => {
        if (key.ctrl && key.name === 'f') {
            console.log('foo')
        }
    })
    

    My initial idea was to expose a useKeypress() React hook. @vadimdemedes suggested we could just expose the keypresses by default if raw-mode is activated, as the output is being diffed anyway, so the cost is negligible.

    enhancement 
    opened by sindresorhus 21
  • Ink 3 ❤️

    Ink 3 ❤️

    Opening this umbrella issue for you folks to test drive pre-release of Ink 3 - https://github.com/vadimdemedes/ink/releases/tag/v3.0.0-0! Latest docs are in readme and you can install it via:

    $ npm install ink@next react
    

    I haven't migrated any of the 3rd-party components to work with Ink 3 yet, but if you want to migrate a component or two, that's always greatly appreciated! Don't forget to release it as a major version and keep a link to previous docs for those who are still on Ink 2.

    Let me know if there are any breaking changes that weren't described in the release notes above. Basically, if anything comes up, just post here and let's talk.

    help wanted 
    opened by vadimdemedes 20
  • Using react hooks with ink: Invariant Violation

    Using react hooks with ink: Invariant Violation

    Hey,

    I am trying to use the new react hooks with Ink components, but I get the error:

    Invariant Violation: Hooks can only be called inside the body of a function component.
    

    I am using the hooks inside a function component, so I guess it is related to Ink implementation?

    Here is the code:

    import React, {useState} from 'react'
    import {h, render, Component} from "ink";
    
    function useTest() {
    	const [value, update] = useState(Date.now());
    	return [value, update]
    }
    
    const Demo = () => {
    	const [value, update] = useTest();
    
    	setInterval(() => {
    		update(Date.now())
    	}, 1000)
    
    	return <div>Hello: {value}</div>
    
    }
    
    class Wrapper extends Component<any, any>{
    	
    	render() {
    		return <Demo />
    	}
    }
    render((<Wrapper />));
    
    opened by shlomokraus 20
  • Provide available terminal space

    Provide available terminal space

    See https://github.com/vadimdemedes/ink/pull/3#discussion_r126292283.

    The problem is demonstrated perfectly when rendering a progress bar:

    <div>
      <Label/>
      <ProgressBar/>
    </div>
    

    In order to render a progress bar that takes entire terminal space (width), <ProgressBar> needs to know how much space <Label> takes.

    I'm short of ideas on how to implement this properly, so any thoughts are welcome!

    opened by vadimdemedes 20
  • Refactor codebase to TypeScript

    Refactor codebase to TypeScript

    Note: I created an integration package that plugin developers can use to test this new package before it's published. Please, test it by installing @frontside/ink@next and let me know how it goes. I'll deprecate this package after this branch is merged.

    Motivation

    I refactored the codebase to TypeScript to make it easier to replace existing DOM with JSDOM and expose JSDOM for testing in more complex UI scenarios. This could also provide a path to #151.

    Backstory

    My team at Frontside and I are starting to work on the CLI for our new BigTest test runner. We wanted to use Ink but we wanted the CLI UI that could compete with Cypress' web UI.

    To test that kind of Ink UI, we'll want to have the ability to assert on a DOM like tree. Ink has DOM like representation but it's not a real DOM so we can't use existing DOM traversal tools to select elements to assert on. I started looking at replacing Ink's DOM with JSDOM but I found it very difficult to navigate the code because it's not typed.

    So, I wanted to add types before diving further.

    Approach

    I had to make quite a few changes.

    1. Added [email protected]
    2. Split up Ink instance into 2 separate constructors because they use different DOM structures.
    3. Typed all of the files
    4. Added types to yoga-layout-prebuilt and applied types everywhere it's used
    5. Changed xo to use typescript
    6. Upgraded Ava to 3.5.0 because I needed debugging functionality and all ava documentation for typescript references the new version
    7. BREAKING: change Node requirements to 10 & 12
    8. Configured GitHub Actions to use matrix of Node 10|12 & Experimental true|false
    9. Updated tests to typescript (but didn't type them completely)
    10. I used ts-node for tests but used version 7.0.0 of ts-node because it seems to have a bug in new versions that makes it slow.
    11. Removed the static index.d.ts in favour of generated types
    12. Added src directory to the distribution
    13. Changed props for Color component to consume types from Chalk

    Questions/TODO

    • [x] Can we remove PropTypes in favour of just using TypeScript?
    • [ ] Does this need to be a major release because of the Node dependency change?
    • [x] Do you want me to revert to default or can we used named imports?
    • [x] How should we ignore intentional unnecessary curly braces? https://github.com/vadimdemedes/ink/pull/264#discussion_r399668789
    opened by taras 19
  • Focus management

    Focus management

    This PR tries to start a focus management for ink. This is a simple start but it should be extensible without breaking the API.

    I really tried to write tests for this, but either I got issue with babel and "import React" errors or I did not managed to make hook works 🤔

    If you can give me an hint about that ?

    Fixes #242

    opened by jdeniau 18
  • TypeScript definitions

    TypeScript definitions

    Hi, just wanted to stop by and leave a comment to tell you how awesome I think this package is. I've been messing around with it, and decided to try doing something with it with typescript and redux.

    I've pushed ink-typescript-redux-test which is essentially the Counter example, but with more awesomeness.

    • I've created a basic typescript definition for ink, contained in the custom_typings/ink directory.
    • Uses redux for state
    • Uses typescript-fsa for action creators and typescript-fsa-reducers for the reducer

    If you want to include the typings in your project, feel free.

    help wanted 
    opened by xdave 17
  • initial useKeypress

    initial useKeypress

    first draft of #138

    todo

    • [x] fix typescript definition (never written typescript before)
    • [x] run npm test ~- [ ] write documentation~
    • [x] write example
    • [x] write some tests
    opened by jedahan 15
  • Emojis render with trailing spaces within Boxes

    Emojis render with trailing spaces within Boxes

    Hi there! First time using ink, and I'm really enjoying it so far, but I've run into a snag for my project.

    I'm making a small terminal-based board game and want to display emojis inside Box elements, but as the title says, every time I render a Box containing a Text element with emojis inside, any row containing emoji gets one space appended per emoji, breaking the right side of the box, which is otherwise appropriately-sized for its content.

    The extra spaces do not seem to appear when emojis are printed in a Text element outside of a Box.

    Example code:

    const Board = () => {
      return (
        <Box flexDirection='column' alignItems='flex-start'>
          <Box borderStyle='round'>
            <Text>🌊</Text>
          </Box>
          <Box borderStyle='round'>
            <Text>🌊🌊</Text>
          </Box>
          <Box borderStyle='round'>
            <Text>🌊🌊🌊</Text>
          </Box>
        </Box>
      )
    };
    

    Example output: Screenshot_20200908_084036

    opened by quinnvoker 14
  • Printing uncaught error messages

    Printing uncaught error messages

    Something we struggle with Ink is that it doesn't print syntax errors e.g. we try to access an undefined variable. Instead Ink just exits with an error exit code.

    Is this something we've misconfigured or is it Ink that's swallowing errors?

    shipped in v3 
    opened by KyleAMathews 14
  • Migrate Ink to ESM

    Migrate Ink to ESM

    Hi 👋🏼

    It's Pedro and Ju (@Arkham) from the team building Shopify's CLI. First of all, congrats for the great work done on this library. Ink has become the foundation of our CLI and we couldn't be happier with the choice we made. It's so easy to implement UI components on it.

    We've been working on some performance optimizations, and we noticed the loading of Ink blocks the runtime while it loads the CommonJS graph. We'd love for the loading of Ink to happen along with the loading of the CLI's ESM module graph. Unfortunately, we can't make that happen because Ink is implemented in CommonJS with support for ESM interoperability.

    We'd be up for doing the migration ourselves (including Ink's dependencies) with guidance from the maintainers of the project if this transition aligns with the direction of the package.

    opened by pepicrft 0
  • waitUntilExit() not resolved with syntax errors

    waitUntilExit() not resolved with syntax errors

    • Node 16.6.1
    • Jest 29.3.1
    • OSX Ventura 13.0.1 (22A400)

    The following hangs:

    import { render, Text, useApp } from 'ink'
    import React, { ErrorInfo, useCallback } from 'react'
    
    jest.setTimeout(2000)
    
    
    class ErrorBoundary extends React.Component<{cb: (err: Error | null, errorInfo: ErrorInfo) => void}> {
    
      static getDerivedStateFromError(error: Error) {
        return null // https://tinyurl.com/y4k99pwr
      }
      componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        this.props.cb(error, errorInfo)
        this.setState(null) // prevents - https://github.com/reactjs/reactjs.org/issues/3028
      }
      render() {
        return this.props.children
      }
    }
    
    const MalformedComponent = () => {
    
      const renderSomething = () => <Text>Broken</Text>
      return (<React.Fragment key="meh">
        {renderSomething()} */} // intentional syntax error
      </React.Fragment>)
    }
    MalformedComponent.displayName = 'MalformedComponent'
    
    
    const RenderAndExit: React.FC<{}> = () => {
      const {exit} = useApp()
      const errorBoundaryCb = useCallback(
        (err, errInfo) => {
          console.log(`errorBoundaryCb called :>> `, err, errInfo)
          exit(err)
          console.log(`errorBoundaryCb called 2 :>> `, err, errInfo)
        }
        ,
        [],
      )
    
      return (
        <ErrorBoundary cb={errorBoundaryCb}>
          <React.Fragment>
            <MalformedComponent />
          </React.Fragment>
        </ErrorBoundary>
      )
    }
    
    const renderWithInk = async () => {
    
      try {
        const {waitUntilExit} = render(<RenderAndExit />, {})
        await waitUntilExit() // hangs here (despite exit() being called). App unmount waitUntilExit is neither resolved nor rejected as expected
        console.log(`:>> not reached`);
      }
      catch (err) {
        console.log(`err :>> `, err) // not called as expected!!
      }
    }
    
    describe('render-component', () => {
      it(`app not unmounting with malformed component despite exit() being called with error`, async () => {
        await renderWithInk()
      })
    })
    
    

    It's strange because waitUntilExit rejects as expected if the rendered component simply throws an error like:

    const MalformedComponent = () => {
      useEffect(() => {
        setTimeout(() => {
          cb(new Error('cb supplied error'))
        }, 1000)
      }, [])
    
      return <Text>Rendered</Text>
    }
    MalformedComponent.displayName = 'MalformedComponent'
    
    opened by elmpp 0
  • ink + zustand not re-rendering

    ink + zustand not re-rendering

    Hello!

    Note TL;DR: components that read values from zustand don't re-render unless an additional render is forced via, say, a useState setter function.

    I've discovered an odd behavior while implementing an Ink CLI that uses zustand for global state management.

    In summary, I've noticed that components that read values from my Zustand store do not re-render in a predictable manner. "Predictable" in the sense that the same (well, similar, since we're dealing with the DOM) code works totally fine in the browser, but not in the CLI environment.

    Here's the issue. I'm storing a global value (let's call it fruit) in Zustand and I expose a select control to end users so that they can update this global value. I expect that when users arrow up or down to a value and press "Enter," the global state updates.

    Reproduction Repo: https://github.com/mattrothenberg/ink-zustand-test

    No update 😭 CleanShot 2022-11-21 at 18 52 28

    In the browser, this similar code works totally fine.

    Reproduction CodeSandbox: https://codesandbox.io/s/staging-violet-zog39y?file=/src/App.tsx CleanShot 2022-11-21 at 18 59 13

    I've noticed, however, that when I trigger an unrelated state update in my component as well, I do see my component re-render and the new global state is reflected in the CLI.

    Hooray 🎉 CleanShot 2022-11-21 at 19 06 30

    const useAppStore = create<AppStore>((set) => ({
      fruit: "mango",
      setFruit: (newFruit) => {
        set(() => ({ fruit: newFruit }));
      },
    }));
    
    const App = () => {
      const { fruit, setFruit } = useAppStore();
      const [_, setState] = useState(0);
    
      return (
        <Box flexDirection="column">
          <Text>Selected fruit: {fruit}</Text>
          <SelectInput
            items={["apple", "mango", "orange", "pineapple"].map((mode) => {
              return {
                label: mode,
                value: mode,
              };
            })}
            onSelect={(item) => {
              setFruit(item.value);
              // it 'works' 🤣
              setState((curr) => curr + 1);
            }}
          />
        </Box>
      );
    };
    

    Do you have any ideas why this might be happening? I fully expect any call to my Zustand update functions to trigger a re-render for any components that read from the same store.

    Thank you!

    opened by mattrothenberg 2
  • Add gap to Box

    Add gap to Box

    It would be nice to have gap on Box to add space in between Text entries. I'm not sure how to approach the spacing other than to manually inject spaces in the right spots, which feels less elegant than gap.

    opened by dzearing 0
  • fix: emojis render with trailing spaces within boxes

    fix: emojis render with trailing spaces within boxes

    Fixes #505

    The problem is happening due to (I think) yoga and slice-ansi incompatibility on string indexes. slice-ansi counts emojis as 1 char and yoga counts it as 2 (again, I think).

    Here I'm using emoji-regex (which was already pulled in by other dependencies so it doesn't add any real new package to the dependency tree) to count emojis on the current line. Then I'm substracting that amount to the x variable (starting offset on where to replace the string).

    If you find a better way to fix this issue please let me know and I'll be happy to try it out and implement it here.

    Before: image

    After: image

    opened by lucas-labs 0
Releases(v3.2.0)
  • v3.2.0(Oct 7, 2021)

  • v3.1.0(Oct 7, 2021)

  • v3.0.9(Jul 19, 2021)

    Highlights

    • Fix border rendering containing emojis and wide characters (#444) a8f563c
    • Fix undefined errors for focusing unmounted elements (#404) deb9d96
    • Fix the TypeScript definitions for the backgroundColor prop of the <Box> component (#451) 7a3b32d

    https://github.com/vadimdemedes/ink/compare/v3.0.8...v3.0.9

    Source code(tar.gz)
    Source code(zip)
  • v3.0.8(Oct 20, 2020)

    Highlights

    • Fix React Devtools integration (regression after 3.0.7 release) (#390) 1396566

    https://github.com/vadimdemedes/ink/compare/v3.0.7...v3.0.8

    Source code(tar.gz)
    Source code(zip)
  • v3.0.7(Oct 1, 2020)

    Highlights

    • Remove import type statements 8389496
    • Import devtools only when DEV=true (#385) e8f2b34
    • Fix borderColor type (#379) ac28ae4

    https://github.com/vadimdemedes/ink/compare/v3.0.6...v3.0.7

    Source code(tar.gz)
    Source code(zip)
  • v3.0.6(Sep 19, 2020)

  • v3.0.5(Sep 1, 2020)

  • v3.0.4(Aug 18, 2020)

  • v3.0.3(Aug 2, 2020)

  • v3.0.2(Aug 2, 2020)

  • v3.0.1(Jul 31, 2020)

  • v3.0.0(Jul 27, 2020)

    This day is finally here - Ink 3 is out! Read the full announcement at https://vadimdemedes.com/posts/ink-3.

    Highlights

    • Add measureElement() API (#307) d0c4417
    • Free memory from unused Yoga nodes (#308) 7c17e85
    • Rerender on resize (#304) 155e1a0
    • Add global error boundary (#303) 2bcb4c0
    • Remove prop types 2b5dbad
    • Add support for borders in <Box> component (#300) 96625bf
    • Rewrite text rendering (#299) ab0f986
    • Add focus management (#295) 706fdb2
    • Add align-self property to <Box> (#296) 125148a
    • Add <Spacer> component e8ae2af
    • Update only changed props and styles of the node 5739a75
    • Add <Newline> component 9f4c6f8
    • Display console.* logs above main output acb6ed2
    • Add clear() method to clear output fec1c4d
    • Add support for React Devtools (#287) 1a44aac
    • Add useStderr hook to access and write to stderr stream (#286) 360d2da
    • Add write() function to write any string to stdout (#285) 27e313e
    • Add <Transform> component (#277) 9ed46a5
    • Render all frames when CI environment variable is explicitly set to false a056565
    • Add isActive option to useInput hook f419028
    • Add support for <Box display="none"> b51476c
    • Add support for React Suspense 89425d5
    • Reduce rendering speed to 30 frames per second 9b99c5a
    • Refactor codebase to TypeScript (#264) c9631c2

    https://github.com/vadimdemedes/ink/compare/v2.7.1...v3.0.0

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-7(Jul 22, 2020)

    Highlights

    • Move @types/* packages to dev dependencies (#322) 91b2afe
    • Fix type of <Box> to include ref prop (#330) 32d331e

    https://github.com/vadimdemedes/ink/compare/v3.0.0-6...v3.0.0-7

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-6(Jul 7, 2020)

    Highlights

    • Make sure that stack lines that can't be parsed don't break Ink's error handler (#326) a02a59c
    • Fall back to 80 columns if the width of the terminal cannot be determined (#327) 4b45184
    • Add migration guide (#323) f8f9ecf

    https://github.com/vadimdemedes/ink/compare/v3.0.0-5...v3.0.0-6

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-5(Jul 5, 2020)

    Highlights

    • New logo 60e8d15
    • Fix error boundary failing when error doesn't have a stack 5b6fa97
    • Remeasure text dimensions when text node is changed 5f080fa
    • Ignore Ctrl+C in useInput hook 50ecd64

    https://github.com/vadimdemedes/ink/compare/v3.0.0-4...v3.0.0-5

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-4(Jun 23, 2020)

    Highlights

    • Upgrade to Chalk 4 (#316) 9df100a
    • Ignore <Text> and <Transform> components with falsy children (#315) 8e68295

    https://github.com/vadimdemedes/ink/compare/v3.0.0-3...v3.0.0-4

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-3(Jun 23, 2020)

  • v3.0.0-2(Jun 23, 2020)

    Highlights

    • Fix error handler with non-existent file path in the stack (#313) 4cf1c28
    • Handle Tab, Shift+Tab and Backspace keys in useInput hook (#312) a715395

    https://github.com/vadimdemedes/ink/compare/v3.0.0-1...v3.0.0-2

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-1(Jun 21, 2020)

  • v3.0.0-0(Jun 20, 2020)

    Ink 3 pre-release is finally here. I'm going to save detailed release description for 3.0.0, but in this one I'm going to link to all important changes that were made and highlight the breaking ones.

    Install

    $ npm install ink@next react
    

    Breaking changes

    Text must be wrapped in <Text> component (#299)

    There are much more details on this change in the linked PR above, but the TLDR is that all text must be wrapped in <Text> component now. Otherwise Ink will throw an error like this:

    Text string "Hello World" must be rendered inside component

    If you've used to building apps with React Native, Ink now has the same requirement in regards to text so it should feel familiar to you.

    // Before
    <Box>Hello World</Box>
    
    // After
    <Text>Hello World</Text>
    

    Note: It's allowed to have nested <Text> components.

    Merged <Color> component functionality into <Text> (#301)

    In Ink 3 there's no more <Color> component. Instead, you can use color and backgroundColor props directly in <Text>. The way you specify colors has also changed a bit. Before there was a separate prop for each color, now there are just two props, which accept CSS-like values.

    // Before
    <Color red>
    	<Text>Hello World</Text>
    </Color>
    
    <Color hex="#ffffff">
    	<Text>Hello World</Text>
    </Color>
    
    <Color white bgGreen>
    	<Text>Hello World</Text>
    </Color>
    
    // After
    <Text color="red">Hello World</Text>
    
    <Text color="#ffffff">Hello World</Text>
    
    <Text color="white" backgroundColor="green">Hello World</Text>
    

    Removed <div> and <span> (#306)

    It was "illegal" to use these tags directly before, but now they're removed completely. If you are using them, switch from <div> to <Box> and from <span> to <Text>.

    Removed unstable__transformChildren from <Box> and <Text> (ab36e7f)

    Previously this function was used to transform the string representation of component's children. You can still do the same stuff, but you should use <Transform> component instead.

    // Before
    <Box unstable__transformChildren={children => children.toUpperCase()}>
    	Hello World
    </Box>
    
    // After
    <Transform transform={children => children.toUpperCase()}>
    	<Text>Hello World</Text>
    </Transform>
    

    Removed AppContext, StdinContext and StdoutContext in favor of useApp, useStdin and useStdout hooks 055a196

    Hooks are the future.

    // Before
    import {AppContext, StdinContext, StdoutContext} from 'ink';
    
    const Example = () => (
    	<AppContext.Consumer>
    		{appProps => (
    			<StdinContext.Consumer>
    				{stdinProps => (
    					<StdoutContext.Consumer>
    						{stdoutProps => (
    							…
    						)}
    					</StdoutContext.Consumer>
    				)}
    			</StdinContext.Consumer>
    		)}
    	</AppContext.Consumer>
    );
    
    // After
    import {useApp, useStdin, useStdout} from 'ink';
    
    const Example = () => {
    	const appProps = useApp();
    	const stdinProps = useStdin();
    	const stdoutProps = useStdout();
    
    	return …;
    };
    

    New <Static> component (#281)

    Functionality has remained the same, but API has changed and performance has significantly improved. New API looks very similar to the one commonly used in virtual list libraries, like react-tiny-virtual-list.

    // Before
    <Static>
    	{items.map(item => (
    		<Text key={item.id}>
    			{item.title}
    		</Text>
    	))}
    </Static>
    
    // After
    <Static items={items}>
    	{item => (
    		<Text key={item.id}>
    			{item.title}
    		</Text>
    	)}
    </Static>
    

    Highlights

    • Add measureElement() API (#307) d0c4417
    • Free memory from unused Yoga nodes (#308) 7c17e85
    • Rerender on resize (#304) 155e1a0
    • Add global error boundary (#303) 2bcb4c0
    • Remove prop types 2b5dbad
    • Add support for borders in <Box> component (#300) 96625bf
    • Rewrite text rendering (#299) ab0f986
    • Add focus management (#295) 706fdb2
    • Add align-self property to <Box> (#296) 125148a
    • Add <Spacer> component e8ae2af
    • Update only changed props and styles of the node 5739a75
    • Add <Newline> component 9f4c6f8
    • Display console.* logs above main output acb6ed2
    • Add clear() method to clear output fec1c4d
    • Add support for React Devtools (#287) 1a44aac
    • Add useStderr hook to access and write to stderr stream (#286) 360d2da
    • Add write() function to write any string to stdout (#285) 27e313e
    • Add <Transform> component (#277) 9ed46a5
    • Render all frames when CI environment variable is explicitly set to false a056565
    • Add isActive option to useInput hook f419028
    • Add support for <Box display="none"> b51476c
    • Add support for React Suspense 89425d5
    • Reduce rendering speed to 30 frames per second 9b99c5a
    • Refactor codebase to TypeScript (#264) c9631c2

    https://github.com/vadimdemedes/ink/compare/v2.7.1...v3.0.0-0

    Source code(tar.gz)
    Source code(zip)
  • v2.7.1(Feb 16, 2020)

  • v2.7.0(Feb 3, 2020)

    Highlights

    • Move @types/react to be an optional peer dependency (#257) 084cded

    https://github.com/vadimdemedes/ink/compare/v2.6.0...v2.7.0

    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(Nov 28, 2019)

    Highlights

    • Upgrade dependencies (#247) 2917bf9
    • Remove @types/react from dependencies (#236) 58314df

    https://github.com/vadimdemedes/ink/compare/v2.5.0...v2.6.0

    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Oct 5, 2019)

    Highlights

    • Clear terminal if output is taller than viewport (#210) af480b8
    • Add useApp, useStdin and useStdout hooks (#235) e0d2b4f
    • Fix removal of props in non-experimental mode e5b7569
    • Fix aligning multiple text nodes within <Box> 1f96300

    https://github.com/vadimdemedes/ink/compare/v2.4.0...v2.5.0

    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Sep 26, 2019)

    Highlights

    • Add useInput hook to handle user input (#227) 4960ff7
    • Update react, react-reconciler and scheduler dependencies, which fixes useEffect hook usage 4a0afb6 9487c00
    • Add new experimental reconciler (#232) 8943332
    • Don't error on empty string input to (#219) 46f243d

    https://github.com/vadimdemedes/ink/compare/v2.3.0...v2.4.0

    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Jun 25, 2019)

  • v2.2.0(May 23, 2019)

    Highlights

    • Fix setRawMode() with non-TTY stdin and expose isRawModeSupported on StdinContext https://github.com/vadimdemedes/ink/commit/7f0b2c086972788702dd283b30e379a8c061638d

    Credits

    ❤️ Thanks to @eweilow for contributing towards this release!

    All changes

    https://github.com/vadimdemedes/ink/compare/v2.1.1...v2.2.0

    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Apr 2, 2019)

    • Cut text taller than its container height 328b750
    • Prevent writing empty strings to output instance 8f44d6d

    https://github.com/vadimdemedes/ink/compare/v2.1.0...v2.1.1

    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Mar 31, 2019)

    • Reject waitUntilExit() promise if error occurs during render (#178) 526d5c3
    • Render only last frame on CI 92f4393
    • Fix rendering identical children 98b3c3e
    • Add wrapping/truncating for text content e8e6811
    • Allow passing an error on exit (#165) dcaaafa
    • Initialize reconciler once (#170) 405fe7a
    • Add flexBasis prop to cfcc1a7
    • Add minWidth and minHeight props to e5f7322
    • Allow width/height to be set in percent 538010a

    Credits

    ❤️ Thanks to @mAAdhaTTah, @sindresorhus and @sebald for contributing to this release!

    https://github.com/vadimdemedes/ink/compare/v2.0.6...v2.1.0

    Source code(tar.gz)
    Source code(zip)
  • v2.0.6(Mar 18, 2019)

    • Prevent squashing of text nodes if flexDirection="column" is set on container 2e8091d

    https://github.com/vadimdemedes/ink/compare/v2.0.5...v2.0.6

    Source code(tar.gz)
    Source code(zip)
Owner
Vadim Demedes
Working on Lotus (https://getlotus.app) to easier to keep up with GitHub notifications without stress.
Vadim Demedes
Control the macOS dark mode from the command-line

dark-mode Control the macOS dark mode from the command-line Requires macOS 10.10 or later. macOS 10.13 or earlier needs to download the Swift runtime

Sindre Sorhus 630 Dec 30, 2022
node.js command-line interfaces made easy

Commander.js The complete solution for node.js command-line interfaces. Read this in other languages: English | 简体中文 Commander.js Installation Declari

TJ Holowaychuk 24k Jan 8, 2023
Pretty unicode tables for the command line

cli-table3 This utility allows you to render unicode-aided tables on the command line from your node.js scripts. cli-table3 is based on (and api compa

null 418 Dec 28, 2022
Control the Plash app from the command-line

plash-cli Control the Plash app from the command-line Install $ npm install --global plash Requires Node.js 14 or later. Requires Plash 2.3.0 or late

Sindre Sorhus 33 Dec 30, 2022
A C++ based command-line (CLI) program that lets you manage your tasks

COMMAND LINE INTERFACE TODO APP a command-line (CLI) program that lets you manage your tasks. The specification for this project is written down as te

Rahul Prabhakar 1 Dec 25, 2021
Close chrome tabs from command-line (macOS only)

Close-tab Read all tabs from an activated window of the chrome, open with vi prompt, you can close tabs by deleting lines. Istallation npm install -g

Karl Saehun Chung 8 Jun 18, 2022
1History is a command line tool to backup your histories of different browsers into one place

1History All your history in one place. 1History is a command line tool to backup your histories of different browsers into one place. Features Suppor

null 340 Dec 31, 2022
Wordle and Termooo style classic word guessing game for the command line. One new word per day!

Wordle and Termooo style classic word guessing game for the command line. One new word per day!

Anderson Silva 3 Nov 27, 2022
SFDX Plugin to set Email Deliverability Access Level for an org easily and quickly via command line interface

SFDX Plugin to set Email Deliverability Access Level for an org easily and quickly via command line interface

Max Goldfarb 11 Dec 16, 2022
A project for FAST command line interface tools.

FAST CLI Project This is the FAST CLI project, containing the FAST CLI package and other related CLI packages for FAST project creation and management

Microsoft 24 Dec 5, 2022
A command line interface for programmatically creating data silos on app.transcend.io

Table of Contents Overview Installation Authentication transcend.yml Usage tr-pull tr-push CI Integration Dynamic Variables tr-scan Overview A command

Transcend 15 Dec 13, 2022
Autify Command Line Interface (CLI)

Autify Command Line Interface (CLI) Autify CLI can help your integration with Autify! Autify Command Line Interface (CLI) Usage Commands Usage Note: n

Autify 36 Jan 2, 2023
Generate a Node.js command line tool from an OpenAPI definition

OpenAPI Commander Generate a Node.js command line tool from an OpenAPI definition using the commander library. Example usage Usage: Subcommands groupe

Barry Coughlan 12 Jan 3, 2023
Windows command line tool to block outbound connections for files within a directory.

fwg A Windows command line tool to block outbound connections for files within a directory. fwg utilizes the power of PowerShell and Windows Network S

raymond wang 3 Jul 19, 2022
The command-line interface for versum

@versumstudios/cli The command-line interface for versum. versum-cli Usage Contributing How to use Export Templates Usage To install the latest versio

Versum Studios 8 Nov 3, 2022
LinkFree CLI is a command line tool that helps you to create your LinkFree profile through CLI.

LinkFree CLI LinkFree CLI is a command line tool that helps you to create your LinkFree profile through CLI. Demo Using the CLI (Commands) Note First

Pradumna Saraf 32 Dec 26, 2022
Run a command when a certain file exists, and/or watch files to rerun on changes

Run a command when a certain file exists, and/or watch files to rerun on changes

EGOIST 45 Sep 23, 2022
CLI Command for Two Factor Authentication.🚀

CLI Command for Two Factor Authentication.??

Yuga Sun 7 Nov 5, 2022
A small Discord moderation bot, without a command handler

A small Discord moderation bot, without a command handler Getting started (Add Star ⭐ <3) ?? Requirements Node.js A code editor (visual studio code, a

null 10 Mar 12, 2022