Ratatui is a crate for building terminal user interfaces in Rust.
In this post, we’ll explore the text primitives of ratatui.
Code 
: dep ratatui =  "0.26.2" : dep ratatui- macros =  "0.4.0" fn  span_to_html(s:  ratatui::text:: Span) ->  String { let  mut  html =  String :: new(); . push_str("<span style= \" " ); // Set foreground color if  let  Some (color) =  & s. style. fg { . push_str(& format! ("color: {};" ,  color)); } // Set background color if  let  Some (color) =  & s. style. bg { . push_str(& format! ("background-color: {};" ,  color)); } // Add modifiers match  s. style. add_modifier { ratatui::style::Modifier:: BOLD =>  html. push_str("font-weight: bold;" ), ratatui::style::Modifier:: ITALIC =>  html. push_str("font-style: italic;" ), ratatui::style::Modifier:: UNDERLINED =>  html. push_str("text-decoration: underline;" ), =>  {} } . push_str(" \" >" ); . push_str(& s. content); . push_str("</span>" ); } fn  buffer_to_html(buf:  & ratatui::buffer:: Buffer) ->  String  { fn  escape_special_html_characters(text:  & str ) ->  String  { . replace("&" ,  "&" ). replace("<" ,  "<" ). replace(">" ,  ">" ). replace(" \" " ,  """ ). replace("'" ,  "'" )} let  mut  html =  String :: from("<pre><code>" ); let  w =  buf. area. width; let  h =  buf. area. height; for  y in  0 .. h { for  x in  0 .. w { let  s =  buf. get(x,  y). symbol(); let  escaped =  escape_special_html_characters(s);  let  style =  buf. get(x,  y). style(); let  span =  ratatui::text::Span:: styled(s,  style); . push_str(& span_to_html(span)); } . push(' \n ' ); } . push_str("</code></pre>" ); } fn  show_html< D> (content:  D) where  D:  std::fmt:: Display  { println! (r#"EVCXR_BEGIN_CONTENT text/html <div style="display: flex; justify-content:start; gap: 1em; margin: 1em"> {} </div> EVCXR_END_CONTENT"# ,  content); }  
 
Text primitives 
In Ratatui, there are 3 fundamental text primitives that you should be aware of.
Span 
The first is a Span.
use  ratatui::text:: Span; let  span =  Span:: raw("hello world" ); 
Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } } 
 
A Span contains two fields.
Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } 
 
A Style object contains foreground color, background color, and modifiers for whether the style being applied is bold , italics , etc
There are a number of constructors for Span that you may use, but ratatui exposes a Stylize trait that makes it easy to style content which I find very useful.
use  ratatui::style:: Stylize;  // required trait to use style methods "hello world" . bold()
Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: BOLD, sub_modifier: NONE } } 
 
You can even chain these trait methods to add more styles:
"hello world" . bold(). yellow(). on_black()
Span { content: "hello world", style: Style { fg: Some(Yellow), bg: Some(Black), underline_color: None, add_modifier: BOLD, sub_modifier: NONE } } 
 
"hello world" . bold())) 
"hello world" . yellow(). bold(). on_black())) 
With ratatui-macros, you can even use a format! style macro to create a Span
use  ratatui_macros:: span; let  world =  "world" ; span! ("hello {}" ,  world)
Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } } 
 
 
Line 
The second primitive to be aware of is a Line.
A line consists of one or more spans.
use  ratatui::text:: Line; let  line =  Line:: raw("hello world" ); 
Line { spans: [Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } }], style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE }, alignment: None } 
 
[Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } }] 
 
A unique feature of lines is that new lines are removed but the content is split into multiple spans.
let  line =  Line:: raw("hello world \n goodbye world" );  
Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } } 
 
Span { content: "goodbye world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } } 
 
A line can also be styled with methods from the Stylize trait:
Line:: raw("hello world" ). bold()
Line { spans: [Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } }], style: Style { fg: None, bg: None, underline_color: None, add_modifier: BOLD, sub_modifier: NONE }, alignment: None } 
 
In this case, the individual span’s styles are left untouched but the Line’s style is updated.
Another unique feature about Line is that they can be aligned.
let  centered_line =  line. centered(); 
Line { spans: [Span { content: "hello world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } }, Span { content: "goodbye world", style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE } }], style: Style { fg: None, bg: None, underline_color: None, add_modifier: NONE, sub_modifier: NONE }, alignment: Some(Center) } 
 
With ratatui-macros, you can create a Line using the line! macro using a vec!-like syntax.
use  ratatui_macros:: line; line! ["hello" ,  " " ,  "world" ]. yellow(). bold(). centered();  
Every element in the line! macro is converted to a Span.
 
Text 
Finally there is Text, which is a collection of Lines.
use  ratatui::text:: Text; Text:: from(vec! [Line:: raw("hello world" ),  Line:: raw("goodbye world" )]);  
With ratatui-macros, you can create a Text using text! macro using a vec!-like syntax.
use  ratatui_macros:: text; text! ["hello world" ,  "goodbye world" ];  
Here, every element in the text! macro is converted to a Line.
Like Line, Text can also be aligned. In this case, the alignment occurs on every Line inside the Text.
let  t =  text! ["hello world" ,  "goodbye world" ]. right_aligned(); . alignment 
use  ratatui::widgets:: Widget; let  (x,  y,  width,  height) =  (0 ,  0 ,  50 ,  5 );  let  area =  ratatui::layout::Rect:: new(x,  y,  width,  height); let  mut  buf =  ratatui::buffer::Buffer:: empty(area); text! ["left aligned bold text" . bold(),  "center aligned italic text" . italic(). into_centered_line(), "right aligned with yellow on black" . yellow(). on_black(). into_right_aligned_line(), . render(area,  & mut  buf); & buf))
l e f t   a l i g n e d   b o l d   t e x t                                                         
                        c e n t e r   a l i g n e d   i t a l i c   t e x t                         
                                r i g h t   a l i g n e d   w i t h   y e l l o w   o n   b l a c k 
                                                                                                    
                                                                                                    
 
 
 
 
 
Conclusion 
In the next post, we’ll examine the a few commonly used widgets.
 
Citation BibTeX citation:
@online{krishnamurthy2024,
  author = {Krishnamurthy, Dheepak},
  title = {The {Basic} {Building} Blocks of {Ratatui} - {Part} 3},
  date = {2024-05-17},
  url = {https://kdheepak.com/blog/the-basic-building-blocks-of-ratatui-part-3/},
  langid = {en}
}
For attribution, please cite this work as: