12k
All articles

¿Es OpenTUI una alternativa real a Ink?

OpenTUI vs Ink: compara rendimiento, límites de runtime, componentes integrados y coste de migración para interfaces terminal y CLI en streaming.

OpenReplay Team
OpenReplay Team
¿Es OpenTUI una alternativa real a Ink?

OpenTUI es una alternativa real a Ink para interfaces de terminal que se redibujan continuamente, como la salida en streaming de agentes, los visores de logs y los dashboards en vivo, pero sus requisitos de runtime de última generación y la rotación de versiones 0.x hacen de Ink la opción predeterminada más segura para una CLI que publiques para usuarios cotidianos de Node.

Si alguna vez has visto tu propio dashboard de Ink entrecortarse bajo un flujo intenso de logs, ya sabes por qué la gente está mirando otras opciones. Esa brecha es la que OpenTUI se construyó para cerrar.

Lo que sigue aplica los mismos criterios a ambas bibliotecas: dónde llega al límite el renderizador de Ink, qué cambia la arquitectura de OpenTUI, cómo se ve la misma pequeña UI en cada una, qué crédito en producción ha ganado OpenTUI y qué cuesta un cambio en términos de runtime, ecosistema y estabilidad. Termina con una recomendación.

Puntos clave

  • OpenTUI renderiza a través de un núcleo nativo escrito en Zig, alcanzado desde TypeScript mediante FFI, con layout flexbox de Yoga y bindings tanto para React como para Solid.
  • Ink limita los redibujados a 30 fps por defecto, configurable mediante la opción de render maxFps; la cifra de “32 FPS” que circula en internet es una mala lectura del intervalo de throttle de 32 milisegundos en el código fuente de Ink.
  • Una CLI con OpenTUI exige que cada usuario final ejecute Bun 1.3+ o Node.js 26.4+ con el flag experimental --experimental-ffi, lo cual es una restricción de distribución, no solo un paso de configuración local.
  • OpenTUI renderiza la interfaz de terminal de OpenCode en producción a través de su reconciliador de Solid, reemplazando una implementación en Go y Bubble Tea.
  • La línea 0.5.x de OpenTUI publica varias releases por mes, mientras que Ink cambia mucho más lentamente: su última release con cambios incompatibles, Ink 7, elevó el mínimo a Node 22 y React 19.2 y dejó intacta la API de componentes. Ink también tiene un ecosistema de componentes comunitarios mucho mayor.

¿Dónde llega al límite el renderizador de Ink?

El techo de Ink es su throttle de renderizado: por defecto limita los redibujados a 30 frames por segundo, y toda actualización de estado que exceda ese presupuesto espera al siguiente frame. Para los fundamentos de Ink, consulta la guía anterior sobre construir interfaces de terminal con Node.js, que recomendaba Ink en diciembre de 2025, antes de que OpenTUI fuera una opción seria; este artículo es la actualización de ese veredicto.

El throttle está documentado, no es folclore. Históricamente Ink tenía codificado de forma fija un throttle de 32 milisegundos alrededor de su función de render, de donde proviene la cifra tan repetida del “límite de 32 FPS”: 32 es el intervalo en milisegundos, y la tasa que se deriva de él es un techo de 30 frames por segundo. La versión actual de Ink expone esto como la opción de render maxFps, con valor predeterminado 30, junto con una opción incrementalRendering que limita cada repintado a las líneas que cambiaron. Así que el límite es ajustable. Lo que no es ajustable es la arquitectura: cada frame se compone en JavaScript, se diferencia como strings y se escribe en stdout por el mismo event loop que ejecuta la lógica de tu aplicación.

Para un spinner, un formulario o una barra de progreso, nada de esto es perceptible. Se vuelve perceptible cuando la salida es un stream: tokens del modelo llegando más rápido que el presupuesto de frames, un visor de logs siguiendo un servicio con mucha carga, un dashboard que re-renderiza regiones grandes. También hay un piso de memoria. Un proceso de Ink carga el runtime de Node más el reconciliador de React para lo que podrían ser unas pocas líneas de salida; no existe ninguna medición pública fiable de ese overhead, así que desconfía de cualquier cifra concreta en megabytes que leas.

¿Qué añade OpenTUI?

