Struct Block

Source
pub struct Block<'a> { /* private fields */ }
Expand description

Base widget to be used to display a box border around all upper level ones.

The borders can be configured with Block::borders and others. A block can have multiple Title using Block::title. It can also be styled and padded.

You can call the title methods multiple times to add multiple titles. Each title will be rendered with a single space separating titles that are in the same position or alignment. When both centered and non-centered titles are rendered, the centered space is calculated based on the full width of the block, rather than the leftover width.

Titles are not rendered in the corners of the block unless there is no border on that edge. If the block is too small and multiple titles overlap, the border may get cut off at a corner.

┌With at least a left border───

Without left border───

§Constructor methods

§Setter methods

These methods are fluent setters. They return a new Block with the specified property set.

§Other Methods

  • Block::inner Compute the inner area of a block based on its border visibility rules.

Styles are applied first to the entire block, then to the borders, and finally to the titles. If the block is used as a container for another widget, the inner widget can also be styled. See Style for more information on how merging styles works.

§Examples

use ratatui::{
    style::{Color, Style},
    widgets::{Block, BorderType, Borders},
};

Block::new()
    .border_type(BorderType::Rounded)
    .borders(Borders::LEFT | Borders::RIGHT)
    .border_style(Style::default().fg(Color::White))
    .style(Style::default().bg(Color::Black))
    .title("Block");

You may also use multiple titles like in the following:

use ratatui::widgets::{
    block::{Position, Title},
    Block,
};

Block::new()
    .title("Title 1")
    .title(Title::from("Title 2").position(Position::Bottom));

You can also pass it as parameters of another widget so that the block surrounds them:

use ratatui::widgets::{Block, Borders, List};

let surrounding_block = Block::default()
    .borders(Borders::ALL)
    .title("Here is a list of items");
let items = ["Item 1", "Item 2", "Item 3"];
let list = List::new(items).block(surrounding_block);

Implementations§

Source§

impl<'a> Block<'a>

Source

pub const fn new() -> Self

Creates a new block with no Borders or Padding.

Examples found in repository?
examples/gauge.rs (line 200)
198fn title_block(title: &str) -> Block {
199    let title = Line::from(title).centered();
200    Block::new()
201        .borders(Borders::NONE)
202        .padding(Padding::vertical(1))
203        .title(title)
204        .fg(CUSTOM_LABEL_COLOR)
205}
More examples
Hide additional examples
examples/block.rs (line 108)
107fn render_borders(paragraph: &Paragraph, border: Borders, frame: &mut Frame, area: Rect) {
108    let block = Block::new()
109        .borders(border)
110        .title(format!("Borders::{border:#?}"));
111    frame.render_widget(paragraph.clone().block(block), area);
112}
examples/barchart.rs (line 92)
83fn vertical_barchart(temperatures: &[u8]) -> BarChart {
84    let bars: Vec<Bar> = temperatures
85        .iter()
86        .enumerate()
87        .map(|(hour, value)| vertical_bar(hour, value))
88        .collect();
89    let title = Line::from("Weather (Vertical)").centered();
90    BarChart::default()
91        .data(BarGroup::default().bars(&bars))
92        .block(Block::new().title(title))
93        .bar_width(5)
94}
95
96fn vertical_bar(hour: usize, temperature: &u8) -> Bar {
97    Bar::default()
98        .value(u64::from(*temperature))
99        .label(Line::from(format!("{hour:>02}:00")))
100        .text_value(format!("{temperature:>3}°"))
101        .style(temperature_style(*temperature))
102        .value_style(temperature_style(*temperature).reversed())
103}
104
105/// Create a horizontal bar chart from the temperatures data.
106fn horizontal_barchart(temperatures: &[u8]) -> BarChart {
107    let bars: Vec<Bar> = temperatures
108        .iter()
109        .enumerate()
110        .map(|(hour, value)| horizontal_bar(hour, value))
111        .collect();
112    let title = Line::from("Weather (Horizontal)").centered();
113    BarChart::default()
114        .block(Block::new().title(title))
115        .data(BarGroup::default().bars(&bars))
116        .bar_width(1)
117        .bar_gap(0)
118        .direction(Direction::Horizontal)
119}
examples/constraint-explorer.rs (line 428)
420    fn render_1px(&self, area: Rect, buf: &mut Buffer) {
421        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
422        let main_color = ConstraintName::from(self.constraint).color();
423        let selected_color = if self.selected {
424            lighter_color
425        } else {
426            main_color
427        };
428        Block::new()
429            .fg(Self::TEXT_COLOR)
430            .bg(selected_color)
431            .render(area, buf);
432    }
examples/demo2/app.rs (line 149)
141    fn render(self, area: Rect, buf: &mut Buffer) {
142        let vertical = Layout::vertical([
143            Constraint::Length(1),
144            Constraint::Min(0),
145            Constraint::Length(1),
146        ]);
147        let [title_bar, tab, bottom_bar] = vertical.areas(area);
148
149        Block::new().style(THEME.root).render(area, buf);
150        self.render_title_bar(title_bar, buf);
151        self.render_selected_tab(tab, buf);
152        App::render_bottom_bar(bottom_bar, buf);
153    }
examples/flex.rs (line 275)
273    fn tabs(self) -> impl Widget {
274        let tab_titles = SelectedTab::iter().map(SelectedTab::to_tab_title);
275        let block = Block::new()
276            .title("Flex Layouts ".bold())
277            .title(" Use ◄ ► to change tab, ▲ ▼  to scroll, - + to change spacing ");
278        Tabs::new(tab_titles)
279            .block(block)
280            .highlight_style(Modifier::REVERSED)
281            .select(self.selected_tab as usize)
282            .divider(" ")
283            .padding("", "")
284    }
Source

pub const fn bordered() -> Self

Create a new block with all borders shown

use ratatui::widgets::{Block, Borders};

