Build beautiful terminal applications with Svelte 5
SvelTUI is a terminal UI framework that brings Svelte's elegant reactive programming model to the command line. Write declarative components, enjoy instant reactivity, and build responsive terminal interfaces with the same developer experience you love from web development.
<script>
import { Box, Text, keyboard } from 'sveltui'
let count = $state(0)
keyboard.onKey('Space', () => count++)
</script>
<Box border="rounded" borderColor={0x06} padding={1}>
<Text text="Press Space!" color={0x0a} />
<Text text={`Count: ${count}`} color={0x0b} />
</Box>
Unlike traditional terminal UI libraries that redraw at fixed intervals, SvelTUI only updates what actually changed, when it changes. Press a key and see the result instantly - no frame delay, no flickering, no wasted CPU cycles.
Built from the ground up for Svelte 5's new reactivity system. Use $state, $derived, and $effect naturally - they just work in the terminal.
Full CSS flexbox support via Facebook's Yoga layout engine. Finally, sane layouts in the terminal.
Differential rendering updates only the cells that changed. Your UI stays rock solid.
Box, Text, and more coming soon# Using the CLI (recommended)
bunx sv-tui create my-app
cd my-app
bun install
bun run build
bun run start
mkdir my-app && cd my-app
bun init -y
bun add sveltui svelte
Create src/main.ts:
import { mount } from 'sveltui'
import { mount as mountComponent } from 'svelte'
import App from './App.svelte'
mount(() => {
mountComponent(App, { target: document.body })
}, { fullscreen: true })
Create src/App.svelte:
<script>
import { Box, Text } from 'sveltui'
</script>
<Box x={2} y={2} width={40} height={5} border="rounded" borderColor={0x06}>
<Text x={2} y={1} text="Hello, Terminal!" color={0x0a} />
</Box>
Container component with flexbox layout, borders, and background colors.
<Box
border="rounded"
borderColor={0x06}
backgroundColor={0x000033}
width={40}
height={10}
padding={1}
>
<Text text="Content inside a box" />
</Box>
Props:
| Prop | Type | Description |
|---|---|---|
border |
'none' | 'single' | 'double' | 'rounded' | 'bold' | 'dashed' | 'dotted' |
Border style |
borderColor |
number | string |
Border color (hex number or CSS color) |
backgroundColor |
number | string |
Background color |
x, y |
number |
Position (when using absolute positioning) |
width, height |
number | string |
Dimensions (number for chars, string for percentage) |
padding |
number |
Inner padding |
focusable |
boolean |
Whether the box can receive focus |
onfocus, onblur |
() => void |
Focus event callbacks |
Renders styled text content.
<Text
text="Hello World"
color={0x00ff00}
bold
/>
Props:
| Prop | Type | Description |
|---|---|---|
text |
string |
The text content to display |
color |
number | string |
Text color |
bold |
boolean |
Bold text |
italic |
boolean |
Italic text |
underline |
boolean |
Underlined text |
x, y |
number |
Position |
SvelTUI provides a hybrid keyboard API - reactive state for UI display, imperative callbacks for event handling.
<script>
import { keyboard } from 'sveltui'
</script>
<!-- Automatically updates when any key is pressed -->
<Text text={`Last key: ${keyboard.lastKey}`} />
<script>
import { keyboard } from 'sveltui'
import { onDestroy } from 'svelte'
// Single key
const unsub = keyboard.onKey('Enter', () => {
console.log('Enter pressed!')
})
// Multiple keys
const unsub2 = keyboard.onKey(['ArrowUp', 'k'], () => {
scrollUp()
})
// All keys
const unsub3 = keyboard.on((event) => {
console.log(event.key, event.ctrlKey, event.shiftKey)
})
onDestroy(() => {
unsub()
unsub2()
unsub3()
})
</script>
<script>
import { keyboard } from 'sveltui'
// Built-in: Tab and Shift+Tab cycle focus automatically
// Programmatic control
keyboard.focusNext()
keyboard.focusPrevious()
keyboard.clearFocus()
</script>
ArrowUp, ArrowDown, ArrowLeft, ArrowRightHome, End, PageUp, PageDownEnter, Escape, Tab, Shift+Tab, Backspace, DeleteCtrl+A through Ctrl+Z, Ctrl+C (exits app)a, A, 1, !, etc.)import { mount } from 'sveltui'
mount(() => {
// Your app initialization
}, {
fullscreen: true // Use alternate screen buffer (recommended)
})
SvelTUI's architecture is designed for performance and simplicity:
┌─────────────────────────────────────────────────────────┐
│ Your Svelte App │
│ (Components, State, Event Handlers) │
└─────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────┐
│ Svelte 5 Reactivity │
│ ($state, $derived, $effect) │
└─────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────┐
│ SvelTUI Engine │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Typed Array │ │ Yoga │ │ Differential │ │
│ │ State │ │ Layout │ │ Renderer │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────┬───────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────┐
│ Terminal │
│ (ANSI escape sequences) │
└─────────────────────────────────────────────────────────┘
Reactive-on-demand Rendering: No fixed FPS loop. Updates happen instantly when state changes, and nothing happens when state is stable.
Typed Arrays for State: Component properties stored in typed arrays for cache-friendly access and minimal memory overhead.
Differential Rendering: Only changed cells are written to the terminal. Press a key, and only the affected characters update.
Happy DOM Integration: Svelte needs a DOM to work. We use Happy DOM as a lightweight shim, letting Svelte's reactivity work unchanged.
SvelTUI supports multiple color formats:
<!-- Hex number (recommended for performance) -->
<Text text="Red" color={0xff0000} />
<!-- CSS color names -->
<Text text="Blue" color="blue" />
<!-- Hex string -->
<Text text="Green" color="#00ff00" />
<!-- RGB -->
<Text text="Yellow" color="rgb(255, 255, 0)" />
SvelTUI includes a powerful theming system with semantic colors. Use the variant prop on components to automatically apply theme colors:
<!-- Box variants affect border color -->
<Box variant="primary" border="rounded">
<Text text="Primary box" />
</Box>
<Box variant="success" border="single">
<Text text="Success!" />
</Box>
<!-- Text variants affect text color -->
<Text text="Warning message" variant="warning" />
<Text text="Error occurred" variant="danger" />
<Text text="Information" variant="info" />
<!-- Text also has muted/bright options -->
<Text text="Subtle hint" muted />
<Text text="Important!" bright />
Available variants:
| Variant | Use Case |
|---|---|
primary |
Main actions, focused elements |
secondary |
Secondary actions, less emphasis |
success |
Positive feedback, confirmations |
warning |
Caution, important notices |
danger |
Errors, destructive actions |
info |
Informational messages |
<script>
import { getTheme } from 'sveltui'
const theme = getTheme()
// Switch to a different theme
theme().setTheme('dracula')
</script>
Available themes: default, dracula, nord, monokai, solarized
<script>
import { getTheme } from 'sveltui'
const theme = getTheme()
// Access theme colors for custom use
const primaryColor = theme().colors.primary
const bgColor = theme().colors.background
</script>
<Text text="Custom styled" color={theme().colors.accent} />
# Clone the repository
git clone https://github.com/user/sveltui.git
cd sveltui
# Install dependencies
bun install
# Build the framework
bun run build
# Run the demo
bun run demo
Contributions are welcome! Whether it's:
Please feel free to open an issue or submit a pull request.
SvelTUI stands on the shoulders of giants:
MIT License - see LICENSE for details.
Built with Svelte 5 and a lot of terminal escape sequences