How to Use TuiLoggerWidget Instead of the Smart Widget in tui-logger

To use the standard widget instead of the smart widget, instantiate TuiLoggerWidget directly with a shared TuiWidgetState, configure it with the same builder methods for styling and output formatting, and call its render method within your ratatui draw loop.

The tui-logger crate provides two primary widgets for rendering log output in terminal user interfaces built with ratatui. While TuiLoggerSmartWidget offers a convenience layout that includes a target selector panel alongside the log view, the standard TuiLoggerWidget provides a cleaner implementation when you only need a dedicated log pane without the additional target management UI.

Architectural Differences Between Standard and Smart Widgets

Understanding the relationship between these components helps clarify when to use each approach.

TuiLoggerWidget (defined in src/widget/standard.rs) handles the core rendering of log records, including formatting timestamps, log levels, and source locations. It manages scrolling and styling but does not include any target selection interface.

TuiLoggerSmartWidget (located in src/widget/smart.rs) acts as a thin wrapper that combines a TuiLoggerWidget with a TuiLoggerTargetWidget (defined in src/widget/target.rs). At runtime, it decides whether to display the target selector based on the current widget state, adding extra layout complexity that you may not need.

Both widgets rely on TuiWidgetState (from src/widget/inner.rs) to hold shared UI state such as scroll position and display filters via an internal Arc<Mutex<_>>.

Minimal Implementation of TuiLoggerWidget

To render a basic log pane without the target selector, create a TuiWidgetState instance and pass it to TuiLoggerWidget using the builder pattern.

use ratatui::{
    backend::Backend,
    layout::Rect,
    style::{Color, Style},
    widgets::{Block, Borders, Widget},
    Terminal,
};
use tui_logger::{init_logger, set_default_level, TuiLoggerWidget, TuiWidgetState, LevelFilter};

fn main() -> anyhow::Result<()> {
    // Initialize the logging backend
    init_logger(LevelFilter::Trace)?;
    set_default_level(LevelFilter::Trace);

    // Create shared UI state with default display level
    let mut state = TuiWidgetState::new()
        .set_default_display_level(LevelFilter::Info);

    // Build the standard widget with formatting options
    let log_widget = TuiLoggerWidget::default()
        .block(Block::bordered().title("Standard Log"))
        .style(Style::default().fg(Color::White))
        .output_separator(':')
        .output_timestamp(Some("%H:%M:%S".to_string()))
        .output_level(Some(tui_logger::TuiLoggerLevelOutput::Abbreviated))
        .state(&state);  // Shares the Arc<Mutex<_>> containing scroll position and filters

    // Render inside your ratatui draw loop
    // term.draw(|frame| {
    //     let area = frame.area();
    //     log_widget.render(area, frame.buffer_mut());
    // })?;

    Ok(())
}

The state(&state) method copies the internal Arc<Mutex<_>> into the widget, allowing it to read the shared log buffer and respond to scroll commands. All builder methods available on TuiLoggerSmartWidget—including style_error, style_debug, output_target, and output_file—work identically on TuiLoggerWidget.

Replacing TuiLoggerSmartWidget in Existing Code

If you are migrating from the smart widget to the standard widget, remove the smart wrapper and call TuiLoggerWidget directly. The following example adapts the pattern found in examples/demo.rs (lines approximately 283-296).

Before (Smart Widget):

TuiLoggerSmartWidget::default()
    .style_error(Style::default().fg(Color::Red))
    .style_debug(Style::default().fg(Color::Green))
    .style_warn(Style::default().fg(Color::Yellow))
    .style_trace(Style::default().fg(Color::Magenta))
    .style_info(Style::default().fg(Color::Cyan))
    .output_separator(':')
    .output_timestamp(Some("%H:%M:%S".to_string()))
    .output_level(Some(TuiLoggerLevelOutput::Abbreviated))
    .output_target(true)
    .output_file(true)
    .output_line(true)
    .state(self.selected_state())
    .render(smart_area, buf);

After (Standard Widget):

TuiLoggerWidget::default()
    .block(Block::bordered().title("Standard Log"))
    .style_error(Style::default().fg(Color::Red))
    .style_debug(Style::default().fg(Color::Green))
    .style_warn(Style::default().fg(Color::Yellow))
    .style_trace(Style::default().fg(Color::Magenta))
    .style_info(Style::default().fg(Color::Cyan))
    .output_separator(':')
    .output_timestamp(Some("%H:%M:%S".to_string()))
    .output_level(Some(TuiLoggerLevelOutput::Abbreviated))
    .output_target(true)
    .output_file(true)
    .output_line(true)
    .state(self.selected_state())
    .render(smart_area, buf);

This change removes the conditional target selector logic while preserving all formatting and output options. The standard widget renders only the log records according to the layout area you provide.

Key Source Files for Reference

When working with the standard widget, these implementation files provide authoritative details on behavior and API:

  • src/widget/standard.rs – Contains the TuiLoggerWidget struct and its Widget trait implementation, including the builder methods for styling and output configuration.
  • src/widget/smart.rs – Demonstrates how the smart widget wraps the standard widget and forwards configuration options.
  • src/widget/inner.rs – Defines TuiWidgetState and the internal state management shared between widgets.
  • src/widget/target.rs – Implements TuiLoggerTargetWidget, which is excluded when using the standard widget directly.
  • examples/demo.rs – Shows side-by-side usage of both widget types in a full application context.

Summary

  • Use TuiLoggerWidget (from src/widget/standard.rs) when you need a plain log pane without the target selector overhead.
  • Share state via TuiWidgetState passed to the state() builder method; this manages scroll position and filtering through an internal Arc<Mutex<_>>.
  • Apply identical configuration using builder methods like style_error, output_timestamp, output_target, and block that work the same way on both widget types.
  • Call render(area, buf) directly since TuiLoggerWidget implements the ratatui Widget trait, fitting seamlessly into any ratatui layout.

Frequently Asked Questions

How do I hide the target selector that appears with TuiLoggerSmartWidget?

Switch to TuiLoggerWidget instead of TuiLoggerSmartWidget. The smart widget explicitly includes TuiLoggerTargetWidget to render the target selector panel, while the standard widget in src/widget/standard.rs renders only the log output. Both widgets accept the same state and configuration builders, so migration requires only changing the struct name and adding a block if desired.

Can I use TuiLoggerWidget without TuiWidgetState?

No. The widget requires access to shared UI state to determine scroll position and which log records to display. You must create a TuiWidgetState (defined in src/widget/inner.rs) and pass it via the state(&state) builder method. This state contains the Arc<Mutex<TuiWidgetInnerState>> that the widget needs to access the global log ring buffer.

What styling options are available for TuiLoggerWidget?

The widget supports per-level styling through methods like style_error, style_warn, style_info, style_debug, and style_trace, each accepting a ratatui Style struct. You can also configure the separator character with output_separator, timestamp format with output_timestamp, and toggle metadata columns like target names, filenames, and line numbers using output_target, output_file, and output_line.

How do I initialize the logging system before creating the widget?

Call init_logger from src/logger/api.rs with a LevelFilter (such as LevelFilter::Trace or LevelFilter::Info) before creating any widgets. This sets up the global logger that TuiLoggerWidget reads from. You can also use set_default_level to control the default verbosity for specific targets before the UI renders.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →