Back to Ratatui

v0.30.0

src/content/docs/highlights/v0.30.md

latest23.6 KB
Original Source

We are excited to announce Ratatui 0.30.0, one of our biggest releases yet! ๐Ÿ๐Ÿš€๐ŸŒ•

In this release we've modularized the crates, added full no_std support for embedded targets, introduced the new ratatui::run() API, and brought major widget and layout improvements โ€” all with better backend flexibility and styling improvements.

See the changelog for the full list of changes. See the breaking changes for this release here.

Modularization ๐Ÿงฉ

Starting with Ratatui 0.30.0, the codebase was reorganized from a single monolithic crate into a modular workspace consisting of multiple specialized crates. This architectural decision was made to improve modularity, reduce compilation times, enable more flexible dependency management, and provide better API stability for third-party widget libraries.

Here is the new structure of the Ratatui workspace:

ratatui
โ”œโ”€โ”€ ratatui-core
โ”œโ”€โ”€ ratatui-widgets (depends on ratatui-core)
โ”œโ”€โ”€ ratatui-crossterm (depends on ratatui-core)
โ”œโ”€โ”€ ratatui-termion (depends on ratatui-core)
โ”œโ”€โ”€ ratatui-termwiz (depends on ratatui-core)
โ””โ”€โ”€ ratatui-macros

See the architecture overview for more details.

Migration Guide

If you are an application developer, you can continue using ratatui as before ๐ŸŽ‰

If you are a widget author, consider switching to ratatui-core for better stability:

rust
// Before (0.29.x and earlier; v0.30.0+ for regular users)
use ratatui::{
    widgets::{Widget, StatefulWidget},
    buffer::Buffer,
    layout::Rect,
};

// After (0.30.0+ for widget developers)
use ratatui_core::{
    widgets::{Widget, StatefulWidget},
    buffer::Buffer,
    layout::Rect,
};

:::note

The ratatui-core crate contains the fundamental types, traits, and utilities that form the foundation of Ratatui. It evolves more slowly than the main ratatui crate or the backend/widget crates, meaning fewer breaking changes and a more predictable API surface for third-party widget libraries.

:::

