Generate Ink (React for CLI) components for terminal UIs with hooks, state management, and layout components.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add a5c-ai/babysitter --skill ink-component-generator --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ink Component Generator?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/a5c-ai-ink-component-generator-babysitter)More formats (shields.io, HTML) on the badges page.
---
name: ink-component-generator
description: Generate Ink (React for CLI) components for terminal UIs with hooks, state management, and layout components.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
graph:
domains: [domain:software-engineering]
specializations: [specialization:cli-mcp-development]
skillAreas: [skill-area:cli-design, skill-area:command-line-interface-tools]
roles: [role:backend-engineer, role:platform-engineer]
workflows: [workflow:feature-development]
topics: [topic:developer-experience]
---
# Ink Component Generator
Generate Ink (React) components for terminal UIs.
## Capabilities
- Generate Ink React components
- Create custom hooks for CLI state
- Set up layout components (Box, Text)
- Implement input handling
- Create loading and progress components
- Set up testing with ink-testing-library
## Usage
Invoke this skill when you need to:
- Build terminal UIs with React patterns
- Create interactive CLI components
- Implement stateful terminal interfaces
- Set up Ink project structure
## Inputs
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| projectName | string | Yes | Project name |
| components | array | Yes | Component definitions |
| includeHooks | boolean | No | Generate custom hooks |
### Component Structure
```json
{
"components": [
{
"name": "SelectList",
"type": "interactive",
"props": ["items", "onSelect"],
"state": ["selectedIndex"]
}
]
}
```
## Generated Patterns
### Select List Component
```tsx
import React, { useState, useCallback } from 'react';
import { Box, Text, useInput, useApp } from 'ink';
interface SelectListProps {
items: string[];
onSelect: (item: string, index: number) => void;
}
export const SelectList: React.FC<SelectListProps> = ({ items, onSelect }) => {
const [selectedIndex, setSelectedIndex] = useState(0);
const { exit } = useApp();
useInput((input, key) => {
if (key.upArrow) {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
} else if (key.downArrow) {
setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
} else if (key.return) {
onSelect(items[selectedIndex], selectedIndex);
} else if (input === 'q' || key.escape) {
exit();
}
});
return (
<Box flexDirection="column">
{items.map((item, index) => (
<Box key={item}>
<Text color={index === selectedIndex ? 'green' : undefined}>
{index === selectedIndex ? '> ' : ' '}
{item}
</Text>
</Box>
))}
<Box marginTop={1}>
<Text dimColor>Use arrow keys to navigate, Enter to select, q to quit</Text>
</Box>
</Box>
);
};
```
### Text Input Component
```tsx
import React, { useState } from 'react';
import { Box, Text, useInput } from 'ink';
interface TextInputProps {
placeholder?: string;
onSubmit: (value: string) => void;
mask?: string;
}
export const TextInput: React.FC<TextInputProps> = ({
placeholder = '',
onSubmit,
mask,
}) => {
const [value, setValue] = useState('');
const [cursor, setCursor] = useState(0);
useInput((input, key) => {
if (key.return) {
onSubmit(value);
return;
}
if (key.backspace || key.delete) {
setValue((prev) => prev.slice(0, -1));
setCursor((prev) => Math.max(0, prev - 1));
return;
}
if (!key.ctrl && !key.meta && input) {
setValue((prev) => prev + input);
setCursor((prev) => prev + 1);
}
});
const displayValue = mask ? mask.repeat(value.length) : value;
return (
<Box>
<Text>
{displayValue || <Text dimColor>{placeholder}</Text>}
<Text backgroundColor="white"> </Text>
</Text>
</Box>
);
};
```
### Spinner Component
```tsx
import React, { useState, useEffect } from 'react';
import { Text } from 'ink';
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
interface SpinnerProps {
label?: string;
}
export const Spinner: React.FC<SpinnerProps> = ({ label }) => {
const [frame, setFrame] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setFrame((prev) => (prev + 1) % frames.length);
}, 80);
return () => clearInterval(timer);
}, []);
return (
<Text>
<Text color="green">{frames[frame]}</Text>
{label && <Text> {label}</Text>}
</Text>
);
};
```
### Custom Hook - useAsync
```tsx
import { useState, useEffect, useCallback } from 'react';
interface AsyncState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
export function useAsync<T>(
asyncFn: () => Promise<T>,
deps: any[] = []
): AsyncState<T> & { refetch: () => void } {
const [state, setState] = useState<AsyncState<T>>({
data: null,
loading: true,
error: null,
});
const execute = useCallback(async () => {
setState({ data: null, loading: true, error: null });
try {
const data = await asyncFn();
setState({ data, loading: false, error: null });
} catch (error) {
setState({ data: null, loading: false, error: error as Error });
}
}, deps);
useEffect(() => {
execute();
}, [execute]);
return { ...state, refetch: execute };
}
```
## Dependencies
```json
{
"dependencies": {
"ink": "^4.0.0",
"react": "^18.0.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
"ink-testing-library": "^3.0.0"
}
}
```
## Target Processes
- tui-application-framework
- interactive-form-implementation
- dashboard-monitoring-tui
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!