tui-file-explorer 4.0.0

Embeddable keyboard-driven multi-pane file explorer for Ratatui
Documentation

tui-file-explorer

Crates.io Documentation CI Release tui-file-explorer installs License: MIT

Keyboard-driven, multi-pane file manager for Ratatui. Install the standalone tfe CLI, or embed FileExplorer, DualPane, or the complete App in an existing Ratatui event loop. See Library quick start.

Preview

Dynamic multi-pane layout

Multi-pane file operations

Basic navigation

Theme switcher

Fuzzy finder

Undo and redo

Size info on and off

Bulk rename

Bookmarks

Git change markers

Archive preview and extract

Preview and inline editor

Picture preview

Search

Sort modes

Extension filter

File operations

Pane toggle

Dual-pane widget

Options panel

Editor picker

Create entries

Features

  • Dynamic multi-pane layout — Tab/Shift+Tab cycle focus, Ctrl+T opens a pane, Ctrl+W closes one, and w toggles focused single-pane mode.
  • File operations — mark multiple entries, copy, move, recursively delete, create, rename, and confirm overwrites.
  • Fast navigation — arrows, Vim keys, paging, top/bottom jumps, incremental search, extension filters, and three sort modes.
  • Find, bookmark, undo — recursive fuzzy finder (f), bookmarks (b/Shift+B), undo/redo (u/Ctrl+R), and bulk rename (Shift+R).
  • Git and archives — change markers next to entries (v), archive listings in the preview, and one-key extraction (Shift+X).
  • Never blocks — previews, folder sizes, git status, searches and slow directory reads run on background threads.
  • Configurable — every key can be rebound in keymap.toml; size info, trash and git markers are switchable via keys, flags and settings.
  • Preview and editing — text with line numbers, images, binary hex dumps, directory summaries, external editors, and a built-in editor.
  • Terminal-native images — Kitty, Sixel, iTerm2, and halfblock fallback through ratatui-image.
  • 90 themes (all of tui-nuances) — live theme picker plus fully customizable Theme values.
  • Persistent CLI state — pane directories, active pane, theme, sort mode, visibility settings, editor, and cd-on-exit.
  • Cross-platform shell integration — bash, zsh, fish, PowerShell, and Nushell on macOS, Linux, and Windows.
  • Optional shared theming — enable the theme feature for tui-nuances integration (off by default).
  • Three library tiers — FileExplorer, DualPane, or full App.

Installation

Library

[dependencies]
tui-file-explorer = "3"
ratatui = "0.30"

Choose only the layers your application needs:

[dependencies]
# Lightweight FileExplorer + DualPane widgets
tui-file-explorer = { version = "3", default-features = false }

# Full embeddable App, without the tfe binary or Clap
tui-file-explorer = { version = "3", default-features = false, features = ["full-app"] }
Feature Adds
preview Image/text/binary/directory/archive preview (loaded on a background thread); implies archive
archive .zip / .tar / .tar.gz listing and path-traversal-safe extraction
persistence Serde-backed AppState loading and saving, keymap.toml parsing
trash TrashFileSystem + AppOptions::use_trash: deletions go to the OS trash / recycle bin
full-app Complete App, panels, operations, preview, and persistence
cli (default) tfe binary and Clap argument parsing

CLI

cargo install tui-file-explorer
tfe

Library quick start

tui-file-explorer is designed for library use as well as the standalone CLI; no separate library crate is required. Choose the state/rendering layer that fits your host application, create it once, render it inside Terminal::draw, and forward keyboard events from your existing event loop. Use default-features = false when the tfe binary and clap are not needed.

API Use when Provides
FileExplorer You want one browser widget Navigation, filtering, sorting, marks, create, rename
DualPane You want a focused two-pane widget Two independent FileExplorers, focus switching, single/dual toggle
App + draw You want the complete tfe experience Arbitrary panes, file operations, panels, preview, editors, persistence-ready state

Full app

use std::path::PathBuf;
use tui_file_explorer::{draw, App, AppOptions};

let mut app = App::new(AppOptions {
    pane_dirs: vec![
        std::env::current_dir()?,
        PathBuf::from("/tmp"),
    ],
    ..AppOptions::default()
});

loop {
    terminal.draw(|frame| draw(&mut app, frame))?;
    if app.handle_event()? {
        break;
    }
}
# Ok::<(), Box<dyn std::error::Error>>(())

If your application already owns terminal events and layout, use dispatch_event plus draw_in:

