@baseline-operations/react-console
v0.1.5
Published
React 19+ TypeScript library for building console/terminal applications using JSX
Maintainers
Readme
React Console
React 19+ TypeScript library for building console/terminal applications using JSX and React components.
Alpha Notice: This library is currently in alpha (v0.x). The API may change between versions without notice. We do not guarantee backwards compatibility until v1.0.0. Please pin your dependency to a specific version if stability is important for your project. We will do our best to retain compatibility, but the goal will be to align with React Native by v1.0.0.
Overview
React Console allows you to build terminal/console applications using React 19+ with JSX, components, hooks, and all the modern React features - but for console output instead of HTML.
Features
- ✅ React 19+ support with all modern hooks (
use,useOptimistic,useActionState,useFormStatus, etc.) - ✅ React Compiler integration (automatic memoization)
- ✅ TypeScript with strict mode
- ✅ ESLint 9+ configuration
- ✅ Component-based architecture (Text, View, TextInput, Button, Switch, FlatList, and more)
- ✅ React Native-style APIs (Platform, Dimensions, Clipboard, Alert, AppState, etc.)
- ✅ ANSI color and styling support (foreground, background, text styles)
- ✅ Layout system with padding, margin, responsive sizing
- ✅ Interactive components with JSX-style event handlers
- ✅ Mouse support for terminals that support it
- ✅ Tab navigation with automatic tab index management
- ✅ Multiline input with width and height constraints
- ✅ Responsive sizing (percentages, viewport units, character units)
- ✅ Reactive terminal resizing - UI adjusts automatically when terminal dimensions change
- ✅ Custom React renderer using
react-reconciler - ✅ Multiple rendering modes (static, interactive, fullscreen)
Installation
npm install @baseline-operations/react-console react@^19.0.0Native addon (no fallback)
This package requires a native addon. There is no TypeScript/JavaScript fallback: if the addon fails to load, the library throws with a clear error. You must either:
- Use a prebuild (when available for your platform), or
- Build from source: install Rust (stable, 1.85+), then run
npm run build:nativein the package root.
Supported platforms (build from source): macOS (x64, arm64), Linux (x64, arm64), Windows (x64). Node 20+.
Quick Start
import React from 'react';
import { render, Text, View } from '@baseline-operations/react-console';
function App() {
return (
<View padding={1}>
<Text color="cyan" bold>
Hello, Console!
</Text>
<Text>This is React in the terminal.</Text>
</View>
);
}
render(<App />);Components
<View> / <Box>
Container component for layout with padding, margin, and responsive sizing.
<View padding={2} margin={1}>
<Text>Content with padding and margin</Text>
</View>
<View width="50%" height="80vh">
<Text>Responsive sizing</Text>
</View><Text>
Styled text output with colors and formatting. Supports nested Text components for inline styling.
<Text color="green" bold>Success!</Text>
<Text color="#FF0000">Red text using hex color</Text>
<Text underline>Underlined text</Text>
{/* Nested Text for inline styling */}
<Text>
Normal text with <Text bold color="yellow">highlighted</Text> content
</Text><LineBreak>
Explicit line break component.
<Text>First line</Text>
<LineBreak />
<Text>Second line</Text><TextInput>
Text input component with React Native-compatible API. Supports single-line and multiline input.
const [value, setValue] = useState('');
<TextInput
value={value}
onChangeText={setValue}
onSubmitEditing={(event) => console.log('Submitted:', event.nativeEvent.text)}
placeholder="Enter text..."
maxWidth={80}
multiline
numberOfLines={5}
autoFocus
/>;Props:
value/defaultValue- Controlled or uncontrolled inputplaceholder- Placeholder textplaceholderTextColor- Placeholder text colormaxLength- Maximum character lengthmaxWidth- Maximum input width (defaults to terminal width)multiline- Allow multiline inputnumberOfLines- Maximum number of lines for multiline inputsecureTextEntry- Mask input for passwordskeyboardType- Input type:'default','numeric','decimal-pad','number-pad'editable/disabled- Enable/disable inputautoFocus- Auto-focus on mounttabIndex- Tab orderonChangeText- Called with text string when value changesonSubmitEditing- Called when Enter is pressedonEndEditing- Called when editing endsonKeyDown/onKeyUp/onKeyPress- Keyboard event handlersonFocus/onBlur- Focus event handlers
<Button>
Clickable button component with keyboard and mouse support.
<Button onClick={() => console.log('Clicked!')} label="Click Me" tabIndex={0} />Props:
onClick- Click handleronPress- Press handler (alias for onClick)onMouseDown/onMouseUp/onMouseMove- Mouse event handlersonKeyDown/onKeyUp/onKeyPress- Keyboard event handlersonFocus/onBlur- Focus event handlerstabIndex- Tab orderdisabled- Disable button
<Pressable>
React Native-like pressable component that can wrap any content. Supports state-based styling.
<Pressable
onPress={() => console.log('Pressed!')}
onPressIn={() => console.log('Press started')}
onPressOut={() => console.log('Press ended')}
style={{ padding: 1, backgroundColor: 'blue' }}
pressedStyle={{ backgroundColor: 'darkblue' }}
hoveredStyle={{ backgroundColor: 'lightblue' }}
focusedStyle={{ borderColor: 'yellow' }}
tabIndex={0}
>
<Text>Pressable content</Text>
</Pressable>;
{
/* Function-based styling */
}
<Pressable
onPress={handlePress}
style={({ pressed, focused, hovered }) => ({
backgroundColor: pressed ? 'darkblue' : hovered ? 'lightblue' : 'blue',
borderColor: focused ? 'yellow' : 'transparent',
})}
>
{({ pressed }) => <Text>{pressed ? 'Pressing...' : 'Press me'}</Text>}
</Pressable>;<Switch>
Toggle switch component with React Native-compatible API.
const [isEnabled, setIsEnabled] = useState(false);
<Switch
value={isEnabled}
onValueChange={setIsEnabled}
trackColor={{ false: 'gray', true: 'green' }}
thumbColor="white"
/>;<ActivityIndicator>
Loading indicator (spinner) component.
<ActivityIndicator animating={true} color="cyan" size="large" />
<ActivityIndicator spinnerStyle="dots" label="Loading..." /><Focusable>
Component to make any content focusable with tab navigation.
<Focusable tabIndex={0} onFocus={() => console.log('Focused!')}>
<Text>Focusable content</Text>
</Focusable><Scrollable> / <ScrollView>
Scrollable container for overflow content.
<Scrollable maxHeight={20} maxWidth={80}>
<View>{/* Long content that will scroll */}</View>
</Scrollable>Props:
scrollTop/scrollLeft- Scroll positionmaxHeight/maxWidth- Maximum dimensionshorizontal- Scroll horizontally instead of verticallyshowsScrollIndicator- Show scroll indicators (planned)
<Modal>
Modal/overlay component for layered rendering (React Native-compatible).
const [visible, setVisible] = useState(false);
<Modal visible={visible} onRequestClose={() => setVisible(false)} transparent animationType="fade">
<View padding={2}>
<Text>Modal content</Text>
<Button onPress={() => setVisible(false)}>Close</Button>
</View>
</Modal>;Props:
visible- Show/hide modalonRequestClose- Called when back/escape pressedonShow- Called when modal showsonDismiss- Called when modal dismissestransparent- Transparent backgroundanimationType- Animation type:'none','slide','fade'backdrop- Show backdrop (terminal-specific)backdropColor- Backdrop colorzIndex- Z-index for layering
<FlatList>
Performant list rendering component (React Native-compatible).
<FlatList
data={items}
renderItem={({ item, index }) => (
<View>
<Text>{item.title}</Text>
</View>
)}
keyExtractor={(item) => item.id}
ListHeaderComponent={<Text bold>Header</Text>}
ListFooterComponent={<Text>Footer</Text>}
ListEmptyComponent={<Text>No items</Text>}
ItemSeparatorComponent={<View style={{ height: 1 }} />}
maxHeight={10}
onEndReached={() => loadMore()}
/><SectionList>
Sectioned list component (React Native-compatible).
<SectionList
sections={[
{ title: 'Section 1', data: ['Item 1', 'Item 2'] },
{ title: 'Section 2', data: ['Item 3', 'Item 4'] },
]}
renderItem={({ item }) => <Text>{item}</Text>}
renderSectionHeader={({ section }) => (
<Text bold>{section.title}</Text>
)}
keyExtractor={(item, index) => `${item}-${index}`}
/>
## Styling
### Colors
- **Named colors**: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`
- **Hex colors**: `#FF0000`
- **RGB colors**: `rgb(255, 0, 0)`
### Text Styles
- `bold` - Bold text
- `dim` - Dim text
- `italic` - Italic text
- `underline` - Underlined text
- `strikethrough` - Strikethrough text
- `inverse` - Inverse colors
### Background Colors
```tsx
<Text backgroundColor="blue" color="white">
White text on blue background
</Text>Responsive Sizing
React Console supports responsive sizing similar to CSS:
<View width="50%">50% of terminal width</View>
<View width="80vw">80% of viewport width</View>
<View width="80ch">80 characters wide</View>
<View height="50vh">50% of viewport height</View>The UI automatically adjusts when the terminal is resized, just like React in the browser!
Layout
Padding
<View padding={2}>Uniform padding</View>
<View padding={{ top: 1, right: 2, bottom: 1, left: 2 }}>
Custom padding
</View>Margin
<View margin={1}>Content with margin</View>
<View margin={{ top: 2, bottom: 2 }}>Vertical margin</View>Rendering Modes
Static Mode (Default)
One-time output, perfect for CLI commands:
render(<App />); // or render(<App />, { mode: 'static' });Interactive Mode
Full terminal application with input handling:
render(<App />, { mode: 'interactive' });Fullscreen Mode
Application takes over the entire terminal:
render(<App />, { mode: 'fullscreen' });React 19 Features
React Console supports all React 19 features:
use()hook - For reading resources (promises, context)useOptimistic()- For optimistic updatesuseActionState()- For form actionsuseFormStatus()- For form status- React Compiler - Automatic memoization and optimizations
- Concurrent Features - Support for concurrent rendering
- Actions - Form actions support
Event Handling
All components support JSX-style event handlers with React Native-compatible patterns:
<TextInput
onChangeText={(text) => {
console.log('Value:', text);
}}
onSubmitEditing={(event) => {
console.log('Submitted:', event.nativeEvent.text);
}}
onKeyDown={(event) => {
if (event.key.escape) {
// Handle Escape key
}
}}
/>
<Pressable
onPress={() => console.log('Pressed!')}
onPressIn={() => console.log('Press started')}
onPressOut={() => console.log('Press ended')}
onLongPress={() => console.log('Long pressed!')}
onHoverIn={() => console.log('Hover started')}
onHoverOut={() => console.log('Hover ended')}
/>
<Button
onClick={(event) => {
console.log('Clicked at:', event.x, event.y);
}}
onMouseDown={(event) => {
console.log('Mouse down:', event.button);
}}
/>Tab Navigation
Components automatically support tab navigation:
<TextInput tabIndex={0} autoFocus />
<Button tabIndex={1} />
<Button tabIndex={2} />Use Tab and Shift+Tab to navigate between focusable components. Tab indexes are automatically assigned if not specified.
Mouse Support
For terminals that support mouse events:
<Button
onClick={(event) => {
console.log('Clicked at:', event.x, event.y);
}}
onMouseMove={(event) => {
console.log('Mouse at:', event.x, event.y);
}}
/>Mouse tracking is automatically enabled in interactive and fullscreen modes.
React Native-Compatible APIs
Platform API
Detect the platform and select platform-specific values.
import { Platform } from '@baseline-operations/react-console';
console.log(Platform.OS); // 'terminal'
console.log(Platform.Version); // Node.js version
console.log(Platform.isMacOS); // true/false
console.log(Platform.isWindows); // true/false
const message = Platform.select({
terminal: 'Running in terminal',
default: 'Unknown platform',
});Dimensions API
Get terminal dimensions with resize events.
import { Dimensions, useWindowDimensions } from '@baseline-operations/react-console';
// Hook (recommended)
function MyComponent() {
const { width, height } = useWindowDimensions();
return (
<Text>
Terminal: {width}x{height}
</Text>
);
}
// API
const dims = Dimensions.get('window');
const subscription = Dimensions.addEventListener('change', ({ window }) => {
console.log('Resized:', window.width, window.height);
});Clipboard API
Copy and paste text using system clipboard.
import { Clipboard, useClipboard } from '@baseline-operations/react-console';
// Hook
const [content, setClipboard] = useClipboard();
// API
await Clipboard.setString('Hello!');
const text = await Clipboard.getString();
const hasContent = await Clipboard.hasString();AppState API
Monitor app lifecycle (active/background).
import { AppState, useAppState } from '@baseline-operations/react-console';
// Hook
const appState = useAppState(); // 'active' | 'background' | 'inactive'
// API
const subscription = AppState.addEventListener('change', (state) => {
console.log('App state:', state);
});BackHandler API
Handle escape key (like Android back button).
import { BackHandler, useBackHandler } from '@baseline-operations/react-console';
// Hook
useBackHandler(() => {
console.log('Escape pressed');
return true; // Return true to prevent default (exit)
});
// API
BackHandler.addEventListener('hardwareBackPress', () => {
return true; // Handled
});Alert API
Display modal alert dialogs.
import { Alert } from '@baseline-operations/react-console';
Alert.alert('Confirm', 'Are you sure?', [
{ text: 'Cancel', style: 'cancel' },
{ text: 'OK', onPress: () => console.log('Confirmed') },
]);Bell API
Terminal audio feedback (beeps).
import { Bell, useBell } from '@baseline-operations/react-console';
Bell.ring(); // Single beep
Bell.beep(3); // Multiple beeps
Bell.alert(); // Alert pattern
Bell.success(); // Success pattern
Bell.error(); // Error pattern
Bell.setEnabled(false); // MuteUtilities
Terminal Utilities
import {
getTerminalDimensions,
supportsColor,
Dimensions,
} from '@baseline-operations/react-console';
// Legacy API
const dims = getTerminalDimensions();
console.log(`Terminal: ${dims.columns}x${dims.rows}`);
// React Native-compatible API
const { width, height } = Dimensions.get('window');
if (supportsColor()) {
// Terminal supports colors
}Responsive Utilities
import { resolveWidth, resolveHeight } from '@baseline-operations/react-console';
const width = resolveWidth('50%', 100); // 50
const height = resolveHeight('80vh', 24); // 19 (80% of 24)Text Measurement
import { measureText, wrapText, truncateText } from '@baseline-operations/react-console';
const width = measureText('Hello'); // 5
const lines = wrapText('Long text...', 80);
const truncated = truncateText('Very long text...', 20);Examples
See the examples/ directory for complete examples:
Basic:
basic.tsx- Simple text outputflexbox.tsx- Flexbox layout examplesforms.tsx- Form handling with inputsresponsive.tsx- Responsive sizing and Dimensions APIstate-hooks.tsx- State management with hooksstylesheet.tsx- StyleSheet API usage
Components:
switch.tsx- Switch component demoflatlist.tsx- FlatList component demosectionlist.tsx- SectionList component demostate-styles.tsx- State-based styling patterns
APIs:
clipboard.tsx- Clipboard API demobell.tsx- Bell API demoplatform-dimensions.tsx- Platform and Dimensions APIsappstate-backhandler.tsx- AppState and BackHandler APIs
Refs:
textinput-ref.tsx- TextInput ref methodsscrollview-ref.tsx- ScrollView ref methodsflatlist-ref.tsx- FlatList ref methods
CLI:
cli/- CLI application examples
Run examples with:
npx tsx examples/basic.tsx
npx tsx examples/switch.tsx
npx tsx examples/clipboard.tsxDocumentation
- CLI Framework Guide - Building CLI applications
- Styling Guide - Styling components
- Layout Guide - Flexbox and grid layouts
- Event Handling Guide - Handling keyboard and mouse events
- Input Handling Guide - Working with input components
- Migration Guide - Migrating from other libraries
- API Reference - Full API documentation
License
MIT