assert_eq!(Block::bordered(), Block::new().borders(Borders::ALL));
Examples found in repository?
examples/paragraph.rs (line 122)
121fn title_block(title: &str) -> Block {
122    Block::bordered()
123        .gray()
124        .title(title.bold().into_centered_line())
125}
More examples
Hide additional examples
examples/tabs.rs (line 202)
201    fn block(self) -> Block<'static> {
202        Block::bordered()
203            .border_set(symbols::border::PROPORTIONAL_TALL)
204            .padding(Padding::horizontal(1))
205            .border_style(self.palette().c700)
206    }
examples/block.rs (line 120)
114fn render_border_type(
115    paragraph: &Paragraph,
116    border_type: BorderType,
117    frame: &mut Frame,
118    area: Rect,
119) {
120    let block = Block::bordered()
121        .border_type(border_type)
122        .title(format!("BorderType::{border_type:#?}"));
123    frame.render_widget(paragraph.clone().block(block), area);
124}
125fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
126    let block = Block::bordered()
127        .border_style(Style::new().blue().on_white().bold().italic())
128        .title("Styled borders");
129    frame.render_widget(paragraph.clone().block(block), area);
130}
131
132fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
133    let block = Block::bordered()
134        .style(Style::new().blue().on_white().bold().italic())
135        .title("Styled block");
136    frame.render_widget(paragraph.clone().block(block), area);
137}
138
139fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
140    let block = Block::bordered()
141        .title("Styled title")
142        .title_style(Style::new().blue().on_white().bold().italic());
143    frame.render_widget(paragraph.clone().block(block), area);
144}
145
146fn render_styled_title_content(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
147    let title = Line::from(vec![
148        "Styled ".blue().on_white().bold().italic(),
149        "title content".red().on_white().bold().italic(),
150    ]);
151    let block = Block::bordered().title(title);
152    frame.render_widget(paragraph.clone().block(block), area);
153}
154
155fn render_multiple_titles(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
156    let block = Block::bordered()
157        .title("Multiple".blue().on_white().bold().italic())
158        .title("Titles".red().on_white().bold().italic());
159    frame.render_widget(paragraph.clone().block(block), area);
160}
161
162fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
163    let block = Block::bordered()
164        .title(Line::from("top left").left_aligned())
165        .title(Line::from("top center").centered())
166        .title(Line::from("top right").right_aligned())
167        .title_bottom(Line::from("bottom left").left_aligned())
168        .title_bottom(Line::from("bottom center").centered())
169        .title_bottom(Line::from("bottom right").right_aligned());
170    frame.render_widget(paragraph.clone().block(block), area);
171}
172
173fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
174    let block = Block::bordered()
175        .padding(Padding::new(5, 10, 1, 2))
176        .title("Padding");
177    frame.render_widget(paragraph.clone().block(block), area);
178}
179
180fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
181    let outer_block = Block::bordered().title("Outer block");
182    let inner_block = Block::bordered().title("Inner block");
183    let inner = outer_block.inner(area);
184    frame.render_widget(outer_block, area);
185    frame.render_widget(paragraph.clone().block(inner_block), inner);
186}
examples/canvas.rs (line 178)
176    fn map_canvas(&self) -> impl Widget + '_ {
177        Canvas::default()
178            .block(Block::bordered().title("World"))
179            .marker(self.marker)
180            .paint(|ctx| {
181                ctx.draw(&Map {
182                    color: Color::Green,
183                    resolution: MapResolution::High,
184                });
185                ctx.print(self.x, -self.y, "You are here".yellow());
186            })
187            .x_bounds([-180.0, 180.0])
188            .y_bounds([-90.0, 90.0])
189    }
190
191    fn draw_canvas(&self, area: Rect) -> impl Widget + '_ {
192        Canvas::default()
193            .block(Block::bordered().title("Draw here"))
194            .marker(self.marker)
195            .x_bounds([0.0, f64::from(area.width)])
196            .y_bounds([0.0, f64::from(area.height)])
197            .paint(move |ctx| {
198                let points = self
199                    .points
200                    .iter()
201                    .map(|p| {
202                        (
203                            f64::from(p.x) - f64::from(area.left()),
204                            f64::from(area.bottom()) - f64::from(p.y),
205                        )
206                    })
207                    .collect_vec();
208                ctx.draw(&Points {
209                    coords: &points,
210                    color: Color::White,
211                });
212            })
213    }
214
215    fn pong_canvas(&self) -> impl Widget + '_ {
216        Canvas::default()
217            .block(Block::bordered().title("Pong"))
218            .marker(self.marker)
219            .paint(|ctx| {
220                ctx.draw(&self.ball);
221            })
222            .x_bounds([10.0, 210.0])
223            .y_bounds([10.0, 110.0])
224    }
225
226    fn boxes_canvas(&self, area: Rect) -> impl Widget {
227        let left = 0.0;
228        let right = f64::from(area.width);
229        let bottom = 0.0;
230        let top = f64::from(area.height).mul_add(2.0, -4.0);
231        Canvas::default()
232            .block(Block::bordered().title("Rects"))
233            .marker(self.marker)
234            .x_bounds([left, right])
235            .y_bounds([bottom, top])
236            .paint(|ctx| {
237                for i in 0..=11 {
238                    ctx.draw(&Rectangle {
239                        x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
240                        y: 2.0,
241                        width: f64::from(i),
242                        height: f64::from(i),
243                        color: Color::Red,
244                    });
245                    ctx.draw(&Rectangle {
246                        x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
247                        y: 21.0,
248                        width: f64::from(i),
249                        height: f64::from(i),
250                        color: Color::Blue,
251                    });
252                }
253                for i in 0..100 {
254                    if i % 10 != 0 {
255                        ctx.print(f64::from(i) + 1.0, 0.0, format!("{i}", i = i % 10));
256                    }
257                    if i % 2 == 0 && i % 10 != 0 {
258                        ctx.print(0.0, f64::from(i), format!("{i}", i = i % 10));
259                    }
260                }
261            })
262    }
examples/constraint-explorer.rs (line 442)
434    fn render_2px(&self, area: Rect, buf: &mut Buffer) {
435        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
436        let main_color = ConstraintName::from(self.constraint).color();
437        let selected_color = if self.selected {
438            lighter_color
439        } else {
440            main_color
441        };
442        Block::bordered()
443            .border_set(symbols::border::QUADRANT_OUTSIDE)
444            .border_style(Style::reset().fg(selected_color).reversed())
445            .render(area, buf);
446    }
447
448    fn render_4px(&self, area: Rect, buf: &mut Buffer) {
449        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
450        let main_color = ConstraintName::from(self.constraint).color();
451        let selected_color = if self.selected {
452            lighter_color
453        } else {
454            main_color
455        };
456        let color = if self.legend {
457            selected_color
458        } else {
459            main_color
460        };
461        let label = self.label(area.width);
462        let block = Block::bordered()
463            .border_set(symbols::border::QUADRANT_OUTSIDE)
464            .border_style(Style::reset().fg(color).reversed())
465            .fg(Self::TEXT_COLOR)
466            .bg(color);
467        Paragraph::new(label)
468            .centered()
469            .fg(Self::TEXT_COLOR)
470            .bg(color)
471            .block(block)
472            .render(area, buf);
473
474        if !self.legend {
475            let border_color = if self.selected {
476                lighter_color
477            } else {
478                main_color
479            };
480            if let Some(last_row) = area.rows().last() {
481                buf.set_style(last_row, border_color);
482            }
483        }
484    }
485}
486
487impl Widget for SpacerBlock {
488    fn render(self, area: Rect, buf: &mut Buffer) {
489        match area.height {
490            1 => (),
491            2 => Self::render_2px(area, buf),
492            3 => Self::render_3px(area, buf),
493            _ => Self::render_4px(area, buf),
494        }
495    }
496}
497
498impl SpacerBlock {
499    const TEXT_COLOR: Color = SLATE.c500;
500    const BORDER_COLOR: Color = SLATE.c600;
501
502    /// A block with a corner borders
503    fn block() -> impl Widget {
504        let corners_only = symbols::border::Set {
505            top_left: line::NORMAL.top_left,
506            top_right: line::NORMAL.top_right,
507            bottom_left: line::NORMAL.bottom_left,
508            bottom_right: line::NORMAL.bottom_right,
509            vertical_left: " ",
510            vertical_right: " ",
511            horizontal_top: " ",
512            horizontal_bottom: " ",
513        };
514        Block::bordered()
515            .border_set(corners_only)
516            .border_style(Self::BORDER_COLOR)
517    }
examples/table.rs (line 292)
283    fn render_footer(&self, frame: &mut Frame, area: Rect) {
284        let info_footer = Paragraph::new(Text::from_iter(INFO_TEXT))
285            .style(
286                Style::new()
287                    .fg(self.colors.row_fg)
288                    .bg(self.colors.buffer_bg),
289            )
290            .centered()
291            .block(
292                Block::bordered()
293                    .border_type(BorderType::Double)
294                    .border_style(Style::new().fg(self.colors.footer_border_color)),
295            );
296        frame.render_widget(info_footer, area);
297    }
Source

pub fn title<T>(self, title: T) -> Self
where T: Into<Title<'a>>,

Adds a title to the block.

The title function allows you to add a title to the block. You can call this function multiple times to add multiple titles.

Each title will be rendered with a single space separating titles that are in the same position or alignment. When both centered and non-centered titles are rendered, the centered space is calculated based on the full width of the block, rather than the leftover width.

You can provide any type that can be converted into Title including: strings, string slices (&str), borrowed strings (Cow<str>), spans, or vectors of spans (Vec<Span>).

By default, the titles will avoid being rendered in the corners of the block but will align against the left or right edge of the block if there is no border on that edge. The following demonstrates this behavior, notice the second title is one character off to the left.

┌With at least a left border───

Without left border───

Note: If the block is too small and multiple titles overlap, the border might get cut off at a corner.

§Examples

See the Block example for a visual representation of how the various borders and styles look when rendered.

The following example demonstrates:

  • Default title alignment
  • Multiple titles (notice “Center” is centered according to the full with of the block, not the leftover space)
  • Two titles with the same alignment (notice the left titles are separated)
use ratatui::{
    text::Line,
    widgets::{Block, Borders},
};

Block::new()
    .title("Title") // By default in the top left corner
    .title(Line::from("Left").left_aligned()) // also on the left
    .title(Line::from("Right").right_aligned())
    .title(Line::from("Center").centered());
// Renders
// ┌Title─Left────Center─────────Right┐
§See also

Titles attached to a block can have default behaviors. See

§Future improvements

In a future release of Ratatui this method will be changed to accept Into<Line> instead of Into<Title>. This allows us to remove the unnecessary Title struct and store the position in the block itself. For more information see https://github.com/ratatui/ratatui/issues/738.

Examples found in repository?
examples/paragraph.rs (line 124)
121fn title_block(title: &str) -> Block {
122    Block::bordered()
123        .gray()
124        .title(title.bold().into_centered_line())
125}
More examples
Hide additional examples
examples/line_gauge.rs (line 175)
173fn title_block(title: &str) -> Block {
174    Block::default()
175        .title(Line::from(title).centered())
176        .borders(Borders::NONE)
177        .fg(CUSTOM_LABEL_COLOR)
178        .padding(Padding::vertical(1))
179}
examples/gauge.rs (line 203)
198fn title_block(title: &str) -> Block {
199    let title = Line::from(title).centered();
200    Block::new()
201        .borders(Borders::NONE)
202        .padding(Padding::vertical(1))
203        .title(title)
204        .fg(CUSTOM_LABEL_COLOR)
205}
examples/block.rs (line 110)
107fn render_borders(paragraph: &Paragraph, border: Borders, frame: &mut Frame, area: Rect) {
108    let block = Block::new()
109        .borders(border)
110        .title(format!("Borders::{border:#?}"));
111    frame.render_widget(paragraph.clone().block(block), area);
112}
113
114fn render_border_type(
115    paragraph: &Paragraph,
116    border_type: BorderType,
117    frame: &mut Frame,
118    area: Rect,
119) {
120    let block = Block::bordered()
121        .border_type(border_type)
122        .title(format!("BorderType::{border_type:#?}"));
123    frame.render_widget(paragraph.clone().block(block), area);
124}
125fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
126    let block = Block::bordered()
127        .border_style(Style::new().blue().on_white().bold().italic())
128        .title("Styled borders");
129    frame.render_widget(paragraph.clone().block(block), area);
130}
131
132fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
133    let block = Block::bordered()
134        .style(Style::new().blue().on_white().bold().italic())
135        .title("Styled block");
136    frame.render_widget(paragraph.clone().block(block), area);
137}
138
139fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
140    let block = Block::bordered()
141        .title("Styled title")
142        .title_style(Style::new().blue().on_white().bold().italic());
143    frame.render_widget(paragraph.clone().block(block), area);
144}
145
146fn render_styled_title_content(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
147    let title = Line::from(vec![
148        "Styled ".blue().on_white().bold().italic(),
149        "title content".red().on_white().bold().italic(),
150    ]);
151    let block = Block::bordered().title(title);
152    frame.render_widget(paragraph.clone().block(block), area);
153}
154
155fn render_multiple_titles(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
156    let block = Block::bordered()
157        .title("Multiple".blue().on_white().bold().italic())
158        .title("Titles".red().on_white().bold().italic());
159    frame.render_widget(paragraph.clone().block(block), area);
160}
161
162fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
163    let block = Block::bordered()
164        .title(Line::from("top left").left_aligned())
165        .title(Line::from("top center").centered())
166        .title(Line::from("top right").right_aligned())
167        .title_bottom(Line::from("bottom left").left_aligned())
168        .title_bottom(Line::from("bottom center").centered())
169        .title_bottom(Line::from("bottom right").right_aligned());
170    frame.render_widget(paragraph.clone().block(block), area);
171}
172
173fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
174    let block = Block::bordered()
175        .padding(Padding::new(5, 10, 1, 2))
176        .title("Padding");
177    frame.render_widget(paragraph.clone().block(block), area);
178}
179
180fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
181    let outer_block = Block::bordered().title("Outer block");
182    let inner_block = Block::bordered().title("Inner block");
183    let inner = outer_block.inner(area);
184    frame.render_widget(outer_block, area);
185    frame.render_widget(paragraph.clone().block(inner_block), inner);
186}
examples/barchart.rs (line 92)
83fn vertical_barchart(temperatures: &[u8]) -> BarChart {
84    let bars: Vec<Bar> = temperatures
85        .iter()
86        .enumerate()
87        .map(|(hour, value)| vertical_bar(hour, value))
88        .collect();
89    let title = Line::from("Weather (Vertical)").centered();
90    BarChart::default()
91        .data(BarGroup::default().bars(&bars))
92        .block(Block::new().title(title))
93        .bar_width(5)
94}
95
96fn vertical_bar(hour: usize, temperature: &u8) -> Bar {
97    Bar::default()
98        .value(u64::from(*temperature))
99        .label(Line::from(format!("{hour:>02}:00")))
100        .text_value(format!("{temperature:>3}°"))
101        .style(temperature_style(*temperature))
102        .value_style(temperature_style(*temperature).reversed())
103}
104
105/// Create a horizontal bar chart from the temperatures data.
106fn horizontal_barchart(temperatures: &[u8]) -> BarChart {
107    let bars: Vec<Bar> = temperatures
108        .iter()
109        .enumerate()
110        .map(|(hour, value)| horizontal_bar(hour, value))
111        .collect();
112    let title = Line::from("Weather (Horizontal)").centered();
113    BarChart::default()
114        .block(Block::new().title(title))
115        .data(BarGroup::default().bars(&bars))
116        .bar_width(1)
117        .bar_gap(0)
118        .direction(Direction::Horizontal)
119}
examples/flex.rs (line 276)
273    fn tabs(self) -> impl Widget {
274        let tab_titles = SelectedTab::iter().map(SelectedTab::to_tab_title);
275        let block = Block::new()
276            .title("Flex Layouts ".bold())
277            .title(" Use ◄ ► to change tab, ▲ ▼  to scroll, - + to change spacing ");
278        Tabs::new(tab_titles)
279            .block(block)
280            .highlight_style(Modifier::REVERSED)
281            .select(self.selected_tab as usize)
282            .divider(" ")
283            .padding("", "")
284    }
Source

