inkjs-design

Comprehensive guide for building terminal UIs with Ink.js (React for CLI).

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "inkjs-design" with this command: npx skills add akiojin/skills/akiojin-skills-inkjs-design

Ink.js Design

Comprehensive guide for building terminal UIs with Ink.js (React for CLI).

Quick Start

Creating a New Component

  • Determine component type: Screen / Part / Common

  • Reference component-patterns.md for similar patterns

  • Add type definitions

  • Implement component

  • Write tests

Common Issues & Solutions

Issue Reference

Emoji width misalignment ink-gotchas.md

Ctrl+C called twice ink-gotchas.md

useInput conflicts ink-gotchas.md

Layout breaking responsive-layout.md

Screen navigation multi-screen-navigation.md

Directory Conventions

src/cli/ui/ ├── components/ │ ├── App.tsx # Root component with screen management │ ├── common/ # Common input components (Select, Input) │ ├── parts/ # Reusable UI parts (Header, Footer) │ └── screens/ # Full-screen components ├── hooks/ # Custom hooks ├── utils/ # Utility functions └── types.ts # Type definitions

Component Classification

Screen (Full-page views)

  • Represents a complete screen/page

  • Handles keyboard input via useInput

  • Implements Header/Content/Footer layout

  • Manages screen-level state

Part (Reusable elements)

  • Reusable UI building blocks

  • Optimized with React.memo

  • Stateless/pure components preferred

  • Accept configuration via props

Common (Input components)

  • Basic input components

  • Support both controlled and uncontrolled modes

  • Handle focus management

  • Provide consistent UX

Essential Patterns

  1. Icon Width Override

Fix string-width v8 emoji width calculation issues:

const WIDTH_OVERRIDES: Record<string, number> = { "⚡": 1, "✨": 1, "🐛": 1, "🔥": 1, "🚀": 1, "🟢": 1, "🟠": 1, "✅": 1, "⚠️": 1, };

const getIconWidth = (icon: string): number => { const baseWidth = stringWidth(icon); const override = WIDTH_OVERRIDES[icon]; return override !== undefined ? Math.max(baseWidth, override) : baseWidth; };

  1. useInput Conflict Avoidance

Multiple useInput hooks all fire - use early return or isActive:

useInput((input, key) => { if (disabled) return; // Early return when inactive // Handle input... }, { isActive: isFocused });

  1. Ctrl+C Handling

render(<App />, { exitOnCtrlC: false });

// In component const { exit } = useApp(); useInput((input, key) => { if (key.ctrl && input === "c") { cleanup(); exit(); } });

  1. Dynamic Height Calculation

const { rows } = useTerminalSize(); const HEADER_LINES = 3; const FOOTER_LINES = 2; const contentHeight = rows - HEADER_LINES - FOOTER_LINES; const visibleItems = Math.max(5, contentHeight);

  1. React.memo with Custom Comparator

function arePropsEqual<T>(prev: Props<T>, next: Props<T>): boolean { if (prev.items.length !== next.items.length) return false; for (let i = 0; i < prev.items.length; i++) { if (prev.items[i].value !== next.items[i].value) return false; } return prev.selectedIndex === next.selectedIndex; }

export const Select = React.memo(SelectComponent, arePropsEqual);

  1. Multi-Screen Navigation

type ScreenType = "main" | "detail" | "settings";

const [screenStack, setScreenStack] = useState<ScreenType[]>(["main"]); const currentScreen = screenStack[screenStack.length - 1];

const navigateTo = (screen: ScreenType) => { setScreenStack(prev => [...prev, screen]); };

const goBack = () => { if (screenStack.length > 1) { setScreenStack(prev => prev.slice(0, -1)); } };

Detailed References

Core Patterns

  • Component Patterns - Screen/Part/Common architecture

  • Hooks Guide - Custom hook design patterns

Advanced Topics

  • Multi-Screen Navigation - Screen stack management

  • Animation Patterns - Spinners and progress bars

  • State Management - Complex state patterns

  • Responsive Layout - Terminal size handling

  • Performance Optimization - Optimization techniques

  • Input Handling - Keyboard input patterns

Troubleshooting

  • Ink Gotchas - Common issues and solutions

  • Testing Patterns - ink-testing-library usage

Examples

See examples/ for practical implementation examples.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

opentui-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

drawio

No summary provided by upstream source.

Repository SourceNeeds Review
General

gh-fix-ci

No summary provided by upstream source.

Repository SourceNeeds Review
General

gh-pr

No summary provided by upstream source.

Repository SourceNeeds Review