Crate tui_scrollbar

Crate tui_scrollbar 

Source
Expand description

Smooth, fractional scrollbars for Ratatui. Part of the tui-widgets suite by Joshka.

ScrollBar demo

Crate badge Docs Badge Deps Badge License Badge Coverage Badge Discord Badge

GitHub Repository · API Docs · Examples · Changelog · Contributing · Crate source

Use this crate when you want scrollbars that communicate position and size more precisely than full-cell glyphs. The widget renders into a Buffer for a given Rect and stays reusable by implementing Widget for &ScrollBar.

§Feature highlights

  • Fractional thumbs: render 1/8th-cell steps for clearer position/size feedback.
  • Arrow endcaps: optional start/end arrows with click-to-step support.
  • Backend-agnostic input: handle pointer + wheel events without tying to a backend.
  • Stateless rendering: render via Widget for &ScrollBar with external state.
  • Metrics-first: ScrollMetrics exposes pure geometry for testing and hit testing.

§Why not Ratatui’s scrollbar?

Ratatui’s built-in scrollbar favors simple full-cell glyphs and a stateful widget workflow. This crate chooses fractional glyphs for more precise thumbs, keeps rendering stateless, and exposes a small interaction API plus pure metrics so apps can control behavior explicitly.

§Installation

cargo add tui-scrollbar

§Quick start

This example renders a vertical ScrollBar into a Buffer using a fixed track size and offset. Use it as a minimal template when you just need a thumb and track on screen. If you prefer named arguments, use ScrollLengths.

use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::widgets::Widget;
use tui_scrollbar::{ScrollBar, ScrollBarArrows, ScrollLengths};

let area = Rect::new(0, 0, 1, 6);
let lengths = ScrollLengths {
    content_len: 120,
    viewport_len: 30,
};
let scrollbar = ScrollBar::vertical(lengths)
    .arrows(ScrollBarArrows::Both)
    .offset(45);

let mut buffer = Buffer::empty(area);
scrollbar.render(area, &mut buffer);

§Conceptual overview

The scrollbar works in three pieces:

  1. Your app owns content_len, viewport_len, and offset (lengths along the scroll axis).
  2. ScrollMetrics converts those values into a thumb position and size.
  3. ScrollBar renders the track + thumb using fractional glyphs.

Most apps update offset in response to input events and re-render each frame.

§Units and subcell conversions

content_len, viewport_len, and offset are measured in logical units along the scroll axis. For many apps, those units are items or lines. The ratio between viewport_len and content_len is what matters, so any consistent unit works.

Zero lengths are treated as 1.

§Layout integration

This example shows how to reserve a column for a vertical ScrollBar alongside your content. Use the same pattern for a horizontal ScrollBar by splitting rows instead of columns.

use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Constraint, Layout, Rect};
use ratatui_core::widgets::Widget;
use tui_scrollbar::{ScrollBar, ScrollLengths};

let area = Rect::new(0, 0, 12, 6);
let [content_area, bar_area] = area.layout(&Layout::horizontal([
    Constraint::Fill(1),
    Constraint::Length(1),
]));

let lengths = ScrollLengths {
    content_len: 400,
    viewport_len: 80,
};
let scrollbar = ScrollBar::vertical(lengths).offset(0);

let mut buffer = Buffer::empty(area);
scrollbar.render(bar_area, &mut buffer);

§Interaction loop

This pattern assumes you have enabled mouse capture in your terminal backend and have the scrollbar Rect (bar_area) from your layout each frame. Keep a ScrollBarInteraction in your app state so drag operations persist across draws. Mouse events are handled via ScrollBar::handle_mouse_event, which returns a ScrollCommand to apply.

use ratatui_core::layout::Rect;
use tui_scrollbar::{ScrollBar, ScrollBarInteraction, ScrollCommand, ScrollLengths};

let bar_area = Rect::new(0, 0, 1, 10);
let lengths = ScrollLengths {
    content_len: 400,
    viewport_len: 80,
};
let scrollbar = ScrollBar::vertical(lengths).offset(0);
let mut interaction = ScrollBarInteraction::new();
let mut offset = 0;

if let Event::Mouse(event) = event::read()? {
    if let Some(ScrollCommand::SetOffset(next)) =
        scrollbar.handle_mouse_event(bar_area, event, &mut interaction)
    {
        offset = next;
    }
}

§Metrics-first workflow

This example shows how to compute thumb geometry without rendering via ScrollMetrics. It’s useful for testing, hit testing, or when you want to inspect thumb sizing directly.

use tui_scrollbar::{ScrollLengths, ScrollMetrics, SUBCELL};

let track_cells = 12;
let viewport_len = track_cells * SUBCELL;
let content_len = viewport_len * 6;
let lengths = ScrollLengths {
    content_len,
    viewport_len,
};
let metrics = ScrollMetrics::new(lengths, 0, track_cells as u16);
assert!(metrics.thumb_len() >= SUBCELL);

§Glyph selection

The default glyphs include Symbols for Legacy Computing so the thumb can render upper/right partial fills that are missing from the standard block set. Use GlyphSet when you want to switch to a glyph set that avoids legacy symbols.

use tui_scrollbar::{GlyphSet, ScrollBar, ScrollLengths};

let lengths = ScrollLengths {
    content_len: 10,
    viewport_len: 5,
};
let scrollbar = ScrollBar::vertical(lengths).glyph_set(GlyphSet::unicode());

§API map

§Widgets

  • ScrollBar: renders vertical or horizontal scrollbars with fractional thumbs.

§Supporting types

§Enums and events

§Features

  • crossterm: enables crossterm mouse events (latest supported version, currently crossterm 0.29).
  • crossterm_0_28: enables crossterm mouse events using crossterm 0.28.
  • crossterm_0_29: enables crossterm mouse events using crossterm 0.29.

When multiple crossterm versions are enabled, the latest one is used. The selected version is re-exported as tui_scrollbar::crossterm.

§Important

§See also

§More widgets

For the full suite of widgets, see tui-widgets.

§Feature flags

  • crossterm — Enables ScrollBar::handle_mouse_event for crossterm mouse events (latest supported version, currently crossterm 0.29).
  • crossterm_0_28 — Enables ScrollBar::handle_mouse_event using crossterm 0.28.
  • crossterm_0_29 — Enables ScrollBar::handle_mouse_event using crossterm 0.29 (preferred when multiple are enabled).

Re-exports§

pub use ::crossterm_0_29 as crossterm;

Structs§

GlyphSet
Glyphs used to render the track, arrows, and thumb.
PointerEvent
Pointer input in terminal cell coordinates.
ScrollBar
A proportional scrollbar widget with fractional thumb rendering.
ScrollBarInteraction
Drag state that should persist between frames.
ScrollLengths
Bundle content and viewport lengths to avoid ambiguous arguments.
ScrollMetrics
Precomputed values for proportional scrollbars.
ScrollWheel
Scroll wheel input with an axis and a signed delta.

Enums§

CellFill
Describes how much of a single cell is covered by the thumb.
HitTest
Whether a position lands on the thumb or track.
PointerButton
Pointer button used for interaction.
PointerEventKind
Kind of pointer interaction.
ScrollAxis
Axis for scroll wheel events.
ScrollBarArrows
Which arrow endcaps to render on the track.
ScrollBarOrientation
Axis the scrollbar is laid out on.
ScrollCommand
Action requested by a pointer or wheel event.
ScrollEvent
Backend-agnostic input event for a scrollbar.
TrackClickBehavior
Behavior when the user clicks on the track outside the thumb.

Constants§

SUBCELL
Number of subcells in a single terminal cell.