pub fn title_top<T: Into<Line<'a>>>(self, title: T) -> Self

Adds a title to the top of the block.

You can provide any type that can be converted into Line including: strings, string slices (&str), borrowed strings (Cow<str>), spans, or vectors of spans (Vec<Span>).

§Example
use ratatui::{ widgets::Block, text::Line };

Block::bordered()
    .title_top("Left1") // By default in the top left corner
    .title_top(Line::from("Left2").left_aligned())
    .title_top(Line::from("Right").right_aligned())
    .title_top(Line::from("Center").centered());

// Renders
// ┌Left1─Left2───Center─────────Right┐
// │                                  │
// └──────────────────────────────────┘
Examples found in repository?
examples/chart.rs (line 199)
178fn render_barchart(frame: &mut Frame, bar_chart: Rect) {
179    let dataset = Dataset::default()
180        .marker(symbols::Marker::HalfBlock)
181        .style(Style::new().fg(Color::Blue))
182        .graph_type(GraphType::Bar)
183        // a bell curve
184        .data(&[
185            (0., 0.4),
186            (10., 2.9),
187            (20., 13.5),
188            (30., 41.1),
189            (40., 80.1),
190            (50., 100.0),
191            (60., 80.1),
192            (70., 41.1),
193            (80., 13.5),
194            (90., 2.9),
195            (100., 0.4),
196        ]);
197
198    let chart = Chart::new(vec![dataset])
199        .block(Block::bordered().title_top(Line::from("Bar chart").cyan().bold().centered()))
200        .x_axis(
201            Axis::default()
202                .style(Style::default().gray())
203                .bounds([0.0, 100.0])
204                .labels(["0".bold(), "50".into(), "100.0".bold()]),
205        )
206        .y_axis(
207            Axis::default()
208                .style(Style::default().gray())
209                .bounds([0.0, 100.0])
210                .labels(["0".bold(), "50".into(), "100.0".bold()]),
211        )
212        .hidden_legend_constraints((Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)));
213
214    frame.render_widget(chart, bar_chart);
215}
Source

pub fn title_bottom<T: Into<Line<'a>>>(self, title: T) -> Self

Adds a title to the bottom of the block.

You can provide any type that can be converted into Line including: strings, string slices (&str), borrowed strings (Cow<str>), spans, or vectors of spans (Vec<Span>).

§Example
use ratatui::{ widgets::Block, text::Line };

Block::bordered()
    .title_bottom("Left1") // By default in the top left corner
    .title_bottom(Line::from("Left2").left_aligned())
    .title_bottom(Line::from("Right").right_aligned())
    .title_bottom(Line::from("Center").centered());

// Renders
// ┌──────────────────────────────────┐
// │                                  │
// └Left1─Left2───Center─────────Right┘
Examples found in repository?
examples/block.rs (line 167)
162fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
163    let block = Block::bordered()
164        .title(Line::from("top left").left_aligned())
165        .title(Line::from("top center").centered())
166        .title(Line::from("top right").right_aligned())
167        .title_bottom(Line::from("bottom left").left_aligned())
168        .title_bottom(Line::from("bottom center").centered())
169        .title_bottom(Line::from("bottom right").right_aligned());
170    frame.render_widget(paragraph.clone().block(block), area);
171}
More examples
Hide additional examples
examples/async.rs (line 234)
226    fn render(self, area: Rect, buf: &mut Buffer) {
227        let mut state = self.state.write().unwrap();
228
229        // a block with a right aligned title with the loading state on the right
230        let loading_state = Line::from(format!("{:?}", state.loading_state)).right_aligned();
231        let block = Block::bordered()
232            .title("Pull Requests")
233            .title(loading_state)
234            .title_bottom("j/k to scroll, q to quit");
235
236        // a table with the list of pull requests
237        let rows = state.pull_requests.iter();
238        let widths = [
239            Constraint::Length(5),
240            Constraint::Fill(1),
241            Constraint::Max(49),
242        ];
243        let table = Table::new(rows, widths)
244            .block(block)
245            .highlight_spacing(HighlightSpacing::Always)
246            .highlight_symbol(">>")
247            .row_highlight_style(Style::new().on_blue());
248
249        StatefulWidget::render(table, area, buf, &mut state.table_state);
250    }
Source

pub fn title_style<S: Into<Style>>(self, style: S) -> Self

Applies the style to all titles.

This style will be applied to all titles of the block. If a title has a style set, it will be applied after this style. This style will be applied after any Block::style or Block::border_style is applied.

See Style for more information on how merging styles works.

style accepts any type that is convertible to Style (e.g. Style, Color, or your own type that implements Into<Style>).

Examples found in repository?
examples/block.rs (line 142)
139fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
140    let block = Block::bordered()
141        .title("Styled title")
142        .title_style(Style::new().blue().on_white().bold().italic());
143    frame.render_widget(paragraph.clone().block(block), area);
144}
Source

pub const fn title_alignment(self, alignment: Alignment) -> Self

Sets the default Alignment for all block titles.

Titles that explicitly set an Alignment will ignore this.

§Example

This example aligns all titles in the center except the “right” title which explicitly sets Alignment::Right.

use ratatui::{layout::Alignment, text::Line, widgets::Block};

Block::new()
    .title_alignment(Alignment::Center)
    // This title won't be aligned in the center
    .title(Line::from("right").right_aligned())
    .title("foo")
    .title("bar");
Examples found in repository?
examples/demo2/tabs/about.rs (line 90)
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71    let area = area.inner(Margin {
72        vertical: 4,
73        horizontal: 2,
74    });
75    Clear.render(area, buf); // clear out the color swatches
76    Block::new().style(THEME.content).render(area, buf);
77    let area = area.inner(Margin {
78        vertical: 1,
79        horizontal: 2,
80    });
81    let text = "- cooking up terminal user interfaces -
82
83    Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84    screen efficiently every frame.";
85    Paragraph::new(text)
86        .style(THEME.description)
87        .block(
88            Block::new()
89                .title(" Ratatui ")
90                .title_alignment(Alignment::Center)
91                .borders(Borders::TOP)
92                .border_style(THEME.description_title)
93                .padding(Padding::new(0, 0, 0, 0)),
94        )
95        .wrap(Wrap { trim: true })
96        .scroll((0, 0))
97        .render(area, buf);
98}
More examples
Hide additional examples
examples/demo2/tabs/recipe.rs (line 124)
115    fn render(self, area: Rect, buf: &mut Buffer) {
116        RgbSwatch.render(area, buf);
117        let area = area.inner(Margin {
118            vertical: 1,
119            horizontal: 2,
120        });
121        Clear.render(area, buf);
122        Block::new()
123            .title("Ratatouille Recipe".bold().white())
124            .title_alignment(Alignment::Center)
125            .style(THEME.content)
126            .padding(Padding::new(1, 1, 2, 1))
127            .render(area, buf);
128
129        let scrollbar_area = Rect {
130            y: area.y + 2,
131            height: area.height - 3,
132            ..area
133        };
134        render_scrollbar(self.row_index, scrollbar_area, buf);
135
136        let area = area.inner(Margin {
137            horizontal: 2,
138            vertical: 1,
139        });
140        let [recipe, ingredients] =
141            Layout::horizontal([Constraint::Length(44), Constraint::Min(0)]).areas(area);
142
143        render_recipe(recipe, buf);
144        render_ingredients(self.row_index, ingredients, buf);
145    }
examples/demo2/tabs/traceroute.rs (line 58)
53fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
54    let mut state = TableState::default().with_selected(Some(selected_row));
55    let rows = HOPS.iter().map(|hop| Row::new(vec![hop.host, hop.address]));
56    let block = Block::new()
57        .padding(Padding::new(1, 1, 1, 1))
58        .title_alignment(Alignment::Center)
59        .title("Traceroute bad.horse".bold().white());
60    StatefulWidget::render(
61        Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
62            .header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
63            .row_highlight_style(THEME.traceroute.selected)
64            .block(block),
65        area,
66        buf,
67        &mut state,
68    );
69    let mut scrollbar_state = ScrollbarState::default()
70        .content_length(HOPS.len())
71        .position(selected_row);
72    let area = Rect {
73        width: area.width + 1,
74        y: area.y + 3,
75        height: area.height - 4,
76        ..area
77    };
78    Scrollbar::default()
79        .orientation(ScrollbarOrientation::VerticalLeft)
80        .begin_symbol(None)
81        .end_symbol(None)
82        .track_symbol(None)
83        .thumb_symbol("▌")
84        .render(area, buf, &mut scrollbar_state);
85}
86
87pub fn render_ping(progress: usize, area: Rect, buf: &mut Buffer) {
88    let mut data = [
89        8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 8, 8, 7,
90        7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 2, 4, 6, 7, 8, 8, 8, 8, 6, 4, 2, 1, 1, 1, 1, 2, 2, 2, 3,
91        3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
92    ];
93    let mid = progress % data.len();
94    data.rotate_left(mid);
95    Sparkline::default()
96        .block(
97            Block::new()
98                .title("Ping")
99                .title_alignment(Alignment::Center)
100                .border_type(BorderType::Thick),
101        )
102        .data(data)
103        .style(THEME.traceroute.ping)
104        .render(area, buf);
105}
examples/scrollbar.rs (line 138)
92    fn draw(&mut self, frame: &mut Frame) {
93        let area = frame.area();
94
95        // Words made "loooong" to demonstrate line breaking.
96        let s =
97            "Veeeeeeeeeeeeeeeery    loooooooooooooooooong   striiiiiiiiiiiiiiiiiiiiiiiiiing.   ";
98        let mut long_line = s.repeat(usize::from(area.width) / s.len() + 4);
99        long_line.push('\n');
100
101        let chunks = Layout::vertical([
102            Constraint::Min(1),
103            Constraint::Percentage(25),
104            Constraint::Percentage(25),
105            Constraint::Percentage(25),
106            Constraint::Percentage(25),
107        ])
108        .split(area);
109
110        let text = vec![
111            Line::from("This is a line "),
112            Line::from("This is a line   ".red()),
113            Line::from("This is a line".on_dark_gray()),
114            Line::from("This is a longer line".crossed_out()),
115            Line::from(long_line.clone()),
116            Line::from("This is a line".reset()),
117            Line::from(vec![
118                Span::raw("Masked text: "),
119                Span::styled(Masked::new("password", '*'), Style::new().fg(Color::Red)),
120            ]),
121            Line::from("This is a line "),
122            Line::from("This is a line   ".red()),
123            Line::from("This is a line".on_dark_gray()),
124            Line::from("This is a longer line".crossed_out()),
125            Line::from(long_line.clone()),
126            Line::from("This is a line".reset()),
127            Line::from(vec![
128                Span::raw("Masked text: "),
129                Span::styled(Masked::new("password", '*'), Style::new().fg(Color::Red)),
130            ]),
131        ];
132        self.vertical_scroll_state = self.vertical_scroll_state.content_length(text.len());
133        self.horizontal_scroll_state = self.horizontal_scroll_state.content_length(long_line.len());
134
135        let create_block = |title: &'static str| Block::bordered().gray().title(title.bold());
136
137        let title = Block::new()
138            .title_alignment(Alignment::Center)
139            .title("Use h j k l or ◄ ▲ ▼ ► to scroll ".bold());
140        frame.render_widget(title, chunks[0]);
141
142        let paragraph = Paragraph::new(text.clone())
143            .gray()
144            .block(create_block("Vertical scrollbar with arrows"))
145            .scroll((self.vertical_scroll as u16, 0));
146        frame.render_widget(paragraph, chunks[1]);
147        frame.render_stateful_widget(
148            Scrollbar::new(ScrollbarOrientation::VerticalRight)
149                .begin_symbol(Some("↑"))
150                .end_symbol(Some("↓")),
151            chunks[1],
152            &mut self.vertical_scroll_state,
153        );
154
155        let paragraph = Paragraph::new(text.clone())
156            .gray()
157            .block(create_block(
158                "Vertical scrollbar without arrows, without track symbol and mirrored",
159            ))
160            .scroll((self.vertical_scroll as u16, 0));
161        frame.render_widget(paragraph, chunks[2]);
162        frame.render_stateful_widget(
163            Scrollbar::new(ScrollbarOrientation::VerticalLeft)
164                .symbols(scrollbar::VERTICAL)
165                .begin_symbol(None)
166                .track_symbol(None)
167                .end_symbol(None),
168            chunks[2].inner(Margin {
169                vertical: 1,
170                horizontal: 0,
171            }),
172            &mut self.vertical_scroll_state,
173        );
174
175        let paragraph = Paragraph::new(text.clone())
176            .gray()
177            .block(create_block(
178                "Horizontal scrollbar with only begin arrow & custom thumb symbol",
179            ))
180            .scroll((0, self.horizontal_scroll as u16));
181        frame.render_widget(paragraph, chunks[3]);
182        frame.render_stateful_widget(
183            Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
184                .thumb_symbol("🬋")
185                .end_symbol(None),
186            chunks[3].inner(Margin {
187                vertical: 0,
188                horizontal: 1,
189            }),
190            &mut self.horizontal_scroll_state,
191        );
192
193        let paragraph = Paragraph::new(text.clone())
194            .gray()
195            .block(create_block(
196                "Horizontal scrollbar without arrows & custom thumb and track symbol",
197            ))
198            .scroll((0, self.horizontal_scroll as u16));
199        frame.render_widget(paragraph, chunks[4]);
200        frame.render_stateful_widget(
201            Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
202                .thumb_symbol("░")
203                .track_symbol(Some("─")),
204            chunks[4].inner(Margin {
205                vertical: 0,
206                horizontal: 1,
207            }),
208            &mut self.horizontal_scroll_state,
209        );
210    }
Source

