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

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
Widgetfor&ScrollBarwith 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 Buffer;
use Rect;
use Widget;
use ;
let area = new;
let lengths = ScrollLengths ;
let scrollbar = vertical
.arrows
.offset;
let mut buffer = empty;
scrollbar.render;
Conceptual overview
The scrollbar works in three pieces:
- Your app owns
content_len,viewport_len, andoffset(lengths along the scroll axis). - [
ScrollMetrics] converts those values into a thumb position and size. - [
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 Buffer;
use ;
use Widget;
use ;
let area = new;
let = area.layout;
let lengths = ScrollLengths ;
let scrollbar = vertical.offset;
let mut buffer = empty;
scrollbar.render;
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 Rect;
use ;
let bar_area = new;
let lengths = ScrollLengths ;
let scrollbar = vertical.offset;
let mut interaction = new;
let mut offset = 0;
if let Mouse = read?
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 ;
let track_cells = 12;
let viewport_len = track_cells * SUBCELL;
let content_len = viewport_len * 6;
let lengths = ScrollLengths ;
let metrics = new;
assert!;
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 ;
let lengths = ScrollLengths ;
let scrollbar = vertical.glyph_set;
API map
Widgets
- [
ScrollBar]: renders vertical or horizontal scrollbars with fractional thumbs.
Supporting types
- [
ScrollBarInteraction]: drag capture state for pointer interaction. - [
ScrollMetrics]: pure math for thumb sizing and hit testing. - [
GlyphSet]: glyph selection for track and thumb rendering. - [
ScrollBarArrows]: arrow endcap configuration.
Enums and events
- [
ScrollBarOrientation], [ScrollBarArrows], [TrackClickBehavior] - [
ScrollEvent], [ScrollCommand] - [
PointerEvent], [PointerEventKind], [PointerButton] - [
ScrollWheel], [ScrollAxis]
Features
crossterm: enables thehandle_mouse_eventadapter for crossterm mouse events.
Important
- Zero lengths are treated as 1.
- Arrow endcaps are disabled by default; configure them with [
ScrollBarArrows]. - The default [
GlyphSet] hides the track using spaces; use [GlyphSet::box_drawing] or [GlyphSet::unicode] for a visible track. - The default glyphs use Symbols for Legacy Computing for missing upper/right eighth blocks.
Use [
GlyphSet::unicode] if you need only standard Unicode block elements.
See also
More widgets
For the full suite of widgets, see tui-widgets.
License
Copyright (c) Josh McKinney
This project is licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
See CONTRIBUTING.md.