src/content/docs/highlights/v0.30.md
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.
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.
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:
// 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:
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
no_std projects requires either implementing a custom Backend or using a
third party one (like mousefood).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.
:::
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:
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:
fn run(terminal: &mut DefaultTerminal) -> Result<(), AppError> { ... }
ratatui::run(run)?;
struct App { ... }
impl App {
fn new() -> Self { ... }
fn run(mut self, terminal: &mut DefaultTerminal) -> Result<(), AppError> { ... }
}
ratatui::run(|terminal| App::new().run(terminal))?;
Bar::label() & BarGroup::label() now accept Into<Line<'a>> instead of Line<'a>:
- Bar::default().label("foo".into());
+ Bar::default().label("foo");
- BarGroup::default().label("bar".into());
+ BarGroup::default().label("bar");
BarChart::new , BarChart::vertical, BarChart::horizontal, BarChart::groupedBar::new, Bar::with_labelBarGroup::new, BarGroup::with_labelThis makes it easier to create barcharts and bars without needing to use the builder pattern:
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)],
),
]);
Bar now implements Styled
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.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 */ }).:::
You can now retrieve the ScrollbarState position via ScrollbarState::get_position()
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:
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:
โโโโโ โโโโโ โโโโโฌโโโโฎโโโโโ
โ โ โ โ โ โ โโ โ
โ โ โ โญโโผโโฎโ โ โโ โ
โ โ โ โ โ โโ โ โโ โ
โโโโโผโโโโฎโโโผโโ โโโโโโดโโโโฏโโโโโค
โ โ โ โ โ โ
โ โ โฐโโโโฏ โ โ
โ โ โ โ
โฐโโโโฏ โฐโโโโฏ
BorderTypesLightDoubleDashed:
โโโโโโโโโ
โ โ
โโโโโโโโโ
HeavyDoubleDashed:
โโโโโโโโโ
โ โ
โโโโโโโโโ
LightTripleDashed:
โโโโโโโโโ
โ โ
โโโโโโโโโ
HeavyTripleDashed:
โโ
โ
โ
โ
โ
โ
โ
โ
โ โ
โโ
โ
โ
โ
โ
โ
โ
โ
LightQuadrupleDashed:
โโโโโโโโโ
โ โ
โโโโโโโโโ
HeavyQuadrupleDashed:
โโโโโโโโโ
โ โ
โโโโโโโโโ
Block::titleThe title alignment is better expressed in the Line as this fits more coherently with the rest of
the library.
widgets::block is no longer exportedwidgets::block::Title no longer existswidgets::block::Position is now widgets::TitlePositionBlock::title() now accepts Into::<Line> instead of Into<Title>BlockExt is now exported at widgets::BlockExt instead of widgets::block::BlockExtThis is a breaking change.
LineGauge now support customizable symbols via LineGauge::filled_symbol and
LineGauge::unfilled_symbol methods:
let gauge = LineGauge::default()
.filled_symbol("โ")
.unfilled_symbol("โ")
.ratio(0.80);
80% โโโโโโโโโโโโโโโโ
LineGauge::line_set method is now deprecated.
List::highlight_symbol now accepts Into<Line> instead of &str.
This makes it possible to customize the highlight symbol as follows:
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.
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!
let mascot = RatatuiMascot::new().set_eye(MascotEyeColor::Red);
โโโโโ
โโโโโโโโ
โโโโโโโโโโ
โโโโโโโโโโโโ
โโโโโโโโโโโโโ โโโโโโโโ
โโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโ
โโโโ โโโโโโโโโ
โโ โ โโโโโโโโโโโโ
โโ โโ โโโโโโโโโโ
โโ โโ โโโโโโโโโโโโ
โโ โโ โโโโโโ โโโ
โ โโโ โโโ
โโ โโโโ
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.
AddAssign for TextText now implements AddAssign trait.
This makes it possible to add a second Text instance to a first one using the += operator.
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).
Don't render control characters for
Span
Implement UnicodeWidthStr for Text/Line/Span for retrieving the width via width and
width_cjk
anstyleSupport conversions from anstyle styles (gated behind anstyle
feature):
let anstyle_color = anstyle::Ansi256Color(42);
let color = Color::from(anstyle_color);
Added generic color conversion methods from tuples:
Color::from([255, 0, 0]);
Color::from((255, 0, 0));
Color::from([255, 0, 0, 255]);
Color::from((255, 0, 0, 255));
Implement Styled for primitives such as u8, i32, f64, Cow<'a, str>, etc.
let s = Cow::Borrowed("a");
assert_eq!(s.red(), "a".red());
StyleThis makes it possible to create constants using the shorthand methods.
const MY_STYLE: Style = Style::new().blue().on_black();
This is a breaking change.
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:
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.
// 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.
let areas_vec = area.layout_vec(&layout);
RectsFor centering:
let area = frame
.area()
.centered(Constraint::Ratio(1, 2), Constraint::Ratio(1, 3));
Or for vertical centering:
let area = frame.area().centered_vertically(Constraint::Ratio(1, 2));
Horizontally centering:
let area = frame.area().centered_horizontally(Constraint::Length(3));
Rect::outer methodThis creates a new Rect outside the current one, with the given margin applied on each side.
Also added VerticalAlignment type.
Flex::SpaceEvenlyOld 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:
Alignment to HorizontalAlignment to better reflect its purpose- 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
The From implementations for backend types are now replaced with more specific traits.
This effects the styling conversions such as Color:
+ 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.
Error type and required clear_region methodCustom 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.
- 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.
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.
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.
TestBackend now uses core::convert::Infallible for error handling instead of std::io::Error
State associated types are now ?SizedStatefulWidget::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.
WidgetRef traitWidgetRef 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.
-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.
FrameExt traitTo 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.
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);
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.
- 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.
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:
[](https://ratatui.rs/)
If you want a custom badge, Ratatui logo is also available on shields.io! Some examples are:





Constraint, Direction, Spacing, Layout,
AccentedPalette, NonAccentedPalette, Palette, Padding, Borders, BorderType,
ListDirection, ScrollbarOrientation, ScrollDirection, RenderDirection, and
HighlightSpacing, HorizontalAlignment, VerticalAlignmentStyle deserializationCell::symbol to Option<CompactString> to better represent empty cellsBlock symbols in Chart and Canvas"Rats don't just survive; they discover; they create. ... I mean, just look at what they do with the terminal!" โ Remy & Orhun