pub const fn title_position(self, position: Position) -> Self

Sets the default Position for all block titles.

Titles that explicitly set a Position will ignore this.

§Example

This example positions all titles on the bottom except the “top” title which explicitly sets Position::Top.

use ratatui::widgets::{block::Position, Block};

Block::new()
    .title_position(Position::Bottom)
    // This title won't be aligned in the center
    .title_top("top")
    .title("foo")
    .title("bar");
Source

pub fn border_style<S: Into<Style>>(self, style: S) -> Self

Defines the style of the borders.

This style is applied only to the areas covered by borders, and is applied to the block after any Block::style is applied.

See Style for more information on how merging styles works.

style accepts any type that is convertible to Style (e.g. Style, Color, or your own type that implements Into<Style>).

§Example

This example shows a Block with blue borders.

use ratatui::{
    style::{Style, Stylize},
    widgets::Block,
};
Block::bordered().border_style(Style::new().blue());
Examples found in repository?
examples/tabs.rs (line 205)
201    fn block(self) -> Block<'static> {
202        Block::bordered()
203            .border_set(symbols::border::PROPORTIONAL_TALL)
204            .padding(Padding::horizontal(1))
205            .border_style(self.palette().c700)
206    }
More examples
Hide additional examples
examples/block.rs (line 127)
125fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
126    let block = Block::bordered()
127        .border_style(Style::new().blue().on_white().bold().italic())
128        .title("Styled borders");
129    frame.render_widget(paragraph.clone().block(block), area);
130}
examples/constraint-explorer.rs (line 444)
434    fn render_2px(&self, area: Rect, buf: &mut Buffer) {
435        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
436        let main_color = ConstraintName::from(self.constraint).color();
437        let selected_color = if self.selected {
438            lighter_color
439        } else {
440            main_color
441        };
442        Block::bordered()
443            .border_set(symbols::border::QUADRANT_OUTSIDE)
444            .border_style(Style::reset().fg(selected_color).reversed())
445            .render(area, buf);
446    }
447
448    fn render_4px(&self, area: Rect, buf: &mut Buffer) {
449        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
450        let main_color = ConstraintName::from(self.constraint).color();
451        let selected_color = if self.selected {
452            lighter_color
453        } else {
454            main_color
455        };
456        let color = if self.legend {
457            selected_color
458        } else {
459            main_color
460        };
461        let label = self.label(area.width);
462        let block = Block::bordered()
463            .border_set(symbols::border::QUADRANT_OUTSIDE)
464            .border_style(Style::reset().fg(color).reversed())
465            .fg(Self::TEXT_COLOR)
466            .bg(color);
467        Paragraph::new(label)
468            .centered()
469            .fg(Self::TEXT_COLOR)
470            .bg(color)
471            .block(block)
472            .render(area, buf);
473
474        if !self.legend {
475            let border_color = if self.selected {
476                lighter_color
477            } else {
478                main_color
479            };
480            if let Some(last_row) = area.rows().last() {
481                buf.set_style(last_row, border_color);
482            }
483        }
484    }
485}
486
487impl Widget for SpacerBlock {
488    fn render(self, area: Rect, buf: &mut Buffer) {
489        match area.height {
490            1 => (),
491            2 => Self::render_2px(area, buf),
492            3 => Self::render_3px(area, buf),
493            _ => Self::render_4px(area, buf),
494        }
495    }
496}
497
498impl SpacerBlock {
499    const TEXT_COLOR: Color = SLATE.c500;
500    const BORDER_COLOR: Color = SLATE.c600;
501
502    /// A block with a corner borders
503    fn block() -> impl Widget {
504        let corners_only = symbols::border::Set {
505            top_left: line::NORMAL.top_left,
506            top_right: line::NORMAL.top_right,
507            bottom_left: line::NORMAL.bottom_left,
508            bottom_right: line::NORMAL.bottom_right,
509            vertical_left: " ",
510            vertical_right: " ",
511            horizontal_top: " ",
512            horizontal_bottom: " ",
513        };
514        Block::bordered()
515            .border_set(corners_only)
516            .border_style(Self::BORDER_COLOR)
517    }
examples/table.rs (line 294)
283    fn render_footer(&self, frame: &mut Frame, area: Rect) {
284        let info_footer = Paragraph::new(Text::from_iter(INFO_TEXT))
285            .style(
286                Style::new()
287                    .fg(self.colors.row_fg)
288                    .bg(self.colors.buffer_bg),
289            )
290            .centered()
291            .block(
292                Block::bordered()
293                    .border_type(BorderType::Double)
294                    .border_style(Style::new().fg(self.colors.footer_border_color)),
295            );
296        frame.render_widget(info_footer, area);
297    }
examples/layout.rs (line 155)
146fn render_example_combination(
147    frame: &mut Frame,
148    area: Rect,
149    title: &str,
150    constraints: impl ExactSizeIterator<Item = (Constraint, Constraint)>,
151) {
152    let block = Block::bordered()
153        .title(title.gray())
154        .style(Style::reset())
155        .border_style(Style::default().fg(Color::DarkGray));
156    let inner = block.inner(area);
157    frame.render_widget(block, area);
158    let layout = Layout::vertical(vec![Length(1); constraints.len() + 1]).split(inner);
159    for ((a, b), &area) in constraints.into_iter().zip(layout.iter()) {
160        render_single_example(frame, area, vec![a, b, Min(0)]);
161    }
162    // This is to make it easy to visually see the alignment of the examples
163    // with the constraints.
164    frame.render_widget(Paragraph::new("123456789012"), layout[6]);
165}
examples/demo2/tabs/about.rs (line 92)
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71    let area = area.inner(Margin {
72        vertical: 4,
73        horizontal: 2,
74    });
75    Clear.render(area, buf); // clear out the color swatches
76    Block::new().style(THEME.content).render(area, buf);
77    let area = area.inner(Margin {
78        vertical: 1,
79        horizontal: 2,
80    });
81    let text = "- cooking up terminal user interfaces -
82
83    Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84    screen efficiently every frame.";
85    Paragraph::new(text)
86        .style(THEME.description)
87        .block(
88            Block::new()
89                .title(" Ratatui ")
90                .title_alignment(Alignment::Center)
91                .borders(Borders::TOP)
92                .border_style(THEME.description_title)
93                .padding(Padding::new(0, 0, 0, 0)),
94        )
95        .wrap(Wrap { trim: true })
96        .scroll((0, 0))
97        .render(area, buf);
98}
Source

