Getting Started
Run Ink (React for CLIs) in the browser using Xterm.js
ink-web
Run Ink (React for CLIs) in the browser using Xterm.js.
Features
- 🖥️ Renders Ink components inside Xterm.js in the browser
- 🚫 No Node.js required - uses minimal shims for Node-only modules
- 📦 Bundled and ready to use
Installation
npm install ink-web react xterm
# or
bun add ink-web react xtermUsage
There are two ways to use ink-web, depending on your needs:
Option A: Bundled Version (Recommended for Simplicity)
Best for: Most users who want less configuration and encapsulated dependencies.
Advantages:
- ✅ Minimal or no Vite plugin configuration (Vite auto-polyfills most node built-ins)
- ✅ Shims are largely encapsulated and won't interfere with other code
- ✅ Single import for both ink and the wrapper
Disadvantages:
- ❌ Larger bundle size (~1MB for ink + dependencies)
- ❌ If you use ink/chalk elsewhere, they get duplicated
- ❌ May require minimal Vite config for production builds
Installation:
npm install ink-web react xtermUsage:
import { InkTerminalBox, Box, Text } from 'ink-web/bundled'
import 'ink-web/bundled/css'
import 'xterm/css/xterm.css'
const App = () => (
<InkTerminalBox focus>
<Box flexDirection="column">
<Text color="green">Hello from Ink in the browser!</Text>
</Box>
</InkTerminalBox>
)Note for Production Builds: If you encounter build errors about unresolved modules, add this minimal config:
// vite.config.ts
export default defineConfig({
resolve: {
alias: {
stream: 'stream-browserify',
buffer: 'buffer',
process: 'process/browser',
events: 'events',
},
},
define: {
'process.env': {},
},
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis',
},
},
},
})Then install: npm install stream-browserify buffer process events
Option B: Plugin-Based (Smaller Bundle)
Best for: Advanced users who want smaller bundles or already use ink/chalk in their app.
Advantages:
- ✅ Smaller package bundle size (~5KB)
- ✅ No duplication if you use ink/chalk elsewhere
- ✅ More control over versions
Disadvantages:
- ❌ Requires Vite plugin configuration
- ❌ Shims are global to your entire build (may affect other code)
Installation:
npm install ink-web ink react xtermVite Configuration:
import { inkWebPlugin } from 'ink-web/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [react(), inkWebPlugin()],
})The plugin automatically:
- Sets up all required module aliases for browser shims
- Configures build targets for ES2020+ (needed for top-level await support)
- Defines global variables (
process.env,process.cwd)
Usage:
import { InkXterm } from 'ink-web'
import { Box, Text } from 'ink'
import 'xterm/css/xterm.css'
const App = () => (
<InkXterm focus>
<Box flexDirection="column">
<Text color="green">Hello from Ink!</Text>
</Box>
</InkXterm>
)Note: Import ink components from ink separately.
Examples
Imperative API
import { mountInkInXterm } from 'ink-web'
import { Box, Text } from 'ink'
import React from 'react'
import 'xterm/css/xterm.css'
const App = () => (
<Box flexDirection="column">
<Text color="green">Hello from Ink in the browser!</Text>
<Text>Type something...</Text>
</Box>
)
const container = document.getElementById('terminal')!
const { term, unmount } = mountInkInXterm(<App />, {
container,
focus: true
})
// Clean up when done
await unmount()React Component API
import { InkXterm } from 'ink-web'
import { Box, Text } from 'ink'
import React from 'react'
import 'xterm/css/xterm.css'
const App = () => (
<Box flexDirection="column">
<Text color="green">Hello from Ink!</Text>
</Box>
)
const MyTerminal = () => (
<InkXterm className="terminal-container" focus>
<App />
</InkXterm>
)API Reference
mountInkInXterm(element, options)
Mounts an Ink component into an Xterm.js terminal.
Parameters:
element: React.ReactElement- The Ink component to renderoptions: InkWebOptionscontainer: HTMLElement- The DOM element to mount the terminal infocus?: boolean- Whether to focus the terminal (default: true)termOptions?: ITerminalOptions- Xterm.js terminal options
Returns:
{
term: Terminal // The Xterm.js terminal instance
unmount: () => Promise<void> // Function to clean up and unmount
}<InkXterm>
React component wrapper for mounting Ink in Xterm.
Props:
children: React.ReactElement- The Ink component to renderclassName?: string- CSS class for the containerfocus?: boolean- Whether to focus the terminaltermOptions?: ITerminalOptions- Xterm.js terminal options
How It Works
This library provides browser-compatible shims for Node.js modules that Ink depends on (like process, stream, tty, etc.), allowing Ink to run in a browser environment.
The shims are minimal implementations that provide just enough functionality for Ink to render correctly in Xterm.js. When you configure Vite to alias Node.js modules to these shims, Ink can run without modification.
Compatibility
- React: 19.x+
- Ink: 6.x+
- Node.js: 18.0.0+
- Vite: 7.0.0+ (for plugin-based approach)
- Xterm.js: 5.0.0+