Why am I getting duplicate key events on Windows?
A lot of examples out there in the wild might use the following code for sending key presses:
However, on Windows, when using
Crossterm, this will send the same
Event::Key(e) twice; one for
when you press the key, i.e.
KeyEventKind::Press and one for when you release the key, i.e.
KeyEventKind::Press kinds of
key event is
To make the code work as expected across all platforms, you can do this instead:
When should I use
ratatui isn’t a native
async library. So is it beneficial to use
As a user of
rataui, there really is only one point of interface with the
ratatui library and
terminal.draw(|f| ui(f)) functionality (the creation of widgets provided by
typically happens in
ui(f)). Everything else in your code is your own to do as you wish.
terminal.draw(|f| ui(f)) be
async? Possibly. Rendering to the terminal buffer is
relatively fast, especially using the double buffer technique that only renders diffs that
uses. Creating of the widgets can also be done quite efficiently.
So one question you may ask is can we make
async ourselves? Yes, we
can. Check out https://github.com/ratatui-org/async-template/tree/v0.1.0 for an example.
The only other part related to
ratatui that is beneficial to being
async is reading the key
event inputs from
stdin, and that can be made
So the real question is what other parts of your app require
async or benefit from being
If the answer is not much, maybe it is simpler to not use
async and avoiding
Another way to think about it is, do you think your app would work better with 1 thread like this?
Or would it work with 3 threads /
tokio tasks like this:
main thread or
tokio task, do you expect to be spawning more
tokio tasks? How many
more tasks do you plan to be spawning?
The former can be done without any
async code and the latter is the approach showcased in
simple-async uses this architecture instead with tokio:
The original repository contains all the issues, PRs and discussion that were raised originally, and it is useful to refer to when contributing code, documentation, or issues with Ratatui.
We imported all the PRs from the original repository and implemented many of the smaller ones and made notes on the leftovers. These are marked as draft PRs and labelled as imported from tui. We have documented the current state of those PRs, and anyone is welcome to pick them up and continue the work on them.
We have not imported all issues opened on the previous repository. For that reason, anyone wanting to work on or discuss an issue will have to follow the following workflow:
- Recreate the issue
- Start by referencing the original issue:
Referencing issue #[<issue number>](<original issue link>)
- Then, paste the original issues opening text
You can then resume the conversation by replying to the new issue you have created.
What is the difference between a library and a framework?
The terms library and framework are often used interchangeably in software development, but they serve different purposes and have distinct characteristics.
|A library is a collection of functions and procedures that a programmer can call in their application. The library provides specific functionality, but it’s the developer’s responsibility to explicitly call and use it.
|A framework is a pre-built structure or scaffold that developers build their application within. It provides a foundation, enforcing a particular way of creating an application.
|In the case of a library, the control flow remains with the developer’s application. The developer chooses when and where to use the library.
|With a framework, the control flow is inverted. The framework decides the flow of control by providing places for the developer to plug in their own logic (often referred to as “Inversion of Control” or IoC).
|Libraries are passive in nature. They wait for the application’s code to invoke their methods.
|Frameworks are active and have a predefined flow of their own. The developer fills in specific pieces of the framework with their own code.
|Imagine you’re building a house. A library would be like a toolbox with tools (functions) that you can use at will. You decide when and where to use each tool.
|Using the house-building analogy, a framework would be like a prefabricated house where the main structure is already built. You’re tasked with filling in the interiors and decor, but you have to follow the design and architecture already provided by the prefabricated design.
What is the difference between
ratatui (a library) and
tui-realm (a framework)?
ratatui provides tools (widgets) for building terminal UIs, it doesn’t dictate or enforce a
specific way to structure your application. You need to decide how to best use the library in your
particular context, giving you more flexibility.
tui-realm might provide more guidelines and enforcements about how your application
should be structured or how data flows through it. And, for the price of that freedom, you get more
features out of the box with
tui-realm and potentially lesser code in your application to do the
same thing that you would with
What is the difference between
Cursive and Ratatui are both libraries that make TUIs easier to write. Both libraries are great! Both also work on linux, macOS and windows.
Cursive uses a more declarative UI: the user defines the layout, then cursive handles the event loop. Cursive also handles most input (including mouse clicks), and forwards events to the currently focused view. User-code is more focused on “events” than on keyboard input. Cursive also supports different backends like ncurses, pancurses, termion, and crossterm.
One of cursive’s main features is its built-in event loop. You can easily attach callbacks to events like clicks or key presses, making it straightforward to handle user interactions.
In Ratatui, the user handles the event loop, the application state, and re-draws the entire UI on each iteration. It does not handle input and users have use another library (like crossterm). Ratatui supports Crossterm, termion, wezterm as backends.
You may have to write more code but you get precise control over exact UI you want to display with Ratatui.
Can you change font size in a terminal using
ratatui itself doesn’t control the terminal’s font size.
ratatui renders content based on the
size and capabilities of the terminal it’s running in. If you want to change the font size, you’ll
need to adjust the settings of your terminal emulator.
However, changing this setting in your terminal emulator will only change the font size for you
while you are developing your
ratatui based application.
When a user zooms in and out using terminal shortcuts, that will typically change the font size in their terminal. You typically will not know what the terminal font size is ahead of time.
However, you can know the current terminal size (i.e. columns and rows). Additionally, when zooming
in and out
ratatui applications will see a terminal resize event that will contain the new columns
and rows. You should ensure your
ratatui application can handle these changes gracefully.
You can detect changes in the terminal’s size by listening for terminal resize events from the backend of your choice and you can adjust your application layout as needed.
For example, here’s how you might do it in crossterm:
ratatui does support various styles, including bold, italic, underline, and more, and while this
doesn’t change the font size, it does provide you with the capability to emphasize or de-emphasize
text content in your application.
Some characters appear to be missing / look weird
ratatui, and TUIs in general, use special drawing characters like box-drawing characters,
braille characters or even icons. If your font doesn’t support such features, it may display a
white square (□) or a replacement character (�).
To fix this, we recommend you use a nerd font, this is generally what works the best. Kreative Square is also a good alternative. Though note that some characters may render a bit differently from font to font.
Can you use multiple
terminal.draw() calls consequently?
You cannot use
terminal.draw() multiple times in the same
Because Ratatui uses a double buffer rendering technique, writing code like this will NOT render all three widgets:
You want to write the code like this instead:
Should I use
crossterm, application developers have the option of rendering to
Both of these will work fine for normal purposes. The question you have to ask is how would you like your application to behave in non-TTY environments.
For example, if you run
ratatui-application | grep foo with
stdout, your application won’t
render anything to the screen and there would be no indication of anything going wrong. With
stderr the application will still render a TUI.
- Every app needs to add code to check if the output is a TTY and do something different based on the result
- App can’t write a result to the user that can be passed in a pipeline, e.g.
my-select-some-value-app | grep foo
- Tends to be what most command line applications do by default.
- No special setup necessary in order to run in a pipe command
- Unconventional and that might subvert users expectations
Out of the box,
stdout will be faster than
stderr because it is buffered. However you can very
stderr buffered too by wrapping it in a
BufWriter like so:
Our recommendation is to use
stdout. If you really need
stderr, then accept the performance loss
(which is unnoticeable in most applications) or make it buffered.
If you want to know more, we recommend reading this excellent article by @orhun.