wterm: Fast Web‑Based Terminal Emulator Powered by Zig & WASM

Introduction

If you’ve ever needed a terminal inside a web application—whether for a cloud IDE, a remote‑shell dashboard, or an interactive tutorial—wterm is the tool you’ve been waiting for. Developed by Vercel Labs, wterm is a fully functional terminal emulator that runs in the browser using Zig‑compiled WebAssembly. Its small footprint (≈12 KB for the core WASM binary) and native‑like performance make it ideal for any modern web stack.


Why wterm Stands Out

Feature Benefit
Zig + WASM core Near‑native parsing speed for VT100/VT220/xterm escape sequences.
Pluggable cores Choose the ultra‑lightweight Zig core (~12 KB) or the full‑featured libghostty backend (~400 KB) for complete VT compliance.
DOM rendering Native text selection, copy/paste, browser find, and screen‑reader support without extra libraries.
Framework wrappers Ready‑to‑use packages for React, Vue 3, and a vanilla JS API.
Theming CSS custom properties let you switch between Default, Solarized Dark, Monokai, and Light themes instantly.
WebSocket transport Connect to a remote PTY backend with binary framing and automatic reconnection.
Auto‑resize Uses ResizeObserver to adapt to container size changes.
Scrollback & alternate screen Full support for applications like vim, less, and htop.

These capabilities make wterm more than a demo—it’s a production‑ready component you can embed in any web project.


Getting Started

Prerequisites

  • Zig 0.16.0+ (for building the WASM core)
  • Node.js 20+
  • pnpm 10+ (recommended package manager)

Installation

# Clone the repository
git clone https://github.com/vercel-labs/wterm.git
cd wterm

# Install JavaScript dependencies
pnpm install

Build the WASM Binary

zig build               # Debug build (default)
zig build -Doptimize=ReleaseSmall   # Production‑size build (~12 KB)
The resulting wterm.wasm file lives in the web/ directory and can be served as a static asset.

Build All Packages

pnpm run build   # Compiles @wterm/* packages and bundles examples

Using wterm in Different Environments

1. Vanilla JavaScript (DOM Renderer)

<script type="module">
  import { createTerminal } from "https://cdn.jsdelivr.net/npm/@wterm/dom";
  const term = createTerminal({ wasmUrl: "/wterm.wasm" });
  term.attach(document.getElementById('terminal'));
</script>
<div id="terminal" style="height:400px; width:100%;"></div>
The DOM renderer gives you a plain‑JS terminal with full clipboard support and native text selection.

2. React Integration

import { WTermReact } from "@wterm/react";

export default function App() {
  return (
    <WTermReact
      wasmUrl="/wterm.wasm"
      className="my-terminal"
      onData={data => console.log('User typed:', data)}
    />
  );
}
The useTerminal hook lets you interact with the underlying PTY, send commands, and listen for output.

3. Vue 3 Component

<template>
  <WTermVue :wasm-url="'/wterm.wasm'" />
</template>
<script setup>
import { WTermVue } from '@wterm/vue';
</script>
Vue developers get a simple component that respects the reactivity system and can be placed anywhere in the component tree.


Advanced Features

Theming with CSS Variables

:root {
  --wterm-bg: #1e1e1e;          /* Background */
  --wterm-fg: #d4d4d4;          /* Foreground */
  --wterm-cursor: #ffcc00;     /* Cursor */
}
Switch themes at runtime by updating these variables; the terminal updates instantly without a reload.

Connecting to a Remote PTY via WebSocket

import { createTerminal } from '@wterm/core';
const term = createTerminal({
  wasmUrl: '/wterm.wasm',
  transport: new WebSocketTransport('wss://my-pty-server.example.com')
});
term.attach(document.body);
The built‑in transport handles binary framing, reconnection, and flow control, making it perfect for cloud‑based development environments.

Using the libghostty Backend

If you need full VT100/VT220 compliance (e.g., for complex applications like tmux), install the @wterm/ghostty package:

pnpm add @wterm/ghostty
Then pass the ghostty core when creating the terminal:
import { createTerminal } from '@wterm/core';
import { GhosttyCore } from '@wterm/ghostty';

const term = createTerminal({
  core: new GhosttyCore(),
  wasmUrl: '/wterm.wasm'
});
The additional 400 KB payload brings full VT compliance, including 24‑bit color and advanced line‑drawing.


Real‑World Use Cases

  1. Cloud IDEs – Embed wterm as the built‑in shell for platforms like Gitpod, CodeSandbox, or custom SaaS IDEs.
  2. Documentation & Tutorials – Render live command‑line demos directly in docs without requiring a local terminal.
  3. Monitoring Dashboards – Show real‑time logs or interactive CLI tools inside admin panels.
  4. Education Platforms – Provide students with a sandboxed shell for learning Linux commands.

Contributing & Community

wterm is open source under the Apache‑2.0 license. Contributions are welcome: - Bug reports – Open an issue on GitHub. - Feature requests – Propose enhancements via pull requests. - Documentation – Improve the README, add examples, or write tutorials.

The project uses Playwright for end‑to‑end tests (e2e folder) and Vitest for unit testing across all packages. Running the test suite is as simple as:

pnpm test


Conclusion

wterm bridges the gap between native terminal experiences and modern web applications. Its Zig‑based WASM core delivers speed, while the ecosystem of packages (vanilla, React, Vue, Ghostty) ensures seamless integration regardless of your stack. Whether you’re building a cloud IDE, an interactive tutorial, or a remote‑shell dashboard, wterm gives you a reliable, extensible, and open‑source solution.

Ready to try it? Clone the repo, build the WASM binary, and embed the terminal in minutes. Join the growing community of developers who are bringing the power of the command line to the browser.

Original Article: View Original

Share this article