pub fn style<S: Into<Style>>(self, style: S) -> Self

Defines the style of the entire block.

This is the most generic Style a block can receive, it will be merged with any other more specific styles. Elements can be styled further with Block::title_style and Block::border_style, which will be applied on top of this style. If the block is used as a container for another widget (e.g. a Paragraph), then the style of the widget is generally applied before this style.

See Style for more information on how merging styles works.

style accepts any type that is convertible to Style (e.g. Style, Color, or your own type that implements Into<Style>).

§Example
use ratatui::{
    style::{Color, Style, Stylize},
    widgets::{Block, Paragraph},
};

let block = Block::new().style(Style::new().red().on_black());

// For border and title you can additionally apply styles on top of the block level style.
let block = Block::new()
    .style(Style::new().red().bold().italic())
    .border_style(Style::new().not_italic()) // will be red and bold
    .title_style(Style::new().not_bold()) // will be red and italic
    .title("Title");

// To style the inner widget, you can style the widget itself.
let paragraph = Paragraph::new("Content")
    .block(block)
    .style(Style::new().white().not_bold()); // will be white, and italic
Examples found in repository?
examples/block.rs (line 134)
132fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
133    let block = Block::bordered()
134        .style(Style::new().blue().on_white().bold().italic())
135        .title("Styled block");
136    frame.render_widget(paragraph.clone().block(block), area);
137}
More examples
Hide additional examples
examples/demo2/app.rs (line 149)
141    fn render(self, area: Rect, buf: &mut Buffer) {
142        let vertical = Layout::vertical([
143            Constraint::Length(1),
144            Constraint::Min(0),
145            Constraint::Length(1),
146        ]);
147        let [title_bar, tab, bottom_bar] = vertical.areas(area);
148
149        Block::new().style(THEME.root).render(area, buf);
150        self.render_title_bar(title_bar, buf);
151        self.render_selected_tab(tab, buf);
152        App::render_bottom_bar(bottom_bar, buf);
153    }
examples/flex.rs (line 506)
497    fn illustration(constraint: Constraint, width: u16) -> impl Widget {
498        let main_color = color_for_constraint(constraint);
499        let fg_color = Color::White;
500        let title = format!("{constraint}");
501        let content = format!("{width} px");
502        let text = format!("{title}\n{content}");
503        let block = Block::bordered()
504            .border_set(symbols::border::QUADRANT_OUTSIDE)
505            .border_style(Style::reset().fg(main_color).reversed())
506            .style(Style::default().fg(fg_color).bg(main_color));
507        Paragraph::new(text).centered().block(block)
508    }
examples/demo2/tabs/traceroute.rs (line 41)
34    fn render(self, area: Rect, buf: &mut Buffer) {
35        RgbSwatch.render(area, buf);
36        let area = area.inner(Margin {
37            vertical: 1,
38            horizontal: 2,
39        });
40        Clear.render(area, buf);
41        Block::new().style(THEME.content).render(area, buf);
42        let horizontal = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
43        let vertical = Layout::vertical([Constraint::Min(0), Constraint::Length(3)]);
44        let [left, map] = horizontal.areas(area);
45        let [hops, pings] = vertical.areas(left);
46
47        render_hops(self.row_index, hops, buf);
48        render_ping(self.row_index, pings, buf);
49        render_map(self.row_index, map, buf);
50    }
51}
52
53fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
54    let mut state = TableState::default().with_selected(Some(selected_row));
55    let rows = HOPS.iter().map(|hop| Row::new(vec![hop.host, hop.address]));
56    let block = Block::new()
57        .padding(Padding::new(1, 1, 1, 1))
58        .title_alignment(Alignment::Center)
59        .title("Traceroute bad.horse".bold().white());
60    StatefulWidget::render(
61        Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
62            .header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
63            .row_highlight_style(THEME.traceroute.selected)
64            .block(block),
65        area,
66        buf,
67        &mut state,
68    );
69    let mut scrollbar_state = ScrollbarState::default()
70        .content_length(HOPS.len())
71        .position(selected_row);
72    let area = Rect {
73        width: area.width + 1,
74        y: area.y + 3,
75        height: area.height - 4,
76        ..area
77    };
78    Scrollbar::default()
79        .orientation(ScrollbarOrientation::VerticalLeft)
80        .begin_symbol(None)
81        .end_symbol(None)
82        .track_symbol(None)
83        .thumb_symbol("▌")
84        .render(area, buf, &mut scrollbar_state);
85}
86
87pub fn render_ping(progress: usize, area: Rect, buf: &mut Buffer) {
88    let mut data = [
89        8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 8, 8, 7,
90        7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 2, 4, 6, 7, 8, 8, 8, 8, 6, 4, 2, 1, 1, 1, 1, 2, 2, 2, 3,
91        3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
92    ];
93    let mid = progress % data.len();
94    data.rotate_left(mid);
95    Sparkline::default()
96        .block(
97            Block::new()
98                .title("Ping")
99                .title_alignment(Alignment::Center)
100                .border_type(BorderType::Thick),
101        )
102        .data(data)
103        .style(THEME.traceroute.ping)
104        .render(area, buf);
105}
106
107fn render_map(selected_row: usize, area: Rect, buf: &mut Buffer) {
108    let theme = THEME.traceroute.map;
109    let path: Option<(&Hop, &Hop)> = HOPS.iter().tuple_windows().nth(selected_row);
110    let map = Map {
111        resolution: MapResolution::High,
112        color: theme.color,
113    };
114    Canvas::default()
115        .background_color(theme.background_color)
116        .block(
117            Block::new()
118                .padding(Padding::new(1, 0, 1, 0))
119                .style(theme.style),
120        )
121        .marker(Marker::HalfBlock)
122        // picked to show Australia for the demo as it's the most interesting part of the map
123        // (and the only part with hops ;))
124        .x_bounds([112.0, 155.0])
125        .y_bounds([-46.0, -11.0])
126        .paint(|context| {
127            context.draw(&map);
128            if let Some(path) = path {
129                context.draw(&canvas::Line::new(
130                    path.0.location.0,
131                    path.0.location.1,
132                    path.1.location.0,
133                    path.1.location.1,
134                    theme.path,
135                ));
136                context.draw(&Points {
137                    color: theme.source,
138                    coords: &[path.0.location], // sydney
139                });
140                context.draw(&Points {
141                    color: theme.destination,
142                    coords: &[path.1.location], // perth
143                });
144            }
145        })
146        .render(area, buf);
147}
examples/layout.rs (line 154)
146fn render_example_combination(
147    frame: &mut Frame,
148    area: Rect,
149    title: &str,
150    constraints: impl ExactSizeIterator<Item = (Constraint, Constraint)>,
151) {
152    let block = Block::bordered()
153        .title(title.gray())
154        .style(Style::reset())
155        .border_style(Style::default().fg(Color::DarkGray));
156    let inner = block.inner(area);
157    frame.render_widget(block, area);
158    let layout = Layout::vertical(vec![Length(1); constraints.len() + 1]).split(inner);
159    for ((a, b), &area) in constraints.into_iter().zip(layout.iter()) {
160        render_single_example(frame, area, vec![a, b, Min(0)]);
161    }
162    // This is to make it easy to visually see the alignment of the examples
163    // with the constraints.
164    frame.render_widget(Paragraph::new("123456789012"), layout[6]);
165}
examples/demo2/tabs/about.rs (line 76)
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71    let area = area.inner(Margin {
72        vertical: 4,
73        horizontal: 2,
74    });
75    Clear.render(area, buf); // clear out the color swatches
76    Block::new().style(THEME.content).render(area, buf);
77    let area = area.inner(Margin {
78        vertical: 1,
79        horizontal: 2,
80    });
81    let text = "- cooking up terminal user interfaces -
82
83    Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84    screen efficiently every frame.";
85    Paragraph::new(text)
86        .style(THEME.description)
87        .block(
88            Block::new()
89                .title(" Ratatui ")
90                .title_alignment(Alignment::Center)
91                .borders(Borders::TOP)
92                .border_style(THEME.description_title)
93                .padding(Padding::new(0, 0, 0, 0)),
94        )
95        .wrap(Wrap { trim: true })
96        .scroll((0, 0))
97        .render(area, buf);
98}
Source

pub const fn borders(self, flag: Borders) -> Self

Defines which borders to display.

Borders can also be styled with Block::border_style and Block::border_type.

§Examples

Display left and right borders.

use ratatui::widgets::{Block, Borders};
Block::new().borders(Borders::LEFT | Borders::RIGHT);

To show all borders you can abbreviate this with Block::bordered