let outcome = app.dispatch_event(event)?;
terminal.draw(|frame| {
    draw_in(&mut app, frame, host_file_browser_area);
})?;

match outcome {
    AppOutcome::Selected(path) => select(path),
    AppOutcome::Dismissed => close_file_browser(),
    AppOutcome::OpenEditor { path, editor } => open_editor(path, editor),
    AppOutcome::OperationRequested(request) => worker.send(request)?,
    AppOutcome::Changed(events) => host.handle_changes(events),
    AppOutcome::Continue | AppOutcome::Ignored => {}
}

AppViewOptions controls action/debug bars, panel widths, preview allocation, minimum pane width, and HintLayout. Its default Auto mode stacks action hints vertically below 90 columns. AppCommand and KeyBindings support command palettes and custom shortcuts without replacing the state machine. See the complete embed example.

Single-pane widget

use tui_file_explorer::{render_themed, ExplorerOutcome, FileExplorer, SortMode, Theme};

let mut explorer = FileExplorer::builder(std::env::current_dir()?)
    .allow_extension("rs")
    .allow_extension("toml")
    .show_hidden(false)
    .show_sizes(true)      // file + folder size column
    .show_dir_sizes(false) // folders show item counts, no recursive walk
    .sort_mode(SortMode::Name)
    .build();

terminal.draw(|frame| {
    render_themed(&mut explorer, frame, frame.area(), &Theme::default());
})?;

match explorer.handle_key(key) {
    ExplorerOutcome::Selected(path) => println!("{}", path.display()),
    ExplorerOutcome::Dismissed => {}
    _ => {}
}
# Ok::<(), Box<dyn std::error::Error>>(())

Dual-pane widget

use std::path::PathBuf;
use tui_file_explorer::{render_dual_pane_themed, DualPane, DualPaneOutcome, Theme};

let mut dual = DualPane::builder(std::env::current_dir()?)
    .right_dir(PathBuf::from("/tmp"))
    .show_hidden(false)
    .build();

terminal.draw(|frame| {
    render_dual_pane_themed(&mut dual, frame, frame.area(), &Theme::default());
})?;

match dual.handle_key(key) {
    DualPaneOutcome::Selected(path) => println!("{}", path.display()),
    DualPaneOutcome::Dismissed => {}
    _ => {}
}
# Ok::<(), Box<dyn std::error::Error>>(())

Custom backends implement FileSystem and are passed through FileExplorerBuilder::filesystem, DualPaneBuilder::filesystem, or PaneOptions::filesystem. Checked construction and reload are available through try_build and try_reload.

For non-blocking copy, move, and delete, set OperationMode::Deferred. dispatch_key then returns AppOutcome::OperationRequested; execute it on a worker with execute_operation_with_progress (or execute_operation_with for a custom filesystem), feed progress through apply_operation_progress, and finish with apply_operation_result. Operation IDs reject stale progress/results, and cancel_operation invalidates late worker responses.

Host-owned persistence uses app.snapshot(), app.restore(...), load_state_from, and save_state_to.

See examples/ and the complete API on docs.rs.

Theming

Theming is shared with the other components by the same author through tui-nuances (git dependency until it is published). This is optional and requires the theme feature (not enabled by default, so no extra dependency otherwise):

tui-file-explorer = { version = "3", features = ["theme"] }

The explorer keeps its own nine-colour Theme as the style type; it maps from a shared NuancesTheme (re-exported tui_nuances::Theme). Without a theme the default look is unchanged.

use ratatui::style::Color;
use tui_file_explorer::{render_nuanced, NuancesTheme, Theme, ThemeBuilder};

let shared = NuancesTheme::by_name("tokyo-night").unwrap_or_default();

// terminal.draw(|f| render_nuanced(&mut explorer, f, f.area(), &shared));
let theme: Theme = (&shared).into();                       // From<&NuancesTheme>
// Explicit colours win over the theme, in any call order:
let tuned = ThemeBuilder::new().theme(&shared).brand(Color::Magenta).build();

Role mapping: brand←title, accent←accent, success/match_file←success, dim←muted, fg←foreground, sel_bg←selection_bg, dir←primary, bg←background. To pass your app's theme, implement ThemeProvider (Theme::from_provider(&app_theme)) or impl From<&AppTheme> for NuancesTheme. AppBuilder::theme accepts anything Into<Theme>, including &NuancesTheme. With --features theme, the basic, dual_pane and theme_switcher examples cycle shared themes with t / T (theme_switcher requires it).

Keyboard controls