OpenTUI traslada el renderizado por completo fuera de JavaScript. Su núcleo está escrito en Zig y gestiona el búfer de pantalla, el dibujado y el parseo de entrada de forma nativa; TypeScript lo alcanza mediante FFI, bun:ffi en Bun o el FFI experimental de Node. El layout sigue siendo familiar: el dimensionado y el posicionamiento pasan por flexbox basado en Yoga, el mismo motor que usa Ink, de modo que flexDirection, flexGrow y compañía se transfieren directamente.

Dos añadidos importan más allá del renderizador. Primero, los componentes integrados cubren terreno que Ink deja a paquetes de terceros: un Input y un Textarea enfocables, Select, ScrollBox, resaltado de sintaxis respaldado por tree-sitter en Code, una vista Diff y Markdown. Dos más, una tabla de texto y un terminal embebido, existen únicamente como renderables de Core, por lo que React y Solid no pueden alcanzarlos como elementos JSX. Segundo, la elección de framework: @opentui/react y @opentui/solid son ambos bindings de primera clase, así que los equipos que prefieren reactividad de grano fino para actualizaciones de alta frecuencia no quedan atados al reconciliador de React. También hay una integración con Three.js WebGPU, una curiosidad para esta comparación y, además, exclusiva de Bun.

¿Cómo se ve la misma UI en Ink y en OpenTUI?

El coste de migración se aprecia mejor construyendo una UI dos veces: un panel con borde, una línea de texto, una tecla que alterna el estado y una salida limpia. Los fragmentos apuntan a Ink 7 y OpenTUI 0.5.x.

Ink, con andamiaje generado por npx create-ink-app:

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 />);

OpenTUI, con andamiaje generado por bun create tui --template react:

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 />);

El diff es la guía de migración. El punto de entrada cambia de la llamada render() de Ink a createCliRenderer() de @opentui/core más createRoot(renderer).render() de @opentui/react. Los componentes Box y Text en mayúsculas se convierten en los intrínsecos box y text en minúsculas, y los nombres de elementos de más de una palabra llevan guion, como en <ascii-font>. useInput y useApp de Ink se corresponden con useKeyboard y renderer.destroy() de OpenTUI. React en sí se traslada sin cambios: tanto ink como @opentui/react requieren React 19.2 o posterior, y useState funciona de forma idéntica. Los modelos de foco difieren más: Ink incluye useFocus con ciclado por Tab integrado, mientras que OpenTUI otorga el foco mediante una prop focused que gestionas en el estado.

El crédito: OpenTUI renderiza OpenCode en producción

OpenTUI no es un proyecto de demostración. Fue construido por Anomaly, la empresa detrás de OpenCode, y el README del proyecto presenta a OpenCode como un despliegue en producción que atiende a millones de personas. Esa carga de trabajo, un agente de programación que envía en streaming la salida del modelo, diffs y código con resaltado de sintaxis a un terminal interactivo, es precisamente la carga donde se nota el throttle de Ink. El linaje también importa: la interfaz de OpenCode fue reescrita desde Go y Bubble Tea hacia OpenTUI. Una advertencia para quienes usan React: la TUI de OpenCode se ejecuta sobre el reconciliador de Solid, así que el rodaje en producción cubre el núcleo y el binding de Solid más que @opentui/react, que, a diferencia de Core y Solid, no tiene un carril de Node.js en CI.

El coste: rotación, runtimes y ecosistema

Los costes se concentran en tres lugares, y el del runtime es un problema de distribución, no de experiencia de desarrollo.

CriterioInk 7OpenTUI 0.5.x
RuntimeNode 22+Bun 1.3+ o Node 26.4+ con --experimental-ffi, solo ESM
RenderizadoJavaScript, 30 fps por defecto vía maxFpsNúcleo nativo en Zig mediante FFI
LayoutFlexbox de YogaFlexbox de Yoga
Componentes integradosBox, Text, Static; inputs vía paquetes comunitariosInput, Select, ScrollBox, Code, Diff, Markdown y más
FrameworksReactReact y Solid
MadurezVersiones mayores separadas por años; Ink 7 solo rompió mínimos de runtime y eventos de tecladoVarias releases por mes en una línea 0.x

Una CLI con Ink funciona donde funcione Node 22 o posterior. Una CLI con OpenTUI impone un requisito de última generación a cada usuario final: según la matriz de soporte de runtimes, eso significa Bun 1.3.0+ o Node.js 26.4.0+ con el flag experimental de FFI, solo ESM, y un require de CommonJS falla directamente. Para una herramienta publicada en npm e instalada por desconocidos, eso o reduce tu audiencia o te empuja hacia la distribución como binario compilado.