Examples found in repository?
examples/line_gauge.rs (line 176)
173fn title_block(title: &str) -> Block {
174    Block::default()
175        .title(Line::from(title).centered())
176        .borders(Borders::NONE)
177        .fg(CUSTOM_LABEL_COLOR)
178        .padding(Padding::vertical(1))
179}
More examples
Hide additional examples
examples/gauge.rs (line 201)
198fn title_block(title: &str) -> Block {
199    let title = Line::from(title).centered();
200    Block::new()
201        .borders(Borders::NONE)
202        .padding(Padding::vertical(1))
203        .title(title)
204        .fg(CUSTOM_LABEL_COLOR)
205}
examples/block.rs (line 109)
107fn render_borders(paragraph: &Paragraph, border: Borders, frame: &mut Frame, area: Rect) {
108    let block = Block::new()
109        .borders(border)
110        .title(format!("Borders::{border:#?}"));
111    frame.render_widget(paragraph.clone().block(block), area);
112}
examples/demo2/tabs/about.rs (line 91)
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71    let area = area.inner(Margin {
72        vertical: 4,
73        horizontal: 2,
74    });
75    Clear.render(area, buf); // clear out the color swatches
76    Block::new().style(THEME.content).render(area, buf);
77    let area = area.inner(Margin {
78        vertical: 1,
79        horizontal: 2,
80    });
81    let text = "- cooking up terminal user interfaces -
82
83    Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84    screen efficiently every frame.";
85    Paragraph::new(text)
86        .style(THEME.description)
87        .block(
88            Block::new()
89                .title(" Ratatui ")
90                .title_alignment(Alignment::Center)
91                .borders(Borders::TOP)
92                .border_style(THEME.description_title)
93                .padding(Padding::new(0, 0, 0, 0)),
94        )
95        .wrap(Wrap { trim: true })
96        .scroll((0, 0))
97        .render(area, buf);
98}
examples/list.rs (line 211)
208    fn render_list(&mut self, area: Rect, buf: &mut Buffer) {
209        let block = Block::new()
210            .title(Line::raw("TODO List").centered())
211            .borders(Borders::TOP)
212            .border_set(symbols::border::EMPTY)
213            .border_style(TODO_HEADER_STYLE)
214            .bg(NORMAL_ROW_BG);
215
216        // Iterate through all elements in the `items` and stylize them.
217        let items: Vec<ListItem> = self
218            .todo_list
219            .items
220            .iter()
221            .enumerate()
222            .map(|(i, todo_item)| {
223                let color = alternate_colors(i);
224                ListItem::from(todo_item).bg(color)
225            })
226            .collect();
227
228        // Create a List from all list items and highlight the currently selected one
229        let list = List::new(items)
230            .block(block)
231            .highlight_style(SELECTED_STYLE)
232            .highlight_symbol(">")
233            .highlight_spacing(HighlightSpacing::Always);
234
235        // We need to disambiguate this trait method as both `Widget` and `StatefulWidget` share the
236        // same method name `render`.
237        StatefulWidget::render(list, area, buf, &mut self.todo_list.state);
238    }
239
240    fn render_selected_item(&self, area: Rect, buf: &mut Buffer) {
241        // We get the info depending on the item's state.
242        let info = if let Some(i) = self.todo_list.state.selected() {
243            match self.todo_list.items[i].status {
244                Status::Completed => format!("✓ DONE: {}", self.todo_list.items[i].info),
245                Status::Todo => format!("☐ TODO: {}", self.todo_list.items[i].info),
246            }
247        } else {
248            "Nothing selected...".to_string()
249        };
250
251        // We show the list item's info under the list in this paragraph
252        let block = Block::new()
253            .title(Line::raw("TODO Info").centered())
254            .borders(Borders::TOP)
255            .border_set(symbols::border::EMPTY)
256            .border_style(TODO_HEADER_STYLE)
257            .bg(NORMAL_ROW_BG)
258            .padding(Padding::horizontal(1));
259
260        // We can now render the item info
261        Paragraph::new(info)
262            .block(block)
263            .fg(TEXT_FG_COLOR)
264            .wrap(Wrap { trim: false })
265            .render(area, buf);
266    }
examples/sparkline.rs (line 126)
116    fn draw(&self, frame: &mut Frame) {
117        let chunks = Layout::vertical([
118            Constraint::Length(3),
119            Constraint::Length(3),
120            Constraint::Min(0),
121        ])
122        .split(frame.area());
123        let sparkline = Sparkline::default()
124            .block(
125                Block::new()
126                    .borders(Borders::LEFT | Borders::RIGHT)
127                    .title("Data1"),
128            )
129            .data(&self.data1)
130            .style(Style::default().fg(Color::Yellow));
131        frame.render_widget(sparkline, chunks[0]);
132        let sparkline = Sparkline::default()
133            .block(
134                Block::new()
135                    .borders(Borders::LEFT | Borders::RIGHT)
136                    .title("Data2"),
137            )
138            .data(&self.data2)
139            .style(Style::default().bg(Color::Green));
140        frame.render_widget(sparkline, chunks[1]);
141        // Multiline
142        let sparkline = Sparkline::default()
143            .block(
144                Block::new()
145                    .borders(Borders::LEFT | Borders::RIGHT)
146                    .title("Data3"),
147            )
148            .data(&self.data3)
149            .style(Style::default().fg(Color::Red));
150        frame.render_widget(sparkline, chunks[2]);
151    }
Source

pub const fn border_type(self, border_type: BorderType) -> Self

Sets the symbols used to display the border (e.g. single line, double line, thick or rounded borders).

Setting this overwrites any custom border_set that was set.

See BorderType for the full list of available symbols.

§Examples
use ratatui::widgets::{Block, BorderType};
Block::bordered()
    .border_type(BorderType::Rounded)
    .title("Block");
// Renders
// ╭Block╮
// │     │
// ╰─────╯
Examples found in repository?
examples/block.rs (line 121)
114fn render_border_type(
115    paragraph: &Paragraph,
116    border_type: BorderType,
117    frame: &mut Frame,
118    area: Rect,
119) {
120    let block = Block::bordered()
121        .border_type(border_type)
122        .title(format!("BorderType::{border_type:#?}"));
123    frame.render_widget(paragraph.clone().block(block), area);
124}
More examples
Hide additional examples
examples/table.rs (line 293)
283    fn render_footer(&self, frame: &mut Frame, area: Rect) {
284        let info_footer = Paragraph::new(Text::from_iter(INFO_TEXT))
285            .style(
286                Style::new()
287                    .fg(self.colors.row_fg)
288                    .bg(self.colors.buffer_bg),
289            )
290            .centered()
291            .block(
292                Block::bordered()
293                    .border_type(BorderType::Double)
294                    .border_style(Style::new().fg(self.colors.footer_border_color)),
295            );
296        frame.render_widget(info_footer, area);
297    }
examples/demo2/tabs/traceroute.rs (line 100)
87pub fn render_ping(progress: usize, area: Rect, buf: &mut Buffer) {
88    let mut data = [
89        8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 8, 8, 7,
90        7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 2, 4, 6, 7, 8, 8, 8, 8, 6, 4, 2, 1, 1, 1, 1, 2, 2, 2, 3,
91        3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
92    ];
93    let mid = progress % data.len();
94    data.rotate_left(mid);
95    Sparkline::default()
96        .block(
97            Block::new()
98                .title("Ping")
99                .title_alignment(Alignment::Center)
100                .border_type(BorderType::Thick),
101        )
102        .data(data)
103        .style(THEME.traceroute.ping)
104        .render(area, buf);
105}
examples/demo2/tabs/email.rs (line 131)
124fn render_email(selected_index: usize, area: Rect, buf: &mut Buffer) {
125    let theme = THEME.email;
126    let email = EMAILS.get(selected_index);
127    let block = Block::new()
128        .style(theme.body)
129        .padding(Padding::new(2, 2, 0, 0))
130        .borders(Borders::TOP)
131        .border_type(BorderType::Thick);
132    let inner = block.inner(area);
133    block.render(area, buf);
134    if let Some(email) = email {
135        let vertical = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]);
136        let [headers_area, body_area] = vertical.areas(inner);
137        let headers = vec![
138            Line::from(vec![
139                "From: ".set_style(theme.header),
140                email.from.set_style(theme.header_value),
141            ]),
142            Line::from(vec![
143                "Subject: ".set_style(theme.header),
144                email.subject.set_style(theme.header_value),
145            ]),
146            "-".repeat(inner.width as usize).dim().into(),
147        ];
148        Paragraph::new(headers)
149            .style(theme.body)
150            .render(headers_area, buf);
151        let body = email.body.lines().map(Line::from).collect_vec();
152        Paragraph::new(body)
153            .style(theme.body)
154            .render(body_area, buf);
155    } else {
156        Paragraph::new("No email selected").render(inner, buf);
157    }
158}
Source

pub const fn border_set(self, border_set: Set) -> Self

Sets the symbols used to display the border as a crate::symbols::border::Set.

Setting this overwrites any border_type that was set.

§Examples
use ratatui::{widgets::Block, symbols};