no_std Support ๐Ÿ› ๏ธ {#no_std-support}

we are so embedded

Ratatui now supports compilation for no_std targets! ๐ŸŽ‰

This means it can run on bare metal or microcontrollers. Ratatui was successfully tested to run on ESP32, STM32H7, PSP (yes, the console) and UEFI using experimental backends.

To use it in your no_std project, disable default-features:

toml
ratatui = { version = "0.30.0", default-features = false }

All of the features that don't depend on std feature are also supported in no_std and can be re-enabled if needed.

:::note

  • Using Ratatui in no_std projects requires either implementing a custom Backend or using a third party one (like mousefood).
  • Ratatui still uses allocations and requires defining a global allocator.
  • Ratatui requires atomic types, if your target doesn't support atomics, try enabling portable-atomic feature. For more information check portable-atomic crate's documentation.

:::

:::tip

If you are a widget crate author, check out ratatui-widgets documentation for tips on how to make your widgets no_std-compatible.

:::

:::tip

Check out this tutorial on Rust impl book for using Ratatui in embedded systems.

:::

Execution ๐Ÿ› ๏ธ {#execution}

We introduced ratatui::run() method which runs a closure with a terminal initialized with reasonable defaults for most applications.

This calls ratatui::init() before running the closure and ratatui::restore() after the closure completes, and returns the result of the closure.

A minimal hello world example using the new ratatui::run() method:

rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
    ratatui::run(|terminal| {
        loop {
            terminal.draw(|frame| frame.render_widget("Hello World!", frame.area()))?;
            if crossterm::event::read()?.is_key_press() {
                break Ok(());
            }
        }
    })
}

Of course, this also works both with apps that use free methods and structs:

rust
fn run(terminal: &mut DefaultTerminal) -> Result<(), AppError> { ... }

ratatui::run(run)?;
rust
struct App { ... }

impl App {
    fn new() -> Self { ... }
    fn run(mut self, terminal: &mut DefaultTerminal) -> Result<(), AppError> { ... }
}

ratatui::run(|terminal| App::new().run(terminal))?;

Widgets ๐Ÿงฉ

BarChart ๐Ÿ“Š

Simplified label handling

Bar::label() & BarGroup::label() now accept Into<Line<'a>> instead of Line<'a>:

diff
- Bar::default().label("foo".into());
+ Bar::default().label("foo");
diff
- BarGroup::default().label("bar".into());
+ BarGroup::default().label("bar");

New constructors

  • BarChart::new , BarChart::vertical, BarChart::horizontal, BarChart::grouped
  • Bar::new, Bar::with_label
  • BarGroup::new, BarGroup::with_label

This makes it easier to create barcharts and bars without needing to use the builder pattern:

rust
BarChart::grouped(vec![
    BarGroup::with_label(
        "Group 1",
        vec![Bar::with_label("A", 10), Bar::with_label("B", 20)],
    ),
    BarGroup::with_label(
        "Group 2",
        [Bar::with_label("C", 30), Bar::with_label("D", 40)],
    ),
]);

Other improvements

Bar now implements Styled

Canvas ๐ŸŽจ

New marker types are added to Canvas for better resolution:

  • Marker::Quadrant: densely packed and regularly spaced pseudo-pixels with a 2x2 resolution per character, without visible bands between cells. (e.g. 'โ–Œ', 'โ–ž', 'โ–›')
  • Marker::Sextant: same as Quadrant but with a 2x3 resolution per character (e.g. '๐Ÿฌช', '๐Ÿฌซ', '๐Ÿฌฌ')
  • Marker::Octant: same as Braille but with a 2x4 resolution per character (e.g. '๐œถŸ', '๐œถ ', '๐œถก')

:::note

The Octant marker is an alternative to the Braille marker with the same resolution, but offering densely packed, regular pseudo-pixels, without visible bands between rows and columns.

Sextant and Octant unicode characters that are less widely supported at the moment, which is why Braille was left as the default.

:::

:::caution

Following breaking changes were made:

  • symbols::braille::BLANK and symbols::braille::DOTS have been removed in favor of an ordered array of all Braille characters.
  • The Marker enum is now #[non_exhaustive] so if you were matching on Marker exhaustively, you will need to add a wildcard arm (e.g. _ => { /* handle other cases */ }).

:::

Scrollbar ๐Ÿ–ฑ๏ธ

You can now retrieve the ScrollbarState position via ScrollbarState::get_position()

Block ๐Ÿงฑ

Support for merging borders

When two borders overlap, they will automatically merge into a single, clean border instead of overlapping.

This improves visual clarity and reduces rendering glitches around corners.

For example:

rust
assert_eq!(Cell::new("โ”˜").merge_symbol("โ”", MergeStrategy::Exact).symbol(), "โ•†");

See the MergeStrategy documentation for more details on how this works.

But in a nutshell, it makes it possible to collapse borders as follows:

plain
โ”Œโ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ•ฎโ”Œโ”€โ”€โ”€โ”
โ”‚   โ”‚    โ”‚   โ”‚  โ”‚   โ”‚   โ”‚โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ โ•ญโ”€โ”ผโ”€โ•ฎโ”‚   โ”‚   โ”‚โ”‚   โ”‚
โ”‚   โ”‚    โ”‚ โ”‚ โ”‚ โ”‚โ”‚   โ”‚   โ”‚โ”‚   โ”‚
โ””โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ•ฎโ””โ”€โ”ผโ”€โ”˜ โ”‚โ””โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ•ฏโ”œโ”€โ”€โ”€โ”ค
    โ”‚   โ”‚  โ”‚   โ”‚         โ”‚   โ”‚
    โ”‚   โ”‚  โ•ฐโ”€โ”€โ”€โ•ฏ         โ”‚   โ”‚
    โ”‚   โ”‚                โ”‚   โ”‚
    โ•ฐโ”€โ”€โ”€โ•ฏ                โ•ฐโ”€โ”€โ”€โ•ฏ

New BorderTypes

<details> <summary>Click here to see them!</summary>

LightDoubleDashed:

plain
โ”Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ”
โ•Ž       โ•Ž
โ””โ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ”˜

HeavyDoubleDashed:

plain
โ”โ•โ•โ•โ•โ•โ•โ•โ”“
โ•       โ•
โ”—โ•โ•โ•โ•โ•โ•โ•โ”›

LightTripleDashed:

plain
โ”Œโ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”
โ”†       โ”†
โ””โ”„โ”„โ”„โ”„โ”„โ”„โ”„โ”˜

HeavyTripleDashed:

plain
โ”โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”“
โ”‡       โ”‡
โ”—โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”›

LightQuadrupleDashed:

plain
โ”Œโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”
โ”Š       โ”Š
โ””โ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”˜

HeavyQuadrupleDashed:

plain
โ”โ”‰โ”‰โ”‰โ”‰โ”‰โ”‰โ”‰โ”“
โ”‹       โ”‹
โ”—โ”‰โ”‰โ”‰โ”‰โ”‰โ”‰โ”‰โ”›
</details>

Remove Block::title

The title alignment is better expressed in the Line as this fits more coherently with the rest of the library.

  • widgets::block is no longer exported
  • widgets::block::Title no longer exists
  • widgets::block::Position is now widgets::TitlePosition
  • Block::title() now accepts Into::<Line> instead of Into<Title>
  • BlockExt is now exported at widgets::BlockExt instead of widgets::block::BlockExt

This is a breaking change.

LineGauge ๐Ÿ“

Support customizable symbols

LineGauge now support customizable symbols via LineGauge::filled_symbol and LineGauge::unfilled_symbol methods:

rust
let gauge = LineGauge::default()
    .filled_symbol("โ–ˆ")
    .unfilled_symbol("โ–‘")
    .ratio(0.80);
plain
80% โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘

Deprecations

LineGauge::line_set method is now deprecated.

List ๐Ÿ“ƒ

List::highlight_symbol now accepts Into<Line> instead of &str.

This makes it possible to customize the highlight symbol as follows:

rust
let list = List::new(["Item 0", "Item 1", "Item 2"])
  .highlight_symbol(Line::from(">>").red().bold());

This is a breaking change and any code that uses conversion methods will need to be rewritten. Since Into::into is not const, this function cannot be called in const context.

Tabs ๐Ÿ“‘

Add Tabs::width method to easily calculate the total tab width including all dividers and padding

RatatuiMascot widget ๐Ÿ

Introducing RatatuiMascot: A widget that displays the Ratatui mascot!

rust
let mascot = RatatuiMascot::new().set_eye(MascotEyeColor::Red);
text
             โ–„โ–„โ–ˆโ–ˆโ–ˆ
           โ–„โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
         โ–„โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
        โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
        โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–€   โ–„โ–„โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
              โ–€โ–ˆโ–ˆโ–ˆโ–€โ–„โ–ˆโ–€โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
            โ–„โ–„โ–„โ–„โ–€โ–„โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
           โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
           โ–€โ–ˆโ–ˆโ–ˆโ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
         โ–„โ–€โ–€โ–„   โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
       โ–„โ–€ โ–„  โ–€โ–„โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
     โ–„โ–€  โ–€โ–€    โ–€โ–„โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
   โ–„โ–€      โ–„โ–„    โ–€โ–„โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
 โ–„โ–€         โ–€โ–€     โ–€โ–„โ–€โ–ˆโ–ˆโ–€  โ–ˆโ–ˆโ–ˆ
โ–ˆ                    โ–€โ–„โ–€  โ–„โ–ˆโ–ˆ
 โ–€โ–„                    โ–€โ–„โ–€โ–ˆ

Examples ๐Ÿงช

The examples have been simplified and reorganized.

  • ratatui-widgets/examples: contains simple widget examples (that are meant to be copy & pasted easily).
  • examples/: contains more complex application and concept examples that are useful for getting inspiration for designing your own applications.

Also new examples such as mouse-drawing, widget-ref-container and collapsed-borders are added.

Text ๐Ÿ“

AddAssign for Text

Text now implements AddAssign trait.

This makes it possible to add a second Text instance to a first one using the += operator.

rust
let mut text = Text::from("line 1");
text += Text::from("line 2");

Style and alignment applied to the second text is ignored (though styles and alignment of lines and spans are copied).

Other improvements

  • Don't render control characters for Span

  • Implement UnicodeWidthStr for Text/Line/Span for retrieving the width via width and width_cjk

Styling ๐ŸŽจ

Conversions from anstyle

Support conversions from anstyle styles (gated behind anstyle feature):

rust
let anstyle_color = anstyle::Ansi256Color(42);
let color = Color::from(anstyle_color);

Conversions from tuples

Added generic color conversion methods from tuples:

rust
Color::from([255, 0, 0]);
Color::from((255, 0, 0));
Color::from([255, 0, 0, 255]);
Color::from((255, 0, 0, 255));

Conversions from primitives

Implement Styled for primitives such as u8, i32, f64, Cow<'a, str>, etc.

rust
let s = Cow::Borrowed("a");
assert_eq!(s.red(), "a".red());

Implement stylize methods directly on Style

This makes it possible to create constants using the shorthand methods.

rust
const MY_STYLE: Style = Style::new().blue().on_black();

This is a breaking change.

Layout ๐Ÿ“

Ergonomic layouting methods

We introduced new methods for Rect that simplify the process of splitting a Rect into sub-rects according to a given Layout.

Rect::layout and Rect::try_layout:

rust
use ratatui_core::layout::{Layout, Constraint, Rect};
let area = Rect::new(0, 0, 10, 10);
let layout = Layout::vertical([Constraint::Fill(1); 2]);

// Rect::layout() infers the number of constraints at compile time:
let [top, main] = area.layout(&layout);

Layout::try_areas method that returns an array of sub-rects, with compile-time checks for the number of constraints.

This is added mainly for consistency with the new Rect methods.

rust
// Rect::try_layout() and Layout::try_areas() do the same, but return a
// Result:
let [top, main] = area.try_layout(&layout)?;
let [top, main] = layout.try_areas(area)?;

Rect::layout_vec method that returns a Vec of sub-rects.

rust
let areas_vec = area.layout_vec(&layout);

Helper methods for centering Rects

For centering:

rust
let area = frame
  .area()
  .centered(Constraint::Ratio(1, 2), Constraint::Ratio(1, 3));

Or for vertical centering:

rust
let area = frame.area().centered_vertically(Constraint::Ratio(1, 2));

Horizontally centering:

rust
let area = frame.area().centered_horizontally(Constraint::Length(3));

Add Rect::outer method

This creates a new Rect outside the current one, with the given margin applied on each side.

Also added VerticalAlignment type.

Introduce Flex::SpaceEvenly

Old Flex::SpaceAround behavior is available by using Flex::SpaceEvenly and new Flex::SpaceAround now distributes space evenly around each element except the middle spacers are twice the size of first and last elements

With this change, the following variants of Flex are supported:

  • Flex::Start: Aligns items to the start; excess space appears at the end.
  • Flex::End: Aligns items to the end; excess space appears at the start.
  • Flex::Center: Centers items with equal space on both sides.
  • Flex::SpaceAround (new): Distributes space around items; space between items is twice the edge spacing.
  • Flex::SpaceBetween: Distributes space evenly between items except no space at the edges.
  • Flex::SpaceEvenly (previously Flex::SpaceAround): Distributes space evenly between items and edges.
  • Flex::Legacy: Preserves legacy behavior, placing all excess space at the end.

This aligns behavior of Flex with CSS flexbox more closely.

The following is a screenshot in action:

Other improvements

  • Rename Alignment to HorizontalAlignment to better reflect its purpose
diff
- use ratatui::layout::Alignment;
+ use ratatui::layout::HorizontalAlignment;

- use Alignment::*;
+ use HorizontalAlignment::*;
  • New constructors: Offset::new

  • Rect::from(size) returns a new Rect at the origin (0, 0) with the specified Size

Backend ๐Ÿ–ฅ๏ธ {#backend}

Backend conversion traits

The From implementations for backend types are now replaced with more specific traits.

This effects the styling conversions such as Color:

diff
+ use ratatui::backend::crossterm::{FromCrossterm, IntoCrossterm};

let crossterm_color = crossterm::style::Color::Black;

- let ratatui_color = crossterm_color.into();
- let ratatui_color = ratatui::style::Color::from(crossterm_color);
+ let ratatui_color = ratatui::style::Color::from_crossterm(crossterm_color);

- let crossterm_color = ratatui_color.into();
- let crossterm_color = crossterm::style::Color::from(ratatui_color);
+ let crossterm_color = ratatui_color.into_crossterm();

Backend specific traits are added for crossterm (FromCrossterm, IntoCrossterm), termion (FromTermion, IntoTermion), and termwiz (FromTermwiz, IntoTermwiz).

See this breaking changes entry for more information.

Associated Error type and required clear_region method

Custom Backend implementations now require an associated Error type and clear_region method.

This change was made to provide greater flexibility for custom backends, particularly to remove the explicit dependency on std::io for backends that want to support no_std targets.

Also, if your app or library uses the Backend trait directly - for example, by providing a generic implementation for many backends - you may need to update the referenced error type.

diff
- fn run<B: Backend>(mut terminal: Terminal<B>) -> io::Result<()> {
+ fn run<B: Backend>(mut terminal: Terminal<B>) -> Result<(), B::Error> {

See this breaking changes entry for more information and other workarounds.

Support for multiple crossterm versions

We now have individual feature flags for different crossterm versions. By default, the latest version is enabled. If multiple features are enabled, we choose the latest version.

e.g.

toml
ratatui = { version = "0.30.0", features = ["crossterm_0_28"] } # or "crossterm_0_29"

If your dependency graph ends up with multiple Crossterm majors, see Crossterm version compatibility for the risks and mitigations.

Other improvements

TestBackend now uses core::convert::Infallible for error handling instead of std::io::Error

Traits ๐Ÿ”ง

State associated types are now ?Sized

StatefulWidget::State and StatefulWidgetRef::State are now ?Sized.

This allows implementations of the traits to use unsized types for the State associated type. This is turn is useful when doing things like boxing different stateful widget types with State which implements Any, are slices or any other dynamically sized type.

Changes to WidgetRef trait

WidgetRef no longer has a blanket implementation of Widget.

Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to instead be a blanket implementation of WidgetRef for all &W where W: Widget.

diff
-impl WidgetRef for Foo {
-    fn render_ref(&self, area: Rect, buf: &mut Buffer)
+impl Widget for &Foo {
+    fn render(self, area: Rect, buf: &mut Buffer)
}

Any widgets that previously implemented WidgetRef directly should now instead implement Widget for a reference to the type.

New FrameExt trait

To call Frame::render_widget_ref() or Frame::render_stateful_widget_ref() you now need to import the FrameExt trait and enable the unstable-widget-ref feature.

rust
use ratatui::{
    layout::Rect,
    widgets::{Block, FrameExt},
};

let block = Block::new();
let area = Rect::new(0, 0, 5, 5);
frame.render_widget_ref(&block, area);

Performance ๐Ÿš€

Disabling default-features will now disable layout cache, which can have a negative impact on performance

Layout cache is now opt-in in ratatui-core and enabled by default in ratatui.

If app doesn't make use of no_std-compatibility, and disables default-feature, it is recommended to explicitly re-enable layout cache. Not doing so may impact performance.

diff
- ratatui = { version = "0.29.0", default-features = false }
+ ratatui = { version = "0.30.0", default-features = false, features = ["layout-cache"] }

Also, Layout::init_cache and Layout::DEFAULT_CACHE_SIZE are only available if layout-cache feature is enabled.

Ratatui Badge โญ

We have added a "Built with Ratatui" badge for downstream projects

If you'd like to show your support, you can add the Ratatui badge to your project's README:

md
[![Built With Ratatui](https://ratatui.rs/built-with-ratatui/badge.svg)](https://ratatui.rs/)

If you want a custom badge, Ratatui logo is also available on shields.io! Some examples are:

md
![](https://img.shields.io/badge/Ratatui-000?logo=ratatui&logoColor=fff)
![](https://img.shields.io/badge/Ratatui-fff?logo=ratatui&logoColor=000)
![](https://img.shields.io/badge/Built_With-Ratatui-000?logo=ratatui&logoColor=fff&labelColor=000&color=fff)
![](https://img.shields.io/badge/Ratatui-000?logo=ratatui&logoColor=fff&style=flat-square)
![](https://img.shields.io/badge/Ratatui-000?logo=ratatui&logoColor=fff&style=for-the-badge)

Other ๐Ÿ’ผ

  • MSRV is now 1.86.0
  • The codebase now uses Rust 2024 edition
  • Derive Serialize/Deserialize for Constraint, Direction, Spacing, Layout, AccentedPalette, NonAccentedPalette, Palette, Padding, Borders, BorderType, ListDirection, ScrollbarOrientation, ScrollDirection, RenderDirection, and HighlightSpacing, HorizontalAlignment, VerticalAlignment
  • Allow omitting add/sub modifier fields in Style deserialization
  • VS16 wide emojis are now properly cleared from the buffer
  • Change Cell::symbol to Option<CompactString> to better represent empty cells
  • Make it possible to render Braille characters over Block symbols in Chart and Canvas
  • Add AI contribution guidelines and Copilot instructions

"Rats don't just survive; they discover; they create. ... I mean, just look at what they do with the terminal!" โ€“ Remy & Orhun