OpenTUI: Build Terminal Apps With React, Bun and Zig

BBetter Stack
Computing/SoftwareInternet Technology

Transcript

00:00:00This is OpenTui, a Zig-powered library for building terminal interfaces with bindings
00:00:04for React, Solid and FreeJS so you can build performant Tuis the same way you build web
00:00:09applications. It was built by Anomaly, the team behind OpenCode, which is a popular open source
00:00:14coding agent, to actually power the user interface behind OpenCode, which is used by millions of
00:00:19people on a regular basis. But how does this new library go up against the other popular React
00:00:24terminal interface tool that has been around for much longer and is used to power many popular
00:00:29coding agents? Hit subscribe and let's find out!
00:00:35So Ink is the go-to library for building terminal interfaces with React. I've actually made a
00:00:40video about it that you can check out over here. But it does have some problems. First
00:00:44of all, the simple apps uses more than 50MB of memory and has a hard-coded 30fps cap, which
00:00:51is fine for most applications. But if you're building an app that streams in lots of text,
00:00:56like a coding agent, then the frame cap makes the whole thing feel sluggish. So the team
00:01:00at Anomaly, who originally used Golang with Bubble T to build OpenCode, wanted to rewrite
00:01:06it in TypeScript without using ink. So they created OpenTui. I mean, they bought OpenTui. They didn't
00:01:13exactly build it from scratch because Commander FX was already building a Zig-based terminal
00:01:18library. So Anomaly sponsored him and built OpenTui on top of his work. So the Zig core does all the
00:01:24heavy rendering and the TypeScript bindings allows you to write UI components in React or Solid. But the
00:01:30really clever part is the BUN foreign function interface, which allows you to speak directly
00:01:36from TypeScript to the Zig native code without much delay, which is what makes OpenTui super
00:01:42performant. It uses yoga for Flexbox layouts, comes with built-in components like inputs and selects,
00:01:47and there's even a 3.js package that lets you render web GPU 3D graphics inside the terminal,
00:01:54which is kind of insane. In fact, let's see OpenTui in action by trying it on a very simple demo. There
00:01:59are many ways you can set up a basic OpenTui project. Personally, I like using BUN CreateTui
00:02:04because it gives you this very useful wizard you can use to give a name to your project and choose
00:02:09the template you want to use. Now I'm going to go with React, but I'll explain later on in the video,
00:02:13the differences between these three templates. So once that's done, you get some standard files
00:02:17with an index that runs a basic full screen project. And if we look at the code,
00:02:21we can see here on 915 that it's using the CreateCLI renderer to make use of OpenTui's rendering
00:02:27engine. And beneath that there's CreateRoot, which renders the app component. And if you're familiar
00:02:32with React, this is the code that mounts it to a HTML file. But because we're rendering to a terminal,
00:02:37this project doesn't have an index.html, but instead it uses a custom React reconciler that uses
00:02:42terminal boxes and text instead of HTML components. Above that, we have the JSX that uses the box component
00:02:49and some yoga flex box props to align the box tested inside it that renders an ASCII font and some basic text.
00:02:55Now, if you didn't want this to be a full screen app, OpenTui supports multiple screen modes.
00:03:00And we can change this one to split footer with a footer height, which pins the render to a reserved
00:03:05region at the bottom of the terminal. But let's try to do something a bit more interesting than just changing
00:03:10the screen mode. Here is a basic Tui with some title text and a name input. Now, check this out. With OpenTui, you get
00:03:17responsive to this for free. So no matter the width of my terminal, everything still looks good. And if
00:03:21we take a look at the code, it's using some very familiar React syntax. So over here, we have a use
00:03:26state that's setting the name. And in the input, we're updating the name on the on input prop. And that
00:03:32gets changed in the text. Now, one really cool thing OpenTui does is kind of live reloading without the
00:03:37HMR. So we can see here, if I change the text to goodbye and then save the file, it gets automatically
00:03:43updated. And it's nice having a responsive input, but sometimes the text just doesn't show. This can
00:03:48be fixed by adding a width, which makes things much easier. And with the box component, we have
00:03:52some very cool props like border and background color, which immediately ages this terminal app.
00:03:56Again, we can use an ASCII font instead of this text, which makes the heading stand out a bit more.
00:04:01And of course, there are many ASCII fonts you can choose from. Remember, because this is a terminal
00:04:05app, not a web page, it doesn't render all the fonts you have on your system, just things that support the
00:04:10terminal. We can also do things like change the view on submit the same way you do with a regular react
00:04:15app by changing the state on submit and displaying different JSX based on this condition. But here's
00:04:20one thing you wouldn't expect a terminal to be able to do. If I submit my name, the other state now fades
00:04:25into place. And this is done with the use timeline hook from OpenTui, which is used to set an animation
00:04:30duration, set a target. So in this case, the component has an opacity of zero and an offset of eight,
00:04:36meaning it has a margin top below the center. And here the animation ends with an offset of zero
00:04:40and an opacity of one by updating the offset and opacity states that were set up here. And these
00:04:45values are updated in the box props. Now it does look a little jerky because terminal animations need
00:04:50to move row by row. So kind of like on a grid, but it's very cool that OpenTui allows you to do this
00:04:55so easily. What's really cool is that we can combine everything we've learned so far using the flexbox
00:05:00layout to put boxes next to each other. So we've got an input on one side and another box on the other.
00:05:05And if we type into the input on submit, we can animate the value going into the other box,
00:05:10which is a very nice touch. And from here, we can do so many things with OpenTui,
00:05:14like enable keyboard navigation with the use keyboard hook, show system data using node OS,
00:05:19enable mouse support virtualized lists. And because this is all running on top of BUN,
00:05:24you can use things like the BUN SQLite, BUN Postgres, or fetch external information the same
00:05:28way you do on any website. And the cool thing about this is after I compiled my app, yes, it is 71
00:05:34megabytes because it includes the BUN runtime and the React reconciliation. But when I run it,
00:05:39you can see it uses less than 50 megabytes of memory. And because React is so popular,
00:05:43LLMs know it like the back of their hand. And so building apps in OpenTui can be very simple
00:05:49without the need to go back and forth looking at the documentation. So that's a very, very simple
00:05:53run through of OpenTui. But let's go way back to earlier in the video, where I promised I'd explain
00:05:58the difference between React, Solid and Core. So it's pretty simple if you know about front-end
00:06:02web development. Basically, they all render through the exact same ZIC Core. So the actual drawing to
00:06:07your terminal is pretty identical. The only difference is how you write your components
00:06:11and how the updates get applied. So React reruns your components and diffs a virtual tree on every
00:06:17change, which is pretty similar to how the virtual DOM works. Solid uses fine-grained updates,
00:06:22so it only updates to the thing that changed. And Core skips all of that, meaning you just mutate
00:06:27the objects directly. So on paper, React is the heaviest and Core is the lightest. But in practice,
00:06:33for most terminal apps, the gap is tiny because all the heavy lifting is done by the ZIC Core,
00:06:38which means in this case, frameworks are just a personal preference. But even though an OpenTui
00:06:44app ships with the whole BUN runtime and the framework-specific reconciliation, if it has one,
00:06:50the performance and the size of the app is still much smaller than Ink, but of course not as
00:06:56performance as something native like Ratatouille or Bubble Tea. But in my opinion, JSX is the best
00:07:02and most intuitive way to compose any UI. And I'd rather take the slight memory and size hit and have
00:07:09a much better developer experience building a Tui than to have something that's light in size and light
00:07:15in memory usage, but a pain to write an update. So with that in mind, I'd pick OpenTui
00:07:20over Ink every single time, if I ever get around to building a terminal application,
00:07:25which I promise I will do soon.

