Building interactive CLI tools with raw stdin/stdout is painful. Ink lets you build CLI apps using React components — useState, useEffect, flexbox layout — all in the terminal.
What Is Ink?
Ink provides the React experience for command-line apps. Build interactive CLIs using JSX components, hooks, and flexbox — just like building a web app, but for the terminal.
Quick Start
npm install ink react
import React, { useState, useEffect } from 'react';
import { render, Text, Box } from 'ink';
function App() {
const [counter, setCounter] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCounter(prev => prev + 1);
}, 100);
return () => clearInterval(timer);
}, []);
return (
<Box flexDirection="column" padding={1}>
<Text bold color="green">My CLI App</Text>
<Text>Counter: {counter}</Text>
</Box>
);
}
render(<App />);
Layout With Flexbox
<Box flexDirection="row" gap={2}>
<Box width="50%" borderStyle="round" padding={1}>
<Text>Left Panel</Text>
</Box>
<Box width="50%" borderStyle="round" padding={1}>
<Text>Right Panel</Text>
</Box>
</Box>
Built-In Components
import { Text, Box, Newline, Spacer, Static, Transform } from 'ink';
// Styled text
<Text bold color="red" underline>Error!</Text>
<Text color="#ff6347" backgroundColor="black">Custom colors</Text>
<Text dimColor italic>Subtle hint</Text>
// Spacer (like flex-grow)
<Box>
<Text>Left</Text>
<Spacer />
<Text>Right</Text>
</Box>
User Input
import { useInput, useApp } from 'ink';
function Game() {
const [x, setX] = useState(0);
const { exit } = useApp();
useInput((input, key) => {
if (key.leftArrow) setX(x => x - 1);
if (key.rightArrow) setX(x => x + 1);
if (input === 'q') exit();
});
return <Text>{' '.repeat(x)}🏃</Text>;
}
Real-World Examples
Ink powers many popular CLI tools:
- Gatsby — development server output
- Prisma — database management CLI
- Shopify — Hydrogen CLI
- Jest — test runner output (parts)
Community Components
-
ink-text-input— text input field -
ink-select-input— selection list -
ink-spinner— loading spinners -
ink-table— data tables -
ink-progress-bar— progress bars -
ink-gradient— gradient text -
ink-big-text— ASCII art text
Testing
import { render } from 'ink-testing-library';
test('renders greeting', () => {
const { lastFrame } = render(<Greeting name="World" />);
expect(lastFrame()).toContain('Hello, World');
});
Get Started
- GitHub — 27K+ stars
- Documentation
- create-ink-app
Building developer tools? My Apify scrapers power data-driven CLIs. Custom solutions: spinov001@gmail.com
Top comments (0)