La estabilidad es el segundo coste. La página de releases muestra desde v0.4.4 hasta v0.5.8 publicadas en aproximadamente seis semanas. En una línea 0.x, ese ritmo implica versiones fijadas y vigilancia del changelog. La API de Ink, en cambio, se ha mantenido estable a lo largo de años y versiones mayores. Tercero, el ecosistema: los paquetes comunitarios, recetas y respuestas de Stack Overflow de Ink todavía no tienen equivalente en OpenTUI, aunque los componentes integrados más ricos de OpenTUI compensan parte de esa brecha. La depuración está prácticamente a la par; ambos soportan React DevTools con DEV=true, y OpenTUI añade un overlay de consola y diagnósticos de renderizado.

¿Deberías cambiar de Ink a OpenTUI?

Cambia ya si tu TUI se redibuja continuamente y controlas el runtime: un frontend interno de agente, un visor de logs para tu propio equipo, cualquier cosa distribuida como binario compilado donde el requisito de Bun desaparece dentro del build. El núcleo en Zig, los componentes Code y Diff, y la opción de Solid son ventajas genuinas ahí, y OpenCode demuestra la arquitectura a escala.

Quédate en Ink si publicas una CLI en npm para audiencias generales de Node, si tu UI son formularios, prompts y progreso en lugar de streams continuos, o si no puedes absorber cambios incompatibles entre versiones menores. El valor predeterminado de 30 fps de Ink es ajustable vía maxFps, y su nómina en producción, con Claude Code, Gemini CLI, GitHub Copilot CLI, Wrangler y Prisma entre otros, muestra hasta dónde llega el modelo con throttle. Bubble Tea y Ratatui siguen siendo opciones para equipos dispuestos a abandonar TypeScript, lo cual invalida la premisa de este artículo.

Conclusión

OpenTUI se gana la etiqueta de “alternativa real” por arquitectura y evidencia en producción, e Ink conserva el puesto de opción predeterminada por estabilidad y alcance. La pregunta decisiva no es qué renderizador es más rápido; es si tus usuarios pueden ejecutar tu runtime. Prototipa tu pantalla más exigente con bun create tui --template react, obsérvala bajo un stream real y deja que la restricción de runtime, no el benchmark, tome la decisión.

Preguntas frecuentes

¿OpenTUI es exclusivo de Bun o también funciona en Node.js?

No, OpenTUI no es exclusivo de Bun. Bun 1.3.0 y superior funciona, y también Node.js 26.4.0 y superior, siempre que tu aplicación sea ESM y arranques Node con el flag experimental de FFI; si importas Core mediante un require de CommonJS, lanza un error. Algunas piezas siguen siendo exclusivas de Bun, entre ellas @opentui/three y los plugins cargados en tiempo de ejecución, y el soporte de FFI de Node es en sí mismo experimental, por lo que Bun sigue siendo la vía mejor probada.

¿Funciona OpenTUI en Windows?

Sí. Se publican paquetes precompilados del núcleo nativo para Windows x64 y Windows arm64, junto con macOS y builds tanto de glibc como de musl para Linux. En Windows, las pruebas propias del proyecto se realizan con Bun en x64, mientras que su carril de aceptación de Node.js corre en Linux x64, así que prueba una release en un terminal de Windows real antes de publicar, especialmente si tus usuarios están en Node y no en Bun.

¿OpenTUI soporta React DevTools?

Sí, a pesar de afirmaciones en contra que circulan en internet. La documentación de @opentui/react describe cómo instalar react-devtools-core@7 como dependencia de desarrollo, ejecutar npx react-devtools@7 y lanzar la aplicación con DEV=true para inspeccionar el árbol de componentes. Ink soporta React DevTools de la misma forma mediante DEV=true, así que las herramientas de depuración no son un diferenciador relevante entre ambas bibliotecas.

¿Debería usar el binding de React o el de Solid de OpenTUI?

Elige Solid para la vía más probada en producción: la interfaz de terminal de OpenCode corre sobre el reconciliador de Solid, y @opentui/solid tiene cobertura de CI en Node.js que a @opentui/react le falta. Elige React si tu equipo ya trabaja con él; el binding requiere React 19.2.0 o posterior e incluye hooks como useKeyboard y useTimeline. Ten en cuenta que @opentui/solid fija Solid exactamente en la versión 1.9.12.

DevTools for the frontend

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

We use cookies to improve your experience. By using our site, you accept cookies.