Key Takeaway

OpenTui provides a high-performance alternative to Ink for building terminal interfaces by leveraging a Zig core, Bun FFI, and popular frameworks like React, while maintaining low memory overhead.

Highlights

  • OpenTui utilizes a Zig-based core to handle rendering, enabling high-performance terminal UI development via TypeScript bindings.

  • The Bun foreign function interface (FFI) facilitates direct communication between TypeScript and native Zig code to minimize latency.

  • OpenTui supports responsive layouts using Yoga for Flexbox and includes built-in components like inputs and selects.

  • The library enables advanced features such as WebGPU 3D graphics in the terminal via a 3.js package and frame-by-frame animations using the useTimeline hook.

  • Compiled OpenTui applications consume less than 50MB of memory at runtime, even when including the full Bun runtime and React reconciliation.

Timeline

Limitations of Existing Terminal UI Libraries

  • Ink relies on a React-based approach but suffers from a 50MB memory footprint and a hard-coded 30fps cap.
  • The 30fps frame cap limits performance for applications requiring high-volume text streaming, such as coding agents.
  • Developers aiming to build high-performance terminal interfaces require alternatives to avoid sluggishness in data-heavy applications.

While Ink is a popular choice for building terminal interfaces, its performance limitations become apparent in resource-intensive tasks. The hard-coded frame cap makes complex terminal applications feel unresponsive. These issues prompted the creation of OpenTui as a more performant alternative for complex coding agents.

OpenTui Architecture and Technical Foundation

  • OpenTui builds upon a Zig-based rendering engine to handle heavy UI tasks natively.
  • TypeScript bindings allow developers to construct UI components using familiar frameworks like React or Solid.
  • The Bun FFI allows direct interaction between TypeScript and Zig, significantly reducing communication delay.

OpenTui separates the UI rendering logic from the application logic. The Zig core performs the actual rendering, while the Bun runtime and FFI ensure that calls from the TypeScript layer execute with minimal overhead. This hybrid approach delivers the developer experience of web frameworks alongside the performance of native code.

Developing Terminal Interfaces with React Syntax

  • OpenTui employs a custom React reconciler that targets terminal boxes and text instead of DOM elements.
  • Developers can utilize standard React patterns, including state management, responsive Flexbox layouts, and component-based composition.
  • Advanced features like keyboard navigation, mouse support, and frame-based animations are supported through specific hooks.

Building with OpenTui resembles standard React web development, using familiar components like Box and state hooks. The engine provides responsive layouts for any terminal width and supports live reloading during development. Specialized hooks enable interactive elements, such as animating transitions between different UI states.

Framework Comparisons and Performance

  • React, Solid, and Core all utilize the same Zig rendering engine, meaning the actual output is identical.
  • React involves virtual tree diffing, Solid uses fine-grained updates, and Core allows direct object mutation.
  • OpenTui applications maintain a runtime memory usage under 50MB despite including the Bun runtime and framework-specific reconciliation logic.

The choice between React, Solid, and Core templates primarily affects how UI updates are applied, rather than rendering performance, as the Zig core performs the heavy lifting. While native alternatives like Bubble Tea or Ratatouille may be smaller or faster, OpenTui offers a superior balance of developer experience and performance by enabling the use of JSX.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video