Skip to content


⚠️ See the breaking changes for this release.

LineGauge: Background Styles πŸ“Š

LineGauge::gauge_style is now deprecated in favor of filled_style and unfilled_style methods which makes it possible to set the foreground/background styles for different states.

let gauge = LineGauge::default()

We also added a Line Gauge example:

List: Navigation Methods 🧭

You can now navigate in the List widget by using the following methods!

let mut state = ListState::default();

It also clamps the selected index to the bounds of the list when navigating.

Text: Conversion From Display πŸ”„

Text, Span and Line now supports conversion from any type that implements the Display trait!

let text = "line1\nline2".to_text();
let span = (6.66).to_span();
let line = 42.to_line();

This has been made possible with the newly added ToText, ToSpan and ToLine traits respectfully.

Palette Colors 🎨

⚠️ This is behind the β€œpalette” feature flag.

You can now use colors from the palette crate in Ratatui!

use palette::{LinSrgb, Srgb};
use ratatui::style::Color;
let color = Color::from(Srgb::new(1.0f32, 0.0, 0.0));
let color = Color::from(LinSrgb::new(1.0f32, 0.0, 0.0));

New Border Sets πŸ–ΌοΈ


It uses an empty space symbol (β–‘)

let block = Block::bordered().title("Title").border_set(border::EMPTY);
β–‘β–‘ β–‘β–‘
β–‘β–‘ β–‘β–‘ β–‘β–‘
β–‘β–‘ β–‘β–‘ β–‘β–‘
β–‘β–‘ β–‘β–‘

This is useful for when you need to allocate space for the border and apply the border style to a block without actually drawing a border. This makes it possible to style the entire title area or a block rather than just the title content.


It uses a full block symbol (β–ˆ)

let block = Block::bordered().title("Title").border_set(border::FULL);

Re-export Backends πŸ“€

crossterm, termion, and termwiz can now be accessed as ratatui::{crossterm, termion, termwiz} respectively.

This makes it possible to just add the Ratatui crate as a dependency and use the backend of choice without having to add the backend crates as dependencies.

To update existing code, replace all instances of crossterm:: with ratatui::crossterm::, termion:: with ratatui::termion::, and termwiz:: with ratatui::termwiz::.

Example for crossterm:

use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};

And then you can remove crossterm from Cargo.toml!

Update Prelude πŸ“œ

Based on a suggestion on Reddit we made changes to the prelude module.

Note: This module allows you to easily use ratatui without a huge amount of imports! e.g. use ratatui::prelude::*;

The following items have been removed from the prelude:

  • style::Styled - this trait is useful for widgets that want to support the Stylize trait, but it adds complexity as widgets have two style methods and a set_style method.
  • symbols::Marker - this item is used by code that needs to draw to the Canvas widget, but it’s not a common item that would be used by most users of the library.
  • terminal::{CompletedFrame, TerminalOptions, Viewport} - these items are rarely used by code that needs to interact with the terminal, and they’re generally only ever used once in any app.

The following items have been added to the prelude:

  • layout::{Position, Size} - these items are used by code that needs to interact with the layout system. These are newer items that were added in the last few releases, which should be used more liberally.

Tracing Example πŸ”

Wondering how to debug TUI apps? Tried println and it didn’t work? We got you covered!

We added an example that demonstrates how to log to a file:

tracing example

We added a proof-of-concept example for using hyperlinks in the terminal.


The code is available here.

Cell: New methods πŸ”§

You can now create empty Cells like this:

let mut cell = Cell::EMPTY;
assert_eq!(cell.symbol(), " ");

We also added a constant Cell:new method for simplify the construction as follows:

let mut cell = Cell::default();
let cell = Cell::new("a");

Make Stylize::bg() generic πŸ”„

Previously, Stylize::bg() accepted Color but now accepts Into<Color>. This allows more flexible types from calling scopes, though it can break some type inference in the calling scope.

let srgb_color: Srgb<u8> = Srgb::new(255, 0, 0);;

Writer Methods on Backends πŸ–‹οΈ

crossterm and termion backends now have writer() and writer_mut() methods for obtain access to the underlying writer.

This is useful e.g. if you want to see what has been written so far.

let terminal = Terminal::new(CrosstermBackend::new(Vec::<u8>::new()));
let ui = |frame| { ... };
let crossterm_backend = terminal.backend();
let buffer = crossterm_backend.writer();

Add Missing VHS Tapes πŸ“Ό

We were missing demos for some of our examples. They are now added!

Constraint explorer example:


Minimal example:


List: Remove deprecated start_corner() 🚫

List::start_corner was deprecated back in v0.25.

Use List::direction and ListDirection instead:

// This is not an error, BottomRight rendered top to bottom previously
// all becomes
// becomes

layout::Corner is also removed entirely.

Padding: Deprecate zero() 🚫

It is now a constant!


Buffer: Improve Performance ⚑️

Buffer::filled now moves the cell instead of taking a reference:

Buffer::filled(area, &Cell::new("X"));
Buffer::filled(area, Cell::new("X"));

Rect: Improve Performance ⚑️

Margin needs to be passed without reference now:

let area = area.inner(&Margin {
let area = area.inner(Margin {
vertical: 0,
horizontal: 2,

Other πŸ’Ό

  • Position and Size now implements Display (#1162)
  • Remove newlines when converting strings to Lines (#1191)
    • Line::from("a\nb") now returns a Line with two Spans instead of one
  • Ensure that zero-width characters are rendered correctly (#1165)
  • Respect area width while rendering &str and String (#1177)
  • Improve benchmark consistency (#1126)

β€œI can’t believe it! A real gourmet kitchen, and I get to watch!” – Remy