Block::bordered().border_set(symbols::border::DOUBLE).title("Block");
// Renders
// ╔Block╗
// ║     ║
// ╚═════╝
Examples found in repository?
examples/tabs.rs (line 203)
201    fn block(self) -> Block<'static> {
202        Block::bordered()
203            .border_set(symbols::border::PROPORTIONAL_TALL)
204            .padding(Padding::horizontal(1))
205            .border_style(self.palette().c700)
206    }
More examples
Hide additional examples
examples/constraint-explorer.rs (line 443)
434    fn render_2px(&self, area: Rect, buf: &mut Buffer) {
435        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
436        let main_color = ConstraintName::from(self.constraint).color();
437        let selected_color = if self.selected {
438            lighter_color
439        } else {
440            main_color
441        };
442        Block::bordered()
443            .border_set(symbols::border::QUADRANT_OUTSIDE)
444            .border_style(Style::reset().fg(selected_color).reversed())
445            .render(area, buf);
446    }
447
448    fn render_4px(&self, area: Rect, buf: &mut Buffer) {
449        let lighter_color = ConstraintName::from(self.constraint).lighter_color();
450        let main_color = ConstraintName::from(self.constraint).color();
451        let selected_color = if self.selected {
452            lighter_color
453        } else {
454            main_color
455        };
456        let color = if self.legend {
457            selected_color
458        } else {
459            main_color
460        };
461        let label = self.label(area.width);
462        let block = Block::bordered()
463            .border_set(symbols::border::QUADRANT_OUTSIDE)
464            .border_style(Style::reset().fg(color).reversed())
465            .fg(Self::TEXT_COLOR)
466            .bg(color);
467        Paragraph::new(label)
468            .centered()
469            .fg(Self::TEXT_COLOR)
470            .bg(color)
471            .block(block)
472            .render(area, buf);
473
474        if !self.legend {
475            let border_color = if self.selected {
476                lighter_color
477            } else {
478                main_color
479            };
480            if let Some(last_row) = area.rows().last() {
481                buf.set_style(last_row, border_color);
482            }
483        }
484    }
485}
486
487impl Widget for SpacerBlock {
488    fn render(self, area: Rect, buf: &mut Buffer) {
489        match area.height {
490            1 => (),
491            2 => Self::render_2px(area, buf),
492            3 => Self::render_3px(area, buf),
493            _ => Self::render_4px(area, buf),
494        }
495    }
496}
497
498impl SpacerBlock {
499    const TEXT_COLOR: Color = SLATE.c500;
500    const BORDER_COLOR: Color = SLATE.c600;
501
502    /// A block with a corner borders
503    fn block() -> impl Widget {
504        let corners_only = symbols::border::Set {
505            top_left: line::NORMAL.top_left,
506            top_right: line::NORMAL.top_right,
507            bottom_left: line::NORMAL.bottom_left,
508            bottom_right: line::NORMAL.bottom_right,
509            vertical_left: " ",
510            vertical_right: " ",
511            horizontal_top: " ",
512            horizontal_bottom: " ",
513        };
514        Block::bordered()
515            .border_set(corners_only)
516            .border_style(Self::BORDER_COLOR)
517    }
examples/list.rs (line 212)
208    fn render_list(&mut self, area: Rect, buf: &mut Buffer) {
209        let block = Block::new()
210            .title(Line::raw("TODO List").centered())
211            .borders(Borders::TOP)
212            .border_set(symbols::border::EMPTY)
213            .border_style(TODO_HEADER_STYLE)
214            .bg(NORMAL_ROW_BG);
215
216        // Iterate through all elements in the `items` and stylize them.
217        let items: Vec<ListItem> = self
218            .todo_list
219            .items
220            .iter()
221            .enumerate()
222            .map(|(i, todo_item)| {
223                let color = alternate_colors(i);
224                ListItem::from(todo_item).bg(color)
225            })
226            .collect();
227
228        // Create a List from all list items and highlight the currently selected one
229        let list = List::new(items)
230            .block(block)
231            .highlight_style(SELECTED_STYLE)
232            .highlight_symbol(">")
233            .highlight_spacing(HighlightSpacing::Always);
234
235        // We need to disambiguate this trait method as both `Widget` and `StatefulWidget` share the
236        // same method name `render`.
237        StatefulWidget::render(list, area, buf, &mut self.todo_list.state);
238    }
239
240    fn render_selected_item(&self, area: Rect, buf: &mut Buffer) {
241        // We get the info depending on the item's state.
242        let info = if let Some(i) = self.todo_list.state.selected() {
243            match self.todo_list.items[i].status {
244                Status::Completed => format!("✓ DONE: {}", self.todo_list.items[i].info),
245                Status::Todo => format!("☐ TODO: {}", self.todo_list.items[i].info),
246            }
247        } else {
248            "Nothing selected...".to_string()
249        };
250
251        // We show the list item's info under the list in this paragraph
252        let block = Block::new()
253            .title(Line::raw("TODO Info").centered())
254            .borders(Borders::TOP)
255            .border_set(symbols::border::EMPTY)
256            .border_style(TODO_HEADER_STYLE)
257            .bg(NORMAL_ROW_BG)
258            .padding(Padding::horizontal(1));
259
260        // We can now render the item info
261        Paragraph::new(info)
262            .block(block)
263            .fg(TEXT_FG_COLOR)
264            .wrap(Wrap { trim: false })
265            .render(area, buf);
266    }
examples/flex.rs (line 465)
452    fn render_spacer(spacer: Rect, buf: &mut Buffer) {
453        if spacer.width > 1 {
454            let corners_only = symbols::border::Set {
455                top_left: line::NORMAL.top_left,
456                top_right: line::NORMAL.top_right,
457                bottom_left: line::NORMAL.bottom_left,
458                bottom_right: line::NORMAL.bottom_right,
459                vertical_left: " ",
460                vertical_right: " ",
461                horizontal_top: " ",
462                horizontal_bottom: " ",
463            };
464            Block::bordered()
465                .border_set(corners_only)
466                .border_style(Style::reset().dark_gray())
467                .render(spacer, buf);
468        } else {
469            Paragraph::new(Text::from(vec![
470                Line::from(""),
471                Line::from("│"),
472                Line::from("│"),
473                Line::from(""),
474            ]))
475            .style(Style::reset().dark_gray())
476            .render(spacer, buf);
477        }
478        let width = spacer.width;
479        let label = if width > 4 {
480            format!("{width} px")
481        } else if width > 2 {
482            format!("{width}")
483        } else {
484            String::new()
485        };
486        let text = Text::from(vec![
487            Line::raw(""),
488            Line::raw(""),
489            Line::styled(label, Style::reset().dark_gray()),
490        ]);
491        Paragraph::new(text)
492            .style(Style::reset().dark_gray())
493            .alignment(Alignment::Center)
494            .render(spacer, buf);
495    }
496
497    fn illustration(constraint: Constraint, width: u16) -> impl Widget {
498        let main_color = color_for_constraint(constraint);
499        let fg_color = Color::White;
500        let title = format!("{constraint}");
501        let content = format!("{width} px");
502        let text = format!("{title}\n{content}");
503        let block = Block::bordered()
504            .border_set(symbols::border::QUADRANT_OUTSIDE)
505            .border_style(Style::reset().fg(main_color).reversed())
506            .style(Style::default().fg(fg_color).bg(main_color));
507        Paragraph::new(text).centered().block(block)
508    }
Source

pub const fn padding(self, padding: Padding) -> Self

Defines the padding inside a Block.

See Padding for more information.

§Examples

This renders a Block with no padding (the default).

use ratatui::widgets::{Block, Padding};

Block::bordered().padding(Padding::ZERO);
// Renders
// ┌───────┐
// │content│
// └───────┘

This example shows a Block with padding left and right (Padding::horizontal). Notice the two spaces before and after the content.

use ratatui::widgets::{Block, Padding};

Block::bordered().padding(Padding::horizontal(2));
// Renders
// ┌───────────┐
// │  content  │
// └───────────┘
Examples found in repository?
examples/line_gauge.rs (line 178)
173fn title_block(title: &str) -> Block {
174    Block::default()
175        .title(Line::from(title).centered())
176        .borders(Borders::NONE)
177        .fg(CUSTOM_LABEL_COLOR)
178        .padding(Padding::vertical(1))
179}
More examples
Hide additional examples
examples/tabs.rs (line 204)
201    fn block(self) -> Block<'static> {
202        Block::bordered()
203            .border_set(symbols::border::PROPORTIONAL_TALL)
204            .padding(Padding::horizontal(1))
205            .border_style(self.palette().c700)
206    }
examples/gauge.rs (line 202)
198fn title_block(title: &str) -> Block {
199    let title = Line::from(title).centered();
200    Block::new()
201        .borders(Borders::NONE)
202        .padding(Padding::vertical(1))
203        .title(title)
204        .fg(CUSTOM_LABEL_COLOR)
205}
examples/block.rs (line 175)
173fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
174    let block = Block::bordered()
175        .padding(Padding::new(5, 10, 1, 2))
176        .title("Padding");
177    frame.render_widget(paragraph.clone().block(block), area);
178}
examples/demo2/tabs/weather.rs (line 69)
66fn render_calendar(area: Rect, buf: &mut Buffer) {
67    let date = OffsetDateTime::now_utc().date();
68    Monthly::new(date, CalendarEventStore::today(Style::new().red().bold()))
69        .block(Block::new().padding(Padding::new(0, 0, 2, 0)))
70        .show_month_header(Style::new().bold())
71        .show_weekdays_header(Style::new().italic())
72        .render(area, buf);
73}
74
75fn render_simple_barchart(area: Rect, buf: &mut Buffer) {
76    let data = [
77        ("Sat", 76),
78        ("Sun", 69),
79        ("Mon", 65),
80        ("Tue", 67),
81        ("Wed", 65),
82        ("Thu", 69),
83        ("Fri", 73),
84    ];
85    let data = data
86        .into_iter()
87        .map(|(label, value)| {
88            Bar::default()
89                .value(value)
90                // This doesn't actually render correctly as the text is too wide for the bar
91                // See https://github.com/ratatui/ratatui/issues/513 for more info
92                // (the demo GIFs hack around this by hacking the calculation in bars.rs)
93                .text_value(format!("{value}°"))
94                .style(if value > 70 {
95                    Style::new().fg(Color::Red)
96                } else {
97                    Style::new().fg(Color::Yellow)
98                })
99                .value_style(if value > 70 {
100                    Style::new().fg(Color::Gray).bg(Color::Red).bold()
101                } else {
102                    Style::new().fg(Color::DarkGray).bg(Color::Yellow).bold()
103                })
104                .label(label.into())
105        })
106        .collect_vec();
107    let group = BarGroup::default().bars(&data);
108    BarChart::default()
109        .data(group)
110        .bar_width(3)
111        .bar_gap(1)
112        .render(area, buf);
113}
114
115fn render_horizontal_barchart(area: Rect, buf: &mut Buffer) {
116    let bg = Color::Rgb(32, 48, 96);
117    let data = [
118        Bar::default().text_value("Winter 37-51".into()).value(51),
119        Bar::default().text_value("Spring 40-65".into()).value(65),
120        Bar::default().text_value("Summer 54-77".into()).value(77),
121        Bar::default()
122            .text_value("Fall 41-71".into())
123            .value(71)
124            .value_style(Style::new().bold()), // current season
125    ];
126    let group = BarGroup::default().label("GPU".into()).bars(&data);
127    BarChart::default()
128        .block(Block::new().padding(Padding::new(0, 0, 2, 0)))
129        .direction(Direction::Horizontal)
130        .data(group)
131        .bar_gap(1)
132        .bar_style(Style::new().fg(bg))
133        .value_style(Style::new().bg(bg).fg(Color::Gray))
134        .render(area, buf);
135}
examples/demo2/tabs/about.rs (line 93)
70fn render_crate_description(area: Rect, buf: &mut Buffer) {
71    let area = area.inner(Margin {
72        vertical: 4,
73        horizontal: 2,
74    });
75    Clear.render(area, buf); // clear out the color swatches
76    Block::new().style(THEME.content).render(area, buf);
77    let area = area.inner(Margin {
78        vertical: 1,
79        horizontal: 2,
80    });
81    let text = "- cooking up terminal user interfaces -
82
83    Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
84    screen efficiently every frame.";
85    Paragraph::new(text)
86        .style(THEME.description)
87        .block(
88            Block::new()
89                .title(" Ratatui ")
90                .title_alignment(Alignment::Center)
91                .borders(Borders::TOP)
92                .border_style(THEME.description_title)
93                .padding(Padding::new(0, 0, 0, 0)),
94        )
95        .wrap(Wrap { trim: true })
96        .scroll((0, 0))
97        .render(area, buf);
98}
Source

pub fn inner(&self, area: Rect) -> Rect

Compute the inner area of a block based on its border visibility rules.

§Examples

Draw a block nested within another block

use ratatui::{widgets::Block, Frame};

let outer_block = Block::bordered().title("Outer");
let inner_block = Block::bordered().title("Inner");

let outer_area = frame.area();
let inner_area = outer_block.inner(outer_area);

