DEV Community

Rodrigo Luglio
Rodrigo Luglio

Posted on

Building Terminal UIs with Svelte 5: Introducing SvelTUI

What if building terminal applications felt like building web applications?

That question led me to create SvelTUI - a terminal UI framework that brings Svelte 5's elegant reactive programming model to the command line.

The Problem with Traditional Terminal UIs

If you've ever built a terminal UI, you know the pain:

  • Imperative spaghetti: Manually tracking cursor positions, clearing regions, redrawing elements
  • Flickering: Full screen redraws cause visible flashing
  • Layout nightmares: Calculating x/y coordinates for every element
  • State management: Keeping UI in sync with data requires careful orchestration

Web development solved these problems years ago with declarative components, reactive state, and flexbox. Why can't terminals have the same?

Enter SvelTUI

Here's a complete, interactive counter application:

<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>
Enter fullscreen mode Exit fullscreen mode

That's it. When you press Space, count increments, and the display updates instantly. No manual redrawing. No flicker. Just reactive state doing its thing.

How It Works

SvelTUI's architecture is unconventional but elegant:

1. Svelte in Happy DOM

Svelte needs a DOM to work. Instead of fighting this, we embrace it. Happy DOM provides a lightweight DOM implementation. Svelte doesn't know it's not in a browser.

2. Yoga for Layouts

Yoga is Facebook's cross-platform flexbox implementation (used in React Native). It calculates layouts based on flexbox rules:

<Box
  flexDirection="row"
  justifyContent="space-between"
  alignItems="center"
  padding={2}
  gap={1}
>
  <Text text="Left" />
  <Text text="Center" />
  <Text text="Right" />
</Box>
Enter fullscreen mode Exit fullscreen mode

Real flexbox. In a terminal. Finally.

3. Typed Array Buffers

Component state lives in typed arrays - one for characters, one for foreground colors, one for background colors, one for style flags. This is cache-friendly and enables efficient comparison.

4. Differential Rendering

Every render, we compare the new buffer to the previous one. Only cells that actually changed get written to the terminal. This eliminates flicker completely.

5. Reactive On-Demand

There's no setInterval running at 60fps. Updates happen synchronously when reactive state changes. When nothing changes, nothing happens - zero CPU usage.

Features

Components

  • Box - Container with flexbox layout, borders, backgrounds
  • Text - Styled text with colors and formatting

Layout (via Yoga)

  • flexDirection, justifyContent, alignItems
  • padding, margin, gap
  • Percentage and fixed dimensions
  • flexGrow for fluid layouts

Keyboard API

<script>
  import { keyboard } from 'sveltui'

  // Reactive - use in templates
  keyboard.lastKey

  // Imperative - use for actions
  keyboard.onKey('Enter', () => submit())
  keyboard.onKey(['ArrowUp', 'k'], () => scrollUp())
</script>
Enter fullscreen mode Exit fullscreen mode

Theming
Built-in themes: default, dracula, nord, monokai, solarized

<script>
  import { getTheme } from 'sveltui'
  const theme = getTheme()
  theme().setTheme('dracula')
</script>
Enter fullscreen mode Exit fullscreen mode

Semantic Variants

<Box variant="success" border="rounded">
  <Text text="Operation completed" variant="success" />
</Box>
Enter fullscreen mode Exit fullscreen mode

Getting Started

# Create a new project
bunx @rlabs-inc/sveltui create my-app

# Choose a template (minimal, counter, or dashboard)
cd my-app
bun install
bun run dev
Enter fullscreen mode Exit fullscreen mode

The dashboard template showcases layouts, live data, scrolling, and theming.

The Svelte 5 Advantage

Why Svelte 5 specifically?

Runes are perfect for this. $state creates fine-grained reactive signals. When count changes in our counter example, Svelte knows exactly what depends on it and updates only that.

No virtual DOM overhead. Svelte compiles reactivity away. The runtime is minimal. Combined with our differential rendering, updates are essentially free.

Familiar patterns. If you know Svelte, you know SvelTUI. Same components, same reactivity, same lifecycle.

Current Status

SvelTUI is early stage but functional. The architecture is solid, the core features work well.

What's working:

  • Box and Text components
  • Full flexbox layout
  • Keyboard handling
  • Focus and scroll management
  • Theming system
  • True 24-bit color

What's planned:

  • Input component
  • List/Table components
  • Progress bars
  • Animation primitives
  • More themes

Try It Out

bunx @rlabs-inc/sveltui create my-app --template dashboard
Enter fullscreen mode Exit fullscreen mode

GitHub: github.com/RLabs-Inc/sveltui
npm: @rlabs-inc/sveltui

I'd love to hear what you think. What would you build with this? What components do you need? Drop a comment or open an issue on GitHub!


SvelTUI is MIT licensed and open source. Contributions welcome!

Top comments (0)