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.
Community Posts
No posts yet. Be the first to write about this video!
Write about this video