tui-file-explorer
A keyboard-driven, two-pane file manager widget for Ratatui.
Use it as an embeddable library widget or run it as the standalone tfe CLI tool.
Preview
Navigation · Search · Sort — cargo run --example basic

File Operations — copy, cut, paste, delete across two panes · cargo run --bin tfe

27 Live Themes — cargo run --example theme_switcher

Features
- 🗂️ Two-pane layout — independent left and right explorer panes,
Tabto switch focus - 📋 File operations — copy (
y), cut (x), paste (p), and delete (d) between panes - 🔍 Incremental search — press
/to filter entries live as you type - 🔃 Sort modes — cycle
Name → Size ↓ → Extensionwiths - 🎛️ Extension filter — only matching files are selectable; dirs are always navigable
- 👁️ Toggle hidden dot-file visibility with
. - ⌨️ Full keyboard navigation: arrow keys, vim keys (
h/j/k/l),PgUp/PgDn,g/G - 🎨 27 named themes — Catppuccin, Dracula, Nord, Tokyo Night, Kanagawa, Gruvbox, and more
- 🎛️ Live theme panel — press
tto open a side panel,[/]to cycle themes - 🔧 Fluent builder API for ergonomic embedding
- 🖥️ Standalone
tfebinary with full shell-pipeline integration - ✅ Lean library — only
ratatui+crosstermrequired (clapis opt-out)
Stats
| Metric | Value |
|---|---|
| Library dependencies | 2 (ratatui, crossterm) |
| Named colour themes | 27 |
| Sort modes | 3 (Name, Size ↓, Extension) |
| File operations | 4 (copy, cut, paste, delete) |
| Key bindings | 20+ |
| File-type icons | 50+ extensions mapped |
| Public API surface | 6 types, 4 free functions |
| Unit tests | 93 |
Installation
As a library
[]
= "0.1"
= "0.30"
Library-only (no clap-powered CLI binary):
[]
= { = "0.1", = false }
As a CLI tool
Installs the tfe binary onto your PATH.
Quick Start
use ;
use ;
// 1. Create once — e.g. in your App::new
let mut explorer = builder
.allow_extension
.allow_extension
.sort_mode // largest files first
.show_hidden
.build;
// 2. Inside Terminal::draw:
// render(&mut explorer, frame, frame.area());
// 3. Inside your key-handler:
let key = new;
match explorer.handle_key
Key Bindings
Navigation
| Key | Action |
|---|---|
↑ / k |
Move cursor up |
↓ / j |
Move cursor down |
PgUp |
Jump up 10 rows |
PgDn |
Jump down 10 rows |
g / Home |
Jump to top |
G / End |
Jump to bottom |
Enter / → / l |
Descend into directory or confirm file |
Backspace / ← / h |
Ascend to parent directory |
Tab |
Switch active pane (left ↔ right) |
w |
Toggle two-pane ↔ single-pane layout |
Explorer actions
| Key | Action |
|---|---|
/ |
Activate incremental search |
s |
Cycle sort mode (Name → Size ↓ → Extension) |
. |
Toggle hidden (dot-file) entries |
Esc |
Clear search (if active), then dismiss |
q |
Dismiss when search is not active |
File operations
| Key | Action |
|---|---|
y |
Yank — mark highlighted entry for copy |
x |
Cut — mark highlighted entry for move |
p |
Paste — copy/move clipboard into the other pane's directory |
d |
Delete — remove highlighted entry (asks for confirmation) |
Layout & theme controls
| Key | Action |
|---|---|
w |
Toggle two-pane ↔ single-pane layout |
t |
Next theme |
T |
Toggle theme panel (right sidebar) |
[ |
Previous theme |
Search mode (after pressing /)
| Key | Action |
|---|---|
| Any character | Append to query — list filters live |
Backspace |
Remove last character; empty query exits search |
Esc |
Clear query and exit search |
↑ / ↓ / Enter |
Navigate the filtered results normally |
Two-Pane File Manager
The tfe binary opens a split-screen file manager with a left and right pane.
┌─── Left Pane (active) ──────────┬─── Right Pane ───────────────────┐
│ 📁 ~/projects/tui-file-explorer │ 📁 ~/projects/tui-file-explorer │
├─────────────────────────────────┤──────────────────────────────────┤
│ ▶ 📁 src/ │ 📁 src/ │
│ 📁 examples/ │ 📁 examples/ │
│ 📄 Cargo.toml │ 📄 Cargo.toml │
│ 📄 README.md │ 📄 README.md │
├─────────────────────────────────┴──────────────────────────────────┤
│ 📋 Copy: main.rs Tab pane y copy x cut p paste d del │
└────────────────────────────────────────────────────────────────────┘
- The active pane renders with your full theme accent; the inactive pane dims its borders so focus is always clear at a glance
- Press
Tabto switch which pane has keyboard focus - Press
wto collapse to a single pane — the hidden pane keeps its state and reappears when you presswagain - Press
t/[to cycle themes forward / backward; pressTto open the theme panel - Each pane navigates independently — scroll to different directories and use one as source, one as destination
File Operations (Copy / Cut / Paste / Delete)
The classic Midnight Commander source-to-destination workflow:
- Navigate to the file you want in the active pane
yto yank (copy) orxto cut it — the status bar confirms what is in the clipboardTabto switch to the other pane and navigate to the destination directorypto paste — the file is copied (or moved for cut) into that pane's directory
Active pane: ~/projects/src/ Other pane: ~/backup/
▶ main.rs ← press y ← press p here → main.rs appears
If the destination file already exists a confirmation modal appears asking whether to overwrite. Delete (d) also shows a modal before any data is removed.
All file operations support directories — copy and delete both recurse automatically.
Incremental Search
Press / to activate search mode. The footer transforms into a live input bar showing your query. Entries are filtered in real-time using a case-insensitive substring match on the file name.
// Inspect search state programmatically:
println!;
println!;
Behaviour details:
Backspaceon a non-empty query pops the last characterBackspaceon an empty query deactivates search without dismissingEscclears the query and deactivates search — a secondEsc(when search is already inactive) dismisses the explorer entirely- Search is automatically cleared when navigating into a subdirectory or ascending to a parent
Sort Modes
Press s to cycle through three sort modes, or set one directly:
use ;
let mut explorer = new;
explorer.set_sort_mode; // largest files first
println!; // "size ↓"
| Mode | Trigger | Description |
|---|---|---|
SortMode::Name |
s (1st press) |
Alphabetical A → Z — the default |
SortMode::SizeDesc |
s (2nd press) |
Largest files first |
SortMode::Extension |
s (3rd press) |
Grouped by extension, then by name |
Directories always sort alphabetically among themselves regardless of the active mode. The current sort mode is shown in the footer status panel at all times.
Extension Filtering
Only files whose extension matches the filter are selectable. Directories are always shown and always navigable, regardless of the filter.
use FileExplorer;
// Builder API (preferred)
let explorer = builder
.allow_extension
.allow_extension
.build;
// Or replace the filter at runtime — reloads the listing immediately
explorer.set_extension_filter;
// Pass an empty filter to allow all files
explorer.set_extension_filter;
Non-matching files are shown dimmed so the directory structure remains visible. Attempting to confirm a non-matching file shows a status message in the footer.
Builder API
FileExplorer::builder gives a fluent, chainable construction API:
use ;
let explorer = builder
.allow_extension // add one extension at a time
.allow_extension
.show_hidden // show dot-files on startup
.sort_mode // initial sort order
.build;
Or pass the full filter list at once:
let explorer = builder
.extension_filter
.build;
The classic FileExplorer::new(dir, filter) constructor is still available and fully backwards-compatible.
Theming
Every colour used by the widget is overridable through the Theme struct. Pass a Theme to render_themed instead of render:
use ;
use Color;
let theme = default
.brand // widget title
.accent // borders & current path
.dir // directory names & icons
.sel_bg // highlighted-row background
.success // status bar & selectable files
.match_file;
terminal.draw?;
Named presets (27 included)
All presets are available as associated constructors on Theme:
use Theme;
let t = dracula;
let t = nord;
let t = catppuccin_mocha;
let t = catppuccin_latte;
let t = tokyo_night;
let t = tokyo_night_storm;
let t = gruvbox_dark;
let t = kanagawa_wave;
let t = kanagawa_dragon;
let t = moonfly;
let t = oxocarbon;
let t = grape;
let t = ocean;
let t = neon;
// Iterate the full catalogue (name, description, theme):
for in all_presets
Full preset list:
Default · Dracula · Nord · Solarized Dark · Solarized Light · Gruvbox Dark · Gruvbox Light · Catppuccin Latte · Catppuccin Frappé · Catppuccin Macchiato · Catppuccin Mocha · Tokyo Night · Tokyo Night Storm · Tokyo Night Light · Kanagawa Wave · Kanagawa Dragon · Kanagawa Lotus · Moonfly · Nightfly · Oxocarbon · Grape · Ocean · Sunset · Forest · Rose · Mono · Neon
Palette constants
The default colours are exported as pub const values for use alongside complementary widgets:
| Constant | Default | Used for |
|---|---|---|
C_BRAND |
Rgb(255, 100, 30) |
Widget title |
C_ACCENT |
Rgb(80, 200, 255) |
Borders, current-path text |
C_SUCCESS |
Rgb(80, 220, 120) |
Selectable files, status bar |
C_DIM |
Rgb(120, 120, 130) |
Hints, non-matching files |
C_FG |
White |
Default foreground |
C_SEL_BG |
Rgb(40, 60, 80) |
Selected-row background |
C_DIR |
Rgb(255, 210, 80) |
Directory names & icons |
C_MATCH |
Rgb(80, 220, 120) |
Extension-matched file names |
Examples
basic
examples/basic.rs — a fully self-contained Ratatui app demonstrating:
- Builder API and full event loop
- All
ExplorerOutcomevariants - Custom
Theme - Optional CLI extension-filter arguments
# Browse everything
# Only .rs and .toml files are selectable
theme_switcher
examples/theme_switcher.rs — live theme switching without restarting, with a sidebar showing the full theme catalogue.
| Key | Action |
|---|---|
Tab |
Next theme |
Shift+Tab |
Previous theme |
↑/↓/j/k |
Navigate file list |
Enter |
Descend / select |
Backspace |
Ascend |
. |
Toggle hidden files |
/ |
Search |
s |
Cycle sort mode |
Esc / q |
Quit |
Example Demos
Navigation & Search
Run: cargo run --example basic

Demonstrates directory navigation, incremental search (/), sort mode cycling (s), hidden-file toggle (.), directory descent and ascent, and file selection.
Incremental Search
Run: cargo run --example basic then press /

Shows the full search lifecycle: activate with /, type to filter live, use Backspace to narrow or clear, and Esc to cancel without dismissing the explorer.
Sort Modes
Run: cargo run --example basic then press s

Demonstrates Name → Size ↓ → Extension → Name cycling, combined with search, and sort persistence across directory navigation.
Extension Filter
Run: cargo run --example basic -- rs toml

Only .rs and .toml files are selectable; all other files appear dimmed. The footer reflects the active filter at all times.
File Operations
Run: cargo run --bin tfe

Demonstrates the full copy then paste and cut (move) then paste workflows across both panes, followed by a delete with confirmation modal. The clipboard status bar updates live after each y / x / p keystroke, and an overwrite-prompt appears when the destination file already exists.
Theme Switcher
Run: cargo run --example theme_switcher

Live theme cycling through all 27 named palettes with a real-time sidebar showing the catalogue and the active theme's description.
Pane Toggle
Run: cargo run --bin tfe

Demonstrates the three layout controls in sequence:
Tab— switch keyboard focus between the left and right pane; each pane navigates independently so you can be in different directories at the same timew— collapse to single-pane (the active pane expands to full width) and back to two-pane (the hidden pane reappears with its cursor position preserved)T— open and close the theme-picker sidebar; uset/[to cycle themes while the panel is open; both panes remain fully navigable with the panel visible
Demo Quick Reference
| Demo | Command | Highlights |
|---|---|---|
| Navigation + Search | cargo run --example basic |
All key bindings, search, sort, selection |
| Extension filter | cargo run --example basic -- rs toml |
Dimmed non-matching files, footer status |
| Incremental search | cargo run --example basic → / |
Live filtering, backspace, Esc behaviour |
| Sort modes | cargo run --example basic → s |
Three modes, combined with search |
| File operations | cargo run --bin tfe |
Copy, cut, paste, delete, overwrite modal |
| Theme switcher | cargo run --example theme_switcher |
27 live themes, sidebar catalogue |
| Pane toggle | cargo run --bin tfe |
Tab focus-switch, w single/two-pane, T theme panel |
CLI Usage
| Flag | Description |
|---|---|
[PATH] |
Starting directory (default: current directory) |
-e, --ext <EXT> |
Only select files with this extension (repeatable) |
-H, --hidden |
Show hidden dot-files on startup |
-t, --theme <THEME> |
Colour theme — see --list-themes |
--list-themes |
Print all 27 available themes and exit |
--show-themes |
Open the theme panel on startup (T toggles it at runtime) |
--single-pane |
Start in single-pane mode (default is two-pane; toggle at runtime with w) |
--print-dir |
Print the selected file's parent directory instead of the full path |
-0, --null |
Terminate output with a NUL byte (for xargs -0) |
-h, --help |
Show help |
-V, --version |
Show version |
Exit codes
| Code | Meaning |
|---|---|
0 |
File selected — path printed to stdout |
1 |
Dismissed (Esc / q) without selecting |
2 |
Bad arguments or I/O error |
Shell integration
# Open the selected file in $EDITOR
|
# cd into the directory containing the selected file
# Select a Rust source file and edit it
|
# Start with the Catppuccin Mocha theme and the theme panel open
# Start in single-pane mode (useful for narrow terminals or shell pipelines)
# List all available themes
# NUL-delimited output (safe for filenames with spaces or newlines)
|
Theme names are case-insensitive and hyphens/spaces are interchangeable:
catppuccin-mocha,Catppuccin Mocha, andcatppuccin mochaall resolve to the same preset.
Public API
The public surface is intentionally narrow for stability:
| Item | Kind | Description |
|---|---|---|
FileExplorer |
struct |
Core state machine — cursor, entries, search, sort state |
FileExplorerBuilder |
struct |
Fluent builder for FileExplorer |
ExplorerOutcome |
enum |
Result of handle_key — Selected, Dismissed, Pending, Unhandled |
FsEntry |
struct |
A single directory entry (name, path, size, extension, is_dir) |
SortMode |
enum |
Name | SizeDesc | Extension |
Theme |
struct |
Colour palette with builder methods and 27 named presets |
render |
fn |
Render using the default theme |
render_themed |
fn |
Render with a custom Theme |
entry_icon |
fn |
Map an FsEntry to its Unicode icon |
fmt_size |
fn |
Format a byte count as a human-readable string (1.5 KB) |
Module Layout
Library (src/lib.rs re-exports)
| Module | Contents |
|---|---|
types |
FsEntry, ExplorerOutcome, SortMode — data types only, no I/O |
palette |
Palette constants + Theme builder + 27 named presets |
explorer |
FileExplorer, FileExplorerBuilder, entry_icon, fmt_size |
render |
render, render_themed — pure rendering, no state |
Because rendering is fully decoupled from state, you can slot the explorer into any Ratatui layout, render it conditionally as an overlay, or build a completely custom renderer by reading FileExplorer's public fields directly.
Binary (tfe CLI, not part of the public library API)
| Module | Contents |
|---|---|
main |
Cli struct (argument parsing), run(), run_loop() — thin entry-point only |
app |
App state, Pane, ClipOp, ClipboardItem, Modal, handle_event |
ui |
draw(), render_theme_panel(), render_action_bar(), render_modal() |
fs |
copy_dir_all(), emit_path(), resolve_output_path() |
persistence |
AppState, load_state(), save_state(), resolve_theme_idx() |
Generating Demo GIFs
Install VHS then run:
# Generate all GIFs at once (requires just)
# Or individually
GIFs are written to examples/vhs/generated/ and tracked with Git LFS.
Development
Prerequisites
- Rust 1.74.0 or later
just— task runnergit-cliff— changelog generatorvhs— GIF recorder (optional, for demos)
Common tasks
License
MIT — see LICENSE for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Acknowledgments
Built for the Ratatui ecosystem.
Special thanks to the Ratatui team for an outstanding TUI framework.