ブラウザでvimもHTOPも動く。そこに開発チームは驚きを感じないかもしれないが、テキスト選択・ページ内検索・スクリーンリーダーが普通に機能するブラウザターミナルとなると話は別です。

Vercel Labsが2026年4月に公開したオープンソースライブラリ「wterm」は、ターミナルのコアをZigで書いてWASMにコンパイルし、DOMレンダリングで表示する新しいアプローチを採っています。

この記事でわかること

  • 既存のウェブターミナルライブラリが抱えていた課題
  • wtermのアーキテクチャとパッケージ構成
  • ReactやVueへの組み込み手順
  • WebSocket経由でSSHに接続する方法
  • 軽量コアとlibghosttyの使い分け

ブラウザターミナルはcanvas描画が当たり前だった

Webアプリにターミナルを埋め込む場合、長らくxterm.jsが選ばれてきました。xterm.jsはcanvasにターミナル画面を描画するため描画性能は高いものの、ブラウザのネイティブ機能がほぼ使えません。Ctrl+Fで行うページ内検索、マウスドラッグによるテキスト選択、スクリーンリーダーによる読み上げ——これらはすべてcanvas上では機能しないか、独自実装が必要になります。

wtermはDOMにターミナルをレンダリングすることでこの問題を解消します。各セルをDOM要素として描画するため、テキスト選択・クリップボード操作・ブラウザのページ内検索・スクリーンリーダー対応がそのまま使えます。

wtermの概要

https://github.com/vercel-labs/wterm

Vercel Labsが開発したウェブ向けターミナルエミュレータです。VT100/VT220のエスケープシーケンスをパースするコアをZigで実装しWASMにコンパイルしており、リリースビルドでのバイナリサイズは約12KBにとどまります。Apache-2.0ライセンスで公開されており、2026年4月14日のリリース以来GitHubスターは2,800を超えました(2026年5月時点)。

パッケージ構成

wtermはpnpmモノレポで管理されており、用途に合わせたパッケージが分かれています。

パッケージ 内容
@wterm/core Headless WASMブリッジ、WebSocketトランスポート
@wterm/dom DOMレンダラー、バニラJS向けターミナル
@wterm/react ReactコンポーネントとuseTerminalフック
@wterm/vue Vue 3コンポーネント(v0.2.0で追加)
@wterm/ghostty libghosttyによるフルVT準拠コア(約400KB)
@wterm/just-bash ブラウザ内Bashシェル
@wterm/markdown ターミナル内Markdownレンダリング

コアは軽量な@wterm/core(約12KB)と、Ghosttyで採用されているlibghosttyベースの@wterm/ghostty(約400KB)の2系統から選べます。標準用途には前者で十分で、完全なVT準拠が必要な場面では後者に切り替えます。

主な機能

dirty-rowトラッキングによって変更のあった行だけをrequestAnimationFrameでDOMに反映するため、全行再描画のコストを避けています。vim、less、htopのような代替画面バッファを使うアプリも動作します。24ビットカラー(フルRGB SGR)にも対応しており、カラースキームを損なわずにターミナルアプリを表示できます。

テーマはCSSカスタムプロパティで定義されており、Default・Solarized Dark・Monokai・Lightが標準で用意されています。theme propに文字列を渡すだけで切り替えられます。

リサイズはResizeObserverベースの自動リサイズに対応しており、コンテナサイズに合わせてカラム数・行数が追従します。

Reactへの組み込み

最小構成ではパッケージを2つインストールしてコンポーネントを置くだけです。

npm install @wterm/dom @wterm/react
import { Terminal } from "@wterm/react";
import "@wterm/react/css";

function App() {
  return <Terminal />;
}

onDataを省略した場合、入力はそのままターミナルにエコーバックされます。バックエンドのPTYに接続する場合はuseTerminalフックを使います。

import { Terminal, useTerminal } from "@wterm/react";
import "@wterm/react/css";

function App() {
  const { ref, write } = useTerminal();

  return (
    <Terminal
      ref={ref}
      onData={(data) => {
        socket.send(data);
      }}
    />
  );
}

socket.sendでユーザーの入力をPTYバックエンドに転送し、サーバー側のレスポンスをwriteで端末に書き込む構成です。このWebSocketトランスポートがリモートSSH接続の基盤になります。

主要なpropsは以下の通りです。

Prop デフォルト 説明
cols number 80 初期カラム数
rows number 24 初期行数
theme string テーマ名("monokai", "solarized-dark", "light"
autoResize boolean false コンテナに合わせた自動リサイズ
cursorBlink boolean false カーソルブリンクの有効化
wasmUrl string WASMバイナリを別配信する場合のURL

Vue 3での使い方

Vue 3向けのパッケージ@wterm/vueはv0.2.0(2026年4月26日)で追加されました。

npm install @wterm/dom @wterm/vue
<script setup>
import { Terminal } from "@wterm/vue";
</script>

<template>
  <Terminal theme="monokai" :auto-resize="true" />
</template>

ReactとVue、どちらでも同じ@wterm/coreのWASMブリッジを使っており、動作の一貫性が保たれています。

既存ライブラリとの違い

xterm.jsとの最大の差異はレンダリング方式です。canvasとDOMでは、ブラウザが提供する機能へのアクセス方法が根本的に違います。

バイナリサイズについても差があります。xterm.jsは本体だけで数百KBになるのに対し、wtermの標準コアは12KBです。@wterm/ghosttyを選んでも400KBで、CDNからの読み込みを考えるとwtermの軽量性は明確なメリットです。

一方でwtermはまだv0.2.1であり、エコシステムの成熟度ではxterm.jsが上回ります。プラグインや拡張が豊富な既存エコシステムが必要な場合は選択肢の一つとして残ります。

ユースケース

クラウドIDEや管理コンソールへのターミナル組み込みが直接的な用途です。WebSocketトランスポートの設計がリモートPTYバックエンドとの接続を前提にしており、ホスティング型開発環境や管理画面のコンテナ操作UIに向いています。

@wterm/just-bashを使えばバックエンドなしのブラウザ内Bashシェルも構築でき、インタラクティブなドキュメントやチュートリアルサイトでターミナル操作を実演する場面にも使えます。