Ink

TUIReactCLIJSXTerminal

GitHub Overview

vadimdemedes/ink

🌈 React for interactive command-line apps

Stars30,219
Watchers112
Forks699
Created:June 12, 2017
Language:TypeScript
License:MIT License

Topics

clicommand-lineflexboxinteractivejavascriptreact

Star History

vadimdemedes/ink Star History
Data as of: 7/25/2025, 11:10 AM

Ink

Ink is a JavaScript library that enables developers to build terminal user interfaces using React components. Known as "React for CLI", it allows developers to leverage their existing React knowledge to create powerful terminal applications with familiar component-based architecture.

Features

React Component Model

  • JSX Syntax: Write terminal UIs using familiar JSX syntax
  • State Management: Use React hooks like useState and useEffect
  • Component Reusability: Reuse UI components across projects
  • Virtual DOM: Efficient differential rendering for terminal updates

Rich Built-in Capabilities

  • Flexbox Layout: Uses Facebook's Yoga layout engine for precise positioning
  • Text Styling: Support for colors, bold, italic, underline, and strikethrough
  • Focus Management: Tab/Shift+Tab navigation between interactive elements
  • Input Handling: Process keyboard input events with ease

Developer Experience

  • TypeScript Support: First-class TypeScript support for type safety
  • Hot Reload: Fast feedback during development
  • Debug Support: React DevTools-like debugging experience
  • Testing: Easy component testing with familiar patterns

Basic Usage

Installation

npm install ink react
# or
yarn add ink react

Hello World

import React from 'react';
import {render, Text} from 'ink';

const App = () => <Text>Hello World</Text>;

render(<App />);

User Input Handling

import React, {useState} from 'react';
import {render, Text, useInput} from 'ink';

const Counter = () => {
  const [counter, setCounter] = useState(0);

  useInput((input, key) => {
    if (input === 'q') {
      process.exit();
    }

    if (key.upArrow) {
      setCounter(counter + 1);
    }

    if (key.downArrow) {
      setCounter(counter - 1);
    }
  });

  return (
    <>
      <Text>Count: {counter}</Text>
      <Text color="gray">Use up/down arrows, 'q' to quit</Text>
    </>
  );
};

render(<Counter />);

Layout Construction

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

const App = () => (
  <Box flexDirection="column" padding={1}>
    <Box marginBottom={1}>
      <Text color="green">Header</Text>
    </Box>
    
    <Box flexDirection="row">
      <Box marginRight={2}>
        <Text>Left Sidebar</Text>
      </Box>
      
      <Box flexGrow={1}>
        <Text>Main Content</Text>
      </Box>
    </Box>
    
    <Box marginTop={1}>
      <Text color="gray">Footer</Text>
    </Box>
  </Box>
);

render(<App />);

Core Components

Text

Display and style text content

<Text color="red" bold underline>
  Styled text
</Text>

Box

Flexbox-based layout container

<Box
  flexDirection="row"
  justifyContent="center"
  alignItems="center"
  padding={2}
  border={{type: 'round'}}
>
  <Text>Centered content</Text>
</Box>

Static

Content that permanently remains on screen

<Static items={logs}>
  {log => <Text key={log.id}>{log.message}</Text>}
</Static>

Real-world Example

CLI Progress Display

import React, {useState, useEffect} from 'react';
import {render, Box, Text} from 'ink';
import Spinner from 'ink-spinner';

const ProgressApp = () => {
  const [progress, setProgress] = useState(0);
  const [isComplete, setIsComplete] = useState(false);

  useEffect(() => {
    const timer = setInterval(() => {
      setProgress(prev => {
        if (prev >= 100) {
          setIsComplete(true);
          clearInterval(timer);
          return 100;
        }
        return prev + 10;
      });
    }, 500);

    return () => clearInterval(timer);
  }, []);

  return (
    <Box flexDirection="column">
      <Box>
        <Text color={isComplete ? 'green' : 'blue'}>
          {isComplete ? '✓' : <Spinner type="dots" />}
        </Text>
        <Text> Processing... {progress}%</Text>
      </Box>
      
      <Box marginTop={1}>
        <Text>
          {'█'.repeat(Math.floor(progress / 5))}
          {'░'.repeat(20 - Math.floor(progress / 5))}
        </Text>
      </Box>
    </Box>
  );
};

render(<ProgressApp />);

Ecosystem

Related Packages

  • ink-spinner: Spinner components
  • ink-select-input: Selection menus
  • ink-text-input: Text input fields
  • ink-table: Table display
  • ink-gradient: Gradient effects
  • ink-link: Clickable links

Notable Adopters

  • Cloudflare Wrangler: Serverless development CLI
  • Shopify CLI: Shopify development tools
  • Prisma: Database management CLI
  • Linear CLI: Project management CLI

Advantages

  • Low Learning Curve: Immediate productivity for React developers
  • Maintainability: Component-based architecture improves maintainability
  • Testability: Easy unit testing with familiar patterns
  • Performance: Efficient differential updates
  • Active Community: Rich ecosystem of third-party components

Limitations

  • React Dependency: Includes React bundle size
  • Learning Curve: Requires React knowledge
  • Mouse Support: Limited mouse interaction support
  • Complex Layouts: Advanced terminal features may be restricted

Comparison with Other Libraries

FeatureInkBlessedTerminal Kit
ApproachReact-likeWidget-basedFunction-based
Learning CurveLow (for React devs)MediumMedium
PerformanceHighHighMedium
EcosystemRichMediumMedium
TypeScriptExcellentPartialPartial

Summary

Ink is the most accessible TUI library for React developers. It allows developers to leverage their existing React knowledge to build maintainable terminal applications. It's particularly well-suited for developing CLI tools that require ongoing maintenance, such as team development tools and CI/CD utilities.