OpenTUI は、ストリーミングされるエージェント出力、ログビューア、ライブダッシュボードのように継続的に再描画されるターミナル UI において、Ink の実質的な代替となる。しかし、その最先端のランタイム要件と 0.x リリースの激しい変動により、日常的な Node ユーザーに向けて公開する CLI では Ink のほうが安全なデフォルトであり続ける。
自作の Ink ダッシュボードが慌ただしいログストリームの下でカクついるのを見たことがあるなら、なぜ人々が他の選択肢を探しているのかはもうお分かりだろう。OpenTUI はまさにそのギャップを埋めるために作られた。
以下では、両ライブラリに同じ基準を当てはめていく。Ink のレンダラーの限界はどこか、OpenTUI のアーキテクチャは何を変えるのか、同じ小さな UI が両者でどう書けるのか、OpenTUI がプロダクションでどれだけの信頼を得ているのか、そして乗り換えにランタイム・エコシステム・安定性の面でどれだけのコストがかかるのか。最後に推奨案を示す。
要点
- OpenTUI は Zig で書かれたネイティブコアを通じてレンダリングを行い、TypeScript からは FFI 経由で到達する。レイアウトは Yoga flexbox で、React と Solid の両方のバインディングを備える。
- Ink はデフォルトで再描画を 30 fps に制限しており、これは
maxFpsレンダーオプションで設定できる。ネット上で流通している「32 FPS」という数字は、Ink のソースにある 32 ミリ秒のスロットル間隔の読み違えである。 - OpenTUI 製の CLI では、すべてのエンドユーザーが Bun 1.3+ か、実験的な
--experimental-ffiフラグ付きの Node.js 26.4+ を実行する必要がある。これは単なるローカルのセットアップ手順ではなく、配布上の制約である。 - OpenTUI は Solid レコンサイラを通じて、OpenCode のターミナルインターフェースをプロダクションでレンダリングしており、Go と Bubble Tea による実装を置き換えている。
- OpenTUI の 0.5.x 系は月に複数回リリースされるが、Ink の変化はそれよりはるかに緩やかだ。直近の破壊的リリースである Ink 7 は、要求下限を Node 22 と React 19.2 に引き上げただけで、コンポーネント API はそのまま維持された。また、Ink にはコミュニティ製コンポーネントのはるかに大きなエコシステムがある。
Ink のレンダラーの限界はどこにあるのか?
Ink の上限はレンダースロットルにある。デフォルトでは再描画を毎秒 30 フレームに制限しており、その予算を超える状態更新はすべて次のフレームを待つことになる。Ink の基礎については、以前のNode.js でターミナルインターフェースを構築するガイドを参照してほしい。あの記事は 2025 年 12 月時点で Ink を推奨したもので、当時 OpenTUI はまだ真剣な選択肢ではなかった。本記事はその結論のアップデートである。
このスロットルは俗説ではなく、ドキュメント化されたものだ。Ink は歴史的にレンダー関数の周囲に 32 ミリ秒のスロットルをハードコードしており、広く繰り返される「32 FPS 上限」という数字はここから来ている。32 はミリ秒単位の間隔であり、そこから導かれるレートは毎秒 30 フレームという上限である。現行の Ink はこれを maxFps レンダーオプション(デフォルト 30)として公開しており、併せて各再描画を変更のあった行だけに限定する incrementalRendering オプションもある。つまり上限は調整可能だ。調整できないのはアーキテクチャである。すべてのフレームは JavaScript で組み立てられ、文字列として差分が取られ、アプリケーションロジックを動かしているのと同じイベントループから stdout に書き込まれる。
スピナー、フォーム、プログレスバー程度であれば、これらは一切観測されない。観測されるのは出力がストリームするときだ。フレーム予算より速く届くモデルのトークン、忙しいサービスを tail するログビューア、広い領域を再レンダリングするダッシュボードである。メモリのフロアもある。Ink プロセスは、たかだか数行の出力のために Node ランタイムと React のレコンサイラを抱え込む。ただしそのオーバーヘッドについて信頼できる公開計測値は存在しないので、目にした具体的なメガバイト数値は疑ってかかるべきだ。
OpenTUI は何を追加するのか?
OpenTUI はレンダリングを JavaScript から完全に切り離す。コアは Zig で書かれ、スクリーンバッファ、描画、入力パースをネイティブに処理する。TypeScript からは FFI 経由で、Bun では bun:ffi、Node では実験的 FFI を通じて到達する。レイアウトは馴染みのあるままだ。サイズ指定と配置は Yoga ベースの flexbox を通り、Ink と同じエンジンなので、flexDirection、flexGrow などはそのまま移行できる。
レンダラー以外に重要な追加が 2 つある。ひとつは組み込みコンポーネントで、Ink がサードパーティパッケージに任せている領域をカバーする。フォーカス可能な Input と Textarea、Select、ScrollBox、Code における tree-sitter ベースのシンタックスハイライト、Diff ビュー、そして Markdown である。さらに 2 つ、テキストテーブルと埋め込みターミナルは Core の renderable としてのみ存在するため、React と Solid からは JSX 要素として到達できない。もうひとつはフレームワークの選択肢だ。@opentui/react と @opentui/solid はどちらもファーストクラスのバインディングであり、高頻度更新に対して細粒度リアクティビティを好むチームが React のレコンサイラに縛られることはない。Three.js の WebGPU 統合もあるが、この比較においては興味深い余談であり、しかも Bun 専用である。
同じ UI は Ink と OpenTUI でどう見えるのか?
移行コストは、同じ UI を 2 回作ってみるのが最も分かりやすい。枠線付きパネル、1 行のテキスト、状態を切り替えるキー、そしてきれいな終了処理だ。以下のスニペットは Ink 7 と OpenTUI 0.5.x を対象としている。
npx create-ink-app で足場を作った Ink:
import React, { useState } from "react";
import { render, Box, Text, useApp, useInput } from "ink";
function App() {
const [name, setName] = useState("world");
const { exit } = useApp();
useInput((input, key) => {
if (key.escape) exit();
if (input === "r") {
setName((prev) => (prev === "world" ? "terminal" : "world"));
}
});
return (
<Box borderStyle="round" padding={1} flexDirection="column">
<Text>Hello, {name}! Press r to toggle, Esc to quit.</Text>
</Box>
);
}
render(<App />);
bun create tui --template react で足場を作った OpenTUI:
import { useState } from "react";
import { createCliRenderer } from "@opentui/core";
import { createRoot, useKeyboard, useRenderer } from "@opentui/react";
function App() {
const [name, setName] = useState("world");
const renderer = useRenderer();
useKeyboard((key) => {
if (key.name === "escape") renderer.destroy();
if (key.name === "r") {
setName((prev) => (prev === "world" ? "terminal" : "world"));
}
});
return (
<box style={{ border: true, padding: 1, flexDirection: "column" }}>
<text>Hello, {name}! Press r to toggle, Esc to quit.</text>
</box>
);
}
const renderer = await createCliRenderer();
createRoot(renderer).render(<App />);
この差分こそが移行ガイドである。エントリポイントは Ink の render() 呼び出しから、@opentui/core の createCliRenderer() に加えて @opentui/react の createRoot(renderer).render() へと変わる。大文字始まりの Box と Text コンポーネントは小文字の box と text の intrinsic 要素になり、2 語以上からなる要素名は <ascii-font> のようにハイフンで繋ぐ。Ink の useInput と useApp は OpenTUI の useKeyboard と renderer.destroy() に対応する。React 自体はそのまま引き継がれる。ink と @opentui/react はどちらも React 19.2 以降を必要とし、useState の挙動は同一だ。差が大きいのはフォーカスモデルである。Ink は Tab 循環を組み込んだ useFocus を提供するが、OpenTUI は自分で状態管理する focused プロパティを通じてフォーカスを与える。
実績:OpenTUI はプロダクションで OpenCode をレンダリングしている
OpenTUI はデモプロジェクトではない。OpenCode の開発元である Anomaly が構築したもので、プロジェクトの README は OpenCode を数百万人にサービスを提供するプロダクションデプロイとして挙げている。コーディングエージェントがモデル出力、差分、シンタックスハイライトされたコードをインタラクティブなターミナルにストリームするというこのワークロードは、まさに Ink のスロットルが露呈する領域だ。その系譜も重要である。OpenCode のインターフェースは Go と Bubble Tea から OpenTUI へ書き換えられた。React ユーザーにとっての注意点が 1 つある。OpenCode の TUI は Solid レコンサイラ上で動作するため、プロダクションでの実戦検証はコアと Solid バインディングを主にカバーしており、@opentui/react はその対象ではない。しかも @opentui/react は、Core や Solid と異なり、CI に Node.js のレーンを持っていない。
コスト:変動の速さ、ランタイム、エコシステム
コストは 3 か所に集中しており、ランタイムに関するものは開発者体験の問題ではなく配布の問題である。
| 評価項目 | Ink 7 | OpenTUI 0.5.x |
|---|---|---|
| ランタイム | Node 22+ | Bun 1.3+ または --experimental-ffi 付きの Node 26.4+、ESM のみ |
| レンダリング | JavaScript、maxFps によりデフォルト 30 fps | FFI 経由のネイティブ Zig コア |
| レイアウト | Yoga flexbox | Yoga flexbox |
| 組み込み | Box、Text、Static、入力系はコミュニティパッケージ経由 | Input、Select、ScrollBox、Code、Diff、Markdown ほか |
| フレームワーク | React | React と Solid |
| 成熟度 | メジャー更新は数年おき。Ink 7 が壊したのはランタイム要求下限とキーイベントのみ | 0.x 系で月に複数回リリース |
Ink 製の CLI は Node 22 以降が動く場所ならどこでも動く。OpenTUI 製の CLI は、すべてのエンドユーザーに最先端の要件を課す。ランタイムサポートマトリクスによれば、Bun 1.3.0+ または実験的 FFI フラグ付きの Node.js 26.4.0+ が必要で、ESM のみ対応、CommonJS の require は完全に失敗する。npm に公開して見知らぬ人にインストールされるツールでは、これは対象ユーザーを縮小させるか、コンパイル済みバイナリでの配布に追い込むことになる。
2 つ目のコストは安定性だ。リリースページを見ると、v0.4.4 から v0.5.8 までがおよそ 6 週間で出ている。0.x 系でこのペースということは、バージョン固定と changelog の監視が必要になるということだ。対照的に Ink の API は、複数年・複数のメジャーバージョンにわたって安定している。3 つ目はエコシステムである。Ink のコミュニティパッケージ、レシピ、Stack Overflow の回答に相当するものは OpenTUI にはまだない。ただし OpenTUI の充実した組み込みコンポーネントがその差の一部を埋めている。デバッグはほぼ互角だ。どちらも DEV=true で React DevTools をサポートし、OpenTUI はさらにコンソールオーバーレイとレンダリング診断を追加している。
Ink から OpenTUI に乗り換えるべきか?
TUI が継続的に再描画され、かつランタイムを自分で制御できるなら、今すぐ乗り換えるべきだ。社内向けのエージェントフロントエンド、自チーム用のログビューア、Bun 要件がビルド工程に吸収されるコンパイル済みバイナリとして配布されるものなどである。そうしたケースでは Zig コア、Code と Diff コンポーネント、Solid という選択肢は本物の優位性であり、OpenCode がそのアーキテクチャを大規模に実証している。
一般的な Node ユーザー向けに npm へ CLI を公開する場合、UI が継続的なストリームではなくフォーム・プロンプト・進捗表示中心の場合、あるいはマイナーバージョン間の破壊的変更を吸収できない場合は、Ink に留まるべきだ。Ink のデフォルト 30 fps は maxFps で調整可能であり、そのプロダクション採用実績(Claude Code、Gemini CLI、GitHub Copilot CLI、Wrangler、Prisma など)は、スロットルされたモデルがどこまで通用するかを示している。TypeScript を離れる覚悟のあるチームには Bubble Tea と Ratatui という選択肢も残るが、それでは本記事の前提が崩れてしまう。
まとめ
OpenTUI は、アーキテクチャとプロダクションでの実績によって「本当の代替」という称号を得ている。そして Ink は、安定性とリーチによってデフォルトの座を守っている。決め手となる問いは、どちらのレンダラーが速いかではない。あなたのユーザーがあなたのランタイムを実行できるかどうかだ。最も負荷の高い画面を bun create tui --template react でプロトタイプし、実際のストリーム下で動かして観察し、ベンチマークではなくランタイム制約に判断を委ねよう。
FAQ
OpenTUI は Bun 専用なのか、Node.js でも動くのか?
いいえ、OpenTUI は Bun 専用ではありません。Bun 1.3.0 以降で動作し、Node.js 26.4.0 以降でも動作します。ただしアプリが ESM であり、実験的 FFI フラグ付きで Node を起動する必要があります。CommonJS の require で Core を読み込むと例外が投げられます。@opentui/three やランタイムで読み込むプラグインなど、一部はいまだ Bun 専用であり、Node の FFI サポート自体も実験的なため、より十分にテストされた経路としては Bun が依然として有利です。
OpenTUI は Windows で動くのか?
はい。ネイティブコアのビルド済みパッケージが Windows x64 と Windows arm64 向けに提供されており、macOS 向け、および Linux 向けの glibc と musl の両ビルドもあります。Windows についてはプロジェクト自身のテストが x64 上の Bun を通して行われており、Node.js の受け入れレーンは Linux x64 に置かれています。そのため、特にユーザーが Bun ではなく Node を使っている場合は、出荷前に実際の Windows ターミナルで Windows リリースを試してください。
OpenTUI は React DevTools をサポートしているか?
はい。ネット上には否定する主張もありますが、サポートしています。@opentui/react のドキュメントには、react-devtools-core@7 を dev 依存としてインストールし、npx react-devtools@7 を実行し、DEV=true でアプリを起動してコンポーネントツリーを検査する手順が記載されています。Ink も同じく DEV=true を通じて React DevTools をサポートしているため、デバッグツールは両ライブラリの間で意味のある差別化要因にはなりません。
OpenTUI の React バインディングと Solid バインディングのどちらを使うべきか?
最もプロダクションで検証された経路を選ぶなら Solid です。OpenCode のターミナルインターフェースは Solid レコンサイラ上で動作しており、@opentui/solid には @opentui/react にはない Node.js の CI カバレッジがあります。チームがすでに React で開発しているなら React を選んでください。このバインディングは React 19.2.0 以降を必要とし、useKeyboard や useTimeline といったフックを備えています。なお @opentui/solid は Solid のバージョンを 1.9.12 に厳密に固定していることに注意してください。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k