frame.render_widget(outer_block, outer_area);
frame.render_widget(inner_block, inner_area);
// Renders
// ┌Outer────────┐
// │┌Inner──────┐│
// ││           ││
// │└───────────┘│
// └─────────────┘
Examples found in repository?
examples/block.rs (line 183)
180fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
181    let outer_block = Block::bordered().title("Outer block");
182    let inner_block = Block::bordered().title("Inner block");
183    let inner = outer_block.inner(area);
184    frame.render_widget(outer_block, area);
185    frame.render_widget(paragraph.clone().block(inner_block), inner);
186}
More examples
Hide additional examples
examples/layout.rs (line 156)
146fn render_example_combination(
147    frame: &mut Frame,
148    area: Rect,
149    title: &str,
150    constraints: impl ExactSizeIterator<Item = (Constraint, Constraint)>,
151) {
152    let block = Block::bordered()
153        .title(title.gray())
154        .style(Style::reset())
155        .border_style(Style::default().fg(Color::DarkGray));
156    let inner = block.inner(area);
157    frame.render_widget(block, area);
158    let layout = Layout::vertical(vec![Length(1); constraints.len() + 1]).split(inner);
159    for ((a, b), &area) in constraints.into_iter().zip(layout.iter()) {
160        render_single_example(frame, area, vec![a, b, Min(0)]);
161    }
162    // This is to make it easy to visually see the alignment of the examples
163    // with the constraints.
164    frame.render_widget(Paragraph::new("123456789012"), layout[6]);
165}
examples/demo2/tabs/email.rs (line 132)
124fn render_email(selected_index: usize, area: Rect, buf: &mut Buffer) {
125    let theme = THEME.email;
126    let email = EMAILS.get(selected_index);
127    let block = Block::new()
128        .style(theme.body)
129        .padding(Padding::new(2, 2, 0, 0))
130        .borders(Borders::TOP)
131        .border_type(BorderType::Thick);
132    let inner = block.inner(area);
133    block.render(area, buf);
134    if let Some(email) = email {
135        let vertical = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]);
136        let [headers_area, body_area] = vertical.areas(inner);
137        let headers = vec![
138            Line::from(vec![
139                "From: ".set_style(theme.header),
140                email.from.set_style(theme.header_value),
141            ]),
142            Line::from(vec![
143                "Subject: ".set_style(theme.header),
144                email.subject.set_style(theme.header_value),
145            ]),
146            "-".repeat(inner.width as usize).dim().into(),
147        ];
148        Paragraph::new(headers)
149            .style(theme.body)
150            .render(headers_area, buf);
151        let body = email.body.lines().map(Line::from).collect_vec();
152        Paragraph::new(body)
153            .style(theme.body)
154            .render(body_area, buf);
155    } else {
156        Paragraph::new("No email selected").render(inner, buf);
157    }
158}

Trait Implementations§

Source§

impl<'a> Clone for Block<'a>

Source§

fn clone(&self) -> Block<'a>

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for Block<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a> Default for Block<'a>

Source§

fn default() -> Block<'a>

Returns the “default value” for a type. Read more
Source§

impl<'a> Hash for Block<'a>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<'a> PartialEq for Block<'a>

Source§

fn eq(&self, other: &Block<'a>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<'a> Styled for Block<'a>

Source§

type Item = Block<'a>

Source§

fn style(&self) -> Style

Returns the style of the object.
Source§

fn set_style<S: Into<Style>>(self, style: S) -> Self::Item

Sets the style of the object. Read more
Source§

impl Widget for Block<'_>

Source§

fn render(self, area: Rect, buf: &mut Buffer)

Draws the current state of the widget in the given buffer. That is the only method required to implement a custom widget.
Source§

impl WidgetRef for Block<'_>

Source§

fn render_ref(&self, area: Rect, buf: &mut Buffer)

Available on crate feature unstable-widget-ref only.
Draws the current state of the widget in the given buffer. That is the only method required to implement a custom widget.
Source§

impl<'a> Eq for Block<'a>

Source§

impl<'a> StructuralPartialEq for Block<'a>

Auto Trait Implementations§

§

impl<'a> Freeze for Block<'a>

§

impl<'a> RefUnwindSafe for Block<'a>

§

impl<'a> Send for Block<'a>

§

impl<'a> Sync for Block<'a>

§

impl<'a> Unpin for Block<'a>

§

impl<'a> UnwindSafe for Block<'a>

Blanket Implementations§

Source§

impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for S
where T: Real + Zero + Arithmetics + Clone, Swp: WhitePoint<T>, Dwp: WhitePoint<T>, D: AdaptFrom<S, Swp, Dwp, T>,

Source§

fn adapt_into_using<M>(self, method: M) -> D
where M: TransformMatrix<T>,

Convert the source color to the destination color using the specified method.
Source§

fn adapt_into(self) -> D

Convert the source color to the destination color using the bradford method by default.
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T, C> ArraysFrom<C> for T
where C: IntoArrays<T>,

Source§

fn arrays_from(colors: C) -> T

Cast a collection of colors into a collection of arrays.
Source§

impl<T, C> ArraysInto<C> for T
where C: FromArrays<T>,

Source§

fn arrays_into(self) -> C

Cast this collection of arrays into a collection of colors.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for U
where T: FromCam16Unclamped<WpParam, U>,

Source§

type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T, C> ComponentsFrom<C> for T
where C: IntoComponents<T>,

Source§

fn components_from(colors: C) -> T

Cast a collection of colors into a collection of color components.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromAngle<T> for T

Source§

fn from_angle(angle: T) -> T

Performs a conversion from angle.
Source§

impl<T, U> FromStimulus<U> for T
where U: IntoStimulus<T>,

Source§

fn from_stimulus(other: U) -> T

Converts other into Self, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> IntoAngle<U> for T
where U: FromAngle<T>,

Source§

fn into_angle(self) -> U

Performs a conversion into T.
Source§

impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for U
where T: Cam16FromUnclamped<WpParam, U>,

Source§

type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar

The number type that’s used in parameters when converting.
Source§

fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T

Converts self into C, using the provided parameters.
Source§

impl<T, U> IntoColor<U> for T
where U: FromColor<T>,

Source§

fn into_color(self) -> U

Convert into T with values clamped to the color defined bounds Read more
Source§

impl<T, U> IntoColorUnclamped<U> for T
where U: FromColorUnclamped<T>,

Source§

fn into_color_unclamped(self) -> U

Convert into T. The resulting color might be invalid in its color space Read more
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> IntoStimulus<T> for T

Source§

fn into_stimulus(self) -> T

Converts self into T, while performing the appropriate scaling, rounding and clamping.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<'a, T, U> Stylize<'a, T> for U
where U: Styled<Item = T>,

Source§

fn bg<C>(self, color: C) -> T
where C: Into<Color>,

Source§

fn fg<C>(self, color: C) -> T
where C: Into<Color>,

Source§

fn add_modifier(self, modifier: Modifier) -> T

Source§

fn remove_modifier(self, modifier: Modifier) -> T

Source§

fn reset(self) -> T

Source§

fn black(self) -> T

Sets the foreground color to black.
Source§

fn on_black(self) -> T

Sets the background color to black.
Source§

fn red(self) -> T

Sets the foreground color to red.
Source§

fn on_red(self) -> T

Sets the background color to red.
Source§

fn green(self) -> T

Sets the foreground color to green.
Source§

fn on_green(self) -> T

Sets the background color to green.
Source§

fn yellow(self) -> T

Sets the foreground color to yellow.
Source§

fn on_yellow(self) -> T

Sets the background color to yellow.
Source§

fn blue(self) -> T

Sets the foreground color to blue.
Source§

fn on_blue(self) -> T

Sets the background color to blue.
Source§

fn magenta(self) -> T

Sets the foreground color to magenta.
Source§

fn on_magenta(self) -> T

Sets the background color to magenta.
Source§

fn cyan(self) -> T

Sets the foreground color to cyan.
Source§

fn on_cyan(self) -> T

Sets the background color to cyan.
Source§

fn gray(self) -> T

Sets the foreground color to gray.
Source§

fn on_gray(self) -> T

Sets the background color to gray.
Source§

fn dark_gray(self) -> T

Sets the foreground color to dark_gray.
Source§

fn on_dark_gray(self) -> T

Sets the background color to dark_gray.
Source§

fn light_red(self) -> T

Sets the foreground color to light_red.
Source§

fn on_light_red(self) -> T

Sets the background color to light_red.
Source§

fn light_green(self) -> T

Sets the foreground color to light_green.
Source§

fn on_light_green(self) -> T

Sets the background color to light_green.
Source§

fn light_yellow(self) -> T

Sets the foreground color to light_yellow.
Source§

fn on_light_yellow(self) -> T

Sets the background color to light_yellow.
Source§

fn light_blue(self) -> T

Sets the foreground color to light_blue.
Source§

fn on_light_blue(self) -> T

Sets the background color to light_blue.
Source§

fn light_magenta(self) -> T

Sets the foreground color to light_magenta.
Source§

fn on_light_magenta(self) -> T

Sets the background color to light_magenta.
Source§

fn light_cyan(self) -> T

Sets the foreground color to light_cyan.
Source§

fn on_light_cyan(self) -> T

Sets the background color to light_cyan.
Source§

fn white(self) -> T

Sets the foreground color to white.
Source§

fn on_white(self) -> T

Sets the background color to white.
Source§

fn bold(self) -> T

Adds the BOLD modifier.
Source§

fn not_bold(self) -> T

Removes the BOLD modifier.
Source§

fn dim(self) -> T

Adds the DIM modifier.
Source§

fn not_dim(self) -> T

Removes the DIM modifier.
Source§

fn italic(self) -> T

Adds the ITALIC modifier.
Source§

fn not_italic(self) -> T

Removes the ITALIC modifier.
Source§

fn underlined(self) -> T

Adds the UNDERLINED modifier.
Source§

fn not_underlined(self) -> T

Removes the UNDERLINED modifier.
Adds the SLOW_BLINK modifier.
Removes the SLOW_BLINK modifier.
Adds the RAPID_BLINK modifier.
Removes the RAPID_BLINK modifier.
Source§

fn reversed(self) -> T

Adds the REVERSED modifier.
Source§

fn not_reversed(self) -> T

Removes the REVERSED modifier.
Source§

fn hidden(self) -> T

Adds the HIDDEN modifier.
Source§

fn not_hidden(self) -> T

Removes the HIDDEN modifier.
Source§

fn crossed_out(self) -> T

Adds the CROSSED_OUT modifier.
Source§

fn not_crossed_out(self) -> T

Removes the CROSSED_OUT modifier.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, C> TryComponentsInto<C> for T
where C: TryFromComponents<T>,

Source§

type Error = <C as TryFromComponents<T>>::Error

The error for when try_into_colors fails to cast.
Source§

fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>

Try to cast this collection of color components into a collection of colors. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryIntoColor<U> for T
where U: TryFromColor<T>,

Source§

fn try_into_color(self) -> Result<U, OutOfBounds<U>>

Convert into T, returning ok if the color is inside of its defined range, otherwise an OutOfBounds error is returned which contains the unclamped color. Read more
Source§

impl<C, U> UintsFrom<C> for U
where C: IntoUints<U>,

Source§

fn uints_from(colors: C) -> U

Cast a collection of colors into a collection of unsigned integers.
Source§

impl<C, U> UintsInto<C> for U
where C: FromUints<U>,

Source§

fn uints_into(self) -> C

Cast this collection of unsigned integers into a collection of colors.