Area Keys Action
Navigation ↑/k, ↓/j Move cursor
Navigation ←/h/Backspace Ascend to parent
Navigation → Descend into directory; move down when entry is a file
Navigation Enter/l Descend or confirm file
Navigation PgUp/PgDn, g/G Page, top, bottom
Panes Tab/Shift+Tab Next/previous pane
Panes Ctrl+T/Ctrl+W Open/close pane
Panes w Toggle focused single-pane/multi-pane layout
Search /, Backspace, Esc Start, edit, or cancel incremental search
View s, ., z, Shift+Z Cycle sort, toggle hidden entries, toggle all size info, toggle recursive folder sizes (off = item counts, no directory walk)
Selection Space Mark/unmark current entry
File operations x, p, d Cut, paste, delete
File operations n, N, r New directory, new file, rename
File operations Shift+R Bulk rename: edit all (or marked) names in your editor, one per line
File operations u, Ctrl+R Undo / redo the last rename / create / move / copy / bulk rename / extract (and trash-deletes where the OS trash can restore)
Find f Recursive fuzzy finder; Enter jumps to the match
Bookmarks b, Shift+B Bookmark the current folder; open the bookmark list (Del removes one)
Git v Toggle git change markers (M A ? D R U; folders show their children's state)
Archives Shift+X Extract the highlighted .zip / .tar / .tar.gz / .tgz into a sibling folder; the preview lists archive contents
Preview P, Ctrl+J/Ctrl+K Toggle and scroll preview
Editors e, i, Shift+E External editor, inline editor, editor picker
Panels T, Shift+O Theme picker, options panel
Theme t, [ Next/previous theme
Shell Shift+C Toggle cd-on-exit
Exit Esc/q, Ctrl+C Dismiss or exit

Search and text-input modes consume printable keys before global shortcuts. Delete and overwrite operations require confirmation.

CLI usage

tfe [OPTIONS] [PATH]

Common options:

Option Description
-e, --ext <EXT> Restrict selectable files by extension; repeatable
-H, --hidden Show dotfiles on startup
-t, --theme <THEME> Select persisted or named theme
--list-themes Print all theme names
--show-themes Open theme picker at startup
--single-pane Start with only focused pane visible
--sizes / --no-sizes Show / hide all file and folder size info (persisted)
--trash / --no-trash Send deletions to the OS trash (default) or delete permanently (persisted)
--git / --no-git Show / hide git change markers (persisted)
--dump-keymap Print a complete keymap.toml with the built-in bindings
--dir-sizes / --no-dir-sizes Compute / skip recursive folder sizes; off shows item counts (persisted)
--editor <EDITOR> Select external editor or command
--cd / --no-cd Enable/disable persisted cd-on-exit
--init <SHELL> Install shell wrapper
--print-dir Print selected file's parent directory
-0, --null Use NUL-delimited output
--info / --doctor Show environment info or run diagnostics
-v, --verbose Enable startup logs, log file, and debug panel

Run tfe --help for full option documentation.

Custom key bindings

Every binding can be changed in keymap.toml (next to settings.json; tfe --info shows the directory). Start from tfe --dump-keymap > keymap.toml:

[explorer]                 # per-pane navigation
move_up  = ["up", "k"]
dismiss  = ["esc"]         # drops the default `q`

[app]                      # app-wide commands
undo     = ["u", "ctrl+z"]
redo     = ["ctrl+r"]
cut      = ["m"]           # `x` stops cutting

A command listed in the file replaces all of its default keys; commands you do not list keep theirs. Keys are single characters or esc enter tab backspace up down left right home end pageup pagedown space f1…f24 with ctrl+, alt+ or shift+ prefixes. Mistakes (unknown command, bad key) are reported on stderr and skipped — they never stop tfe from starting.

Embedding apps get the same machinery through Keymap / FileExplorerBuilder::keymap(..) and KeyBindings::remap(..), or App::apply_keymap_config(..).

Shell integration

tfe renders on stderr and reserves stdout for selected paths, cd-on-exit, and one-time shell setup. Enable directory changes and install the wrapper once:

tfe --cd
tfe --init zsh       # or bash, fish, powershell, nushell

Source the file reported by --init, or open a new terminal. Afterwards, dismissing with Esc/q changes the parent shell to the active pane's directory.

Use command tfe to bypass the wrapper when piping output:

command tfe -e rs | xargs -r nvim
command tfe -0 | xargs -0 wc -l

Exit codes:

Code Meaning
0 Selection or successful cd-on-exit output
1 Dismissed without cd-on-exit output
2 Invalid arguments or I/O failure

Main capabilities

Multi-pane workflow

A fresh CLI session starts with two panes; later sessions restore the persisted pane layout. Each pane keeps its own directory, cursor, marks, search, sort, and size-display state. New panes clone the active pane's directory and display settings; at least one pane always remains open.

Typical source-to-destination copy:

  1. Mark one or more entries with Space.
  2. Move to another pane with Tab, or create one with Ctrl+T.
  3. Navigate to destination.
  4. Press p to copy. Use x before switching panes for a move.

Search, filtering, and sorting

  • / starts case-insensitive incremental filename search.
  • Esc clears active search before dismissing explorer.
  • Extension filters dim non-matching files while leaving directories navigable.
  • s cycles Name → Size ↓ → Extension.
  • Directories remain grouped before files.

Preview and editors

P opens preview for text, supported images (PNG, JPEG, GIF, BMP, WebP), binary files, and directories. i opens built-in text editor; Ctrl+S saves and Esc returns. e launches configured external editor.

Themes

Use t/[ to cycle or T to open picker. Forty-three presets include Catppuccin, Dracula, Nord, Solarized, Gruvbox, Tokyo Night, Kanagawa, Rosé Pine, Flexoki, and others. Customize every color with Theme builder methods.

Examples

Example Command Purpose
Basic widget cargo run --example basic Navigation, search, sorting, extension filters
Dual pane cargo run --example dual_pane -- /tmp DualPane library API and independent directories
Theme switcher cargo run --example theme_switcher Live preset switching
Options cargo run --example options Options and editor panels
Editor picker cargo run --example editor_picker Terminal and GUI editor selection
Open file cargo run --example open_file -- "code --wait" Suspend TUI, edit, resume
Create entries cargo run --example create_entries Create directories/files and rename entries
Full app cargo run --example full Complete App integration
Embedded app cargo run --example embed draw_in, host layout, outcomes, key remapping, deferred operations

Public API

Main re-exports:

Group Items
Single pane FileExplorer, FileExplorerBuilder, ExplorerCommand, ExplorerOutcome, SelectionMode, FsEntry, SortMode, render, render_themed
Dual pane DualPane, DualPaneBuilder, DualPaneActive, DualPaneCommand, DualPaneOutcome, render_dual_pane, render_dual_pane_themed
Full app App, AppBuilder, PaneOptions, AppOutcome, AppEvent, AppCommand, KeyBindings, draw, draw_in, AppViewOptions, HintLayout
Operations FileOperation, OperationRequest, OperationProgress, OperationResult, OperationMode
Filesystems FileSystem, StdFileSystem, SharedFileSystem, ExplorerError
Preview/editor PreviewState, PreviewContent, InlineEditor, EditorAction, render_preview, render_inline_editor
Theme/utilities Theme, entry_icon, fmt_size, copy_dir_all, AppState, load_state, save_state

Modules and detailed method docs: docs.rs/tui-file-explorer.

Demo recordings

Install VHS, then run:

just vhs multi_pane  # one tape
just vhs-all         # every tape

Tapes live in examples/vhs/. Generated 1600×900 GIFs live in examples/vhs/generated/ and are tracked with Git LFS.

Tape Demonstrates
multi_pane Dynamic pane creation/closing, focus cycling, single-pane focus
multi_pane_file_ops Copying marked files between three panes
basic, search, sort, filter Core navigation and discovery
file_ops, create_entries Copy/move/delete and create/rename workflows
preview, picture_preview Text, binary, directory, and image preview
theme_switcher, options, editor_picker Themes and configuration panels
pane_toggle, dual_pane CLI layout and DualPane library widget

Troubleshooting

command tfe --info     # environment, paths, terminal, shell
command tfe --doctor   # pass/warn/fail diagnostics with fixes
command tfe --verbose  # debug panel and $TMPDIR/tfe-debug.log

Use command tfe when diagnostics or pipelines must bypass installed shell wrapper.

Development

Requires Rust 1.90 or newer. Optional tooling: just, git-cliff, and vhs.

just check-all   # format check, clippy with denied warnings, tests
just test-all    # all features and targets
just doc         # build and open API docs
just --list      # all project tasks

Fuzzing (requires nightly and cargo install cargo-fuzz):

cargo +nightly fuzz list
cargo +nightly fuzz run explorer_render

Contributions are welcome; see CONTRIBUTING.md.

License

MIT — see LICENSE.

Acknowledgments

Built for the Ratatui ecosystem.