src/content/docs/concepts/no-std.md
:::note
no_std is an attribute that disables Rust's standard library.
In this mode your code can only uses a limited core crate instead of std, so there is no
operating system integration, file I/O, threads, or heap allocator unless you provide one.
:::
Ratatui is no_std-compatible, which allows it to run in embedded and other resource-constrained
environments. This means you can run TUIs on a wider range of targets and have widget usable in both
desktop and embedded environments.
stdstd-only dependencies:ratatui = { version = "0.30", default-features = false }
:::note
You can re-enable only the features you need that don't depend on the standard library. (e.g.
macros, all-widgets, etc.)
Skip features that explicitly require std such as crossterm, serde and so on.
:::
:::caution
On targets without atomic instructions, you must also enable portable-atomic to provide software
atomics:
ratatui = { version = "0.30", default-features = false, features = ["portable-atomic"] }
Additionally, depending on your target, you will have to enable one of the portable-atomic
features. Refer to the portable-atomic crate documentation for
more information.
:::
The built-in backends rely on std, so no_std targets need a custom backend that implements
ratatui::backend::Backend or a third-party option like mousefood 🧀
no_std build, compile with a no_std target.For example, on ESP32:
cargo check --target riscv32imc-unknown-none-elf
no_std widgetsIf you already have a Ratatui widget, you can make it no_std-compatible with a few small changes.
Even if you haven't built for embedded before!
:::tip[Why no_std widgets?]
The same widget can be reused in a terminal app on Linux, a dashboard on a microcontroller, or a web app compiled to WebAssembly. Furthermore, it will be smaller (binary size) and more predictable across platforms because it doesn't pull in extra OS dependencies and only relies on core Rust features.
:::
Opt into no_std and add alloc crate:
// lib.rs
#![no_std]
extern crate alloc;
Depend on ratatui-core instead of the full ratatui crate to avoid backend dependencies:
ratatui-core = { version = "0.1", default-features = false }
Swap std types for their core/alloc equivalents, for example core::fmt,
alloc::string::String, alloc::vec::Vec, and alloc::boxed::Box.
Keep a std feature (off by default) for conveniences like tests or examples, but write your
core widget logic so it also works without it.
Avoid std-only APIs in widget code paths. Examples: use core::time::Duration instead of
std::time::Duration, pass in data rather than reading files, and keep logging behind a feature
so it can be disabled on targets without I/O.
Here is a minimal no_std widget implementation:
#![no_std]
extern crate alloc;
use alloc::string::String;
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::text::Line;
use ratatui_core::widgets::Widget;
struct Greeting {
message: String,
}
impl Widget for &Greeting {
fn render(self, area: Rect, buf: &mut Buffer) {
Line::raw(&self.message).render(area, buf);
}
}
:::note
Some tips for testing and maintaining no_std compatibility:
cargo check --no-default-features (optionally with a no_std target) to catch regressions.no_std-compatible so users know what to enable.cfg(feature = "std") to layer in extra features (e.g. logging)
without breaking no_std.:::