yich29 downloadsA keyboard-driven file explorer with miller columns and live preview, inspired by yazi and ranger. Fuzzy and full-text search, bookmarks, multi-select, and custom openers.
A keyboard-driven file explorer for Obsidian — miller columns, live preview, fuzzy and full-text
search. Inspired by yazi, ranger,
lf and nnn.
If you navigate your vault from the sidebar file tree with a mouse, this is the replacement: a floating three-column browser that opens on the file you are already editing, and that you never have to take your hands off the keyboard to use.

Status: early. In daily use in a 5000-note vault, but the settings schema may still change before 1.0.
Obsidian's built-in File Explorer is a tree in a narrow sidebar. That is fine for clicking, but it makes three things awkward, and this plugin exists for those three:
| Built-in File Explorer | Yazi Explorer | |
|---|---|---|
| Seeing where you are | one narrow column; deep folders scroll sideways | miller columns — parent / current / preview, three levels of context at all times |
| Deciding “is this the file?” | file names only | live preview beside the list: rendered markdown, tables, images, frontmatter |
| Getting there | click, scroll, expand | j k h l, fuzzy file/folder search, full-text search, bookmarks, most-visited |
It is not a replacement for Quick Switcher, which is excellent at “I know the name.” This is for “I want to look around” — browsing, comparing, moving files, and acting on a batch of them.
Not in the community plugin list yet.
With BRAT (recommended — you get updates):
add the beta plugin vi000246/obsidian-yazi.
By hand: download main.js, manifest.json and styles.css from the
latest release, drop them in
<vault>/.obsidian/plugins/yazi-explorer/, reload Obsidian, and enable the plugin.
Alt+E is a good one. (Everything else is reachable from inside, so one hotkey is enough.)? to see every key — and / inside that page to search it.j / k down / up l / Enter / o folder → enter, file → open
h up one folder t open in a new tab
gg / G first / last s / i open in a split
d / u half page q / Esc close
h and l walk up and down the tree; the left column always shows where you came from and the right
one shows what you are about to open. gg / G jump to the ends, d / u move half a page (that is
the Surfingkeys convention — D is delete, not d).
o, l and Enter all open. t opens in a new tab and closes the explorer; gb opens in a
background tab and keeps the explorer open, so you can queue up several files and then carry on.
s and i open in a vertical or horizontal split.
The right column renders markdown the way Reading view does — headings, tables, mermaid diagrams — so you can tell whether it is the file you wanted without opening it.
Plugin blocks (dataviewjs, tasks, meta-bind, query, …) are stripped before rendering, and
note embeds are replaced with a placeholder. Browsing never executes anything from your vault. Toggle
between rendered and raw text with ,p.
Scroll the preview without moving the cursor: J / K (five lines), ^e / ^y (one line),
^d / ^u (half a page), ^f / ^b or PageDown / PageUp (a page). The mouse wheel scrolls
the preview wherever the pointer happens to be, so you never have to move the mouse into it.
go opens the outline of the note under the cursor: its headings, indented by level. Move with
j / k and the preview scrolls to that heading as you go — you see the section before you commit.
Enter opens the note at that heading; t does the same in a new tab. It works from the file view
and from any list (search results, bookmarks), and h takes you back to where you came from.

/ |
filter the folder you are in |
gf |
fuzzy search file names and frontmatter aliases, whole vault |
gd |
fuzzy search folder names |
gt |
full-text search, with the matching lines shown in the preview |
Search has no syntax to memorise. You type words; conditions come from a menu. Press Tab in the
search card to add one — folder, tag, extension, or any frontmatter field you configured. Each
condition becomes a visible chip you can remove, rather than something hidden inside a query string.
The candidate values are read from your vault and come with counts, so you only ever see values that
actually exist, and a typo shows up as “only 1 note has this”.
Searches always have a scope, shown as a chip. ^f changes it, offering candidates from widest to
narrowest: whole vault → each folder above → where you started → each folder below.

gr shows the relations of the note under the cursor, grouped:
PARENT Roadmap 2026
CHILDREN Ship the importer
Write the migration guide
RELATED Prior art — other explorers
▸ 12 Backlinks
▸ 3 Links
Parent and Related are read from frontmatter; the reverse direction is derived, never stored
— Children is "every note whose parent points here", so the two sides can never disagree. Plain
[[links]] that carry no relation type come last, folded into one line each so they cannot bury
the typed groups; anything already shown under a typed group is not repeated there.
Relations are walked exactly like folders. l or Enter on a note enters it: its relations
become the list, the left column becomes the list you just left (the way the parent folder does),
and h steps back. l on a folded ▸ 12 Backlinks line expands it — same key, same idea. The
preview on the right shows the note under the cursor with a strip on top saying which groups it has
and how many, so you know before pressing l whether it leads anywhere. o and t open the note
for real.
Which fields count as relations is configurable; out of the box:
| field | group | reverse group |
|---|---|---|
parent |
Parent | Children |
up |
Up | Down |
related |
Related | (symmetric — shown on both notes) |
blocks |
Blocks | Blocked by |
up is the LYT convention: it points at the map-of-content
note this one belongs under. A field you don't use simply never produces a group, and you can add,
rename or disable fields under Settings → Relations & views.
Values should be [[wikilinks]], or a list of them:
---
parent: "[[Roadmap 2026]]"
related:
- "[[Prior art — other explorers]]"
- "[[Keyboard-first tools]]"
---
Plain text that names a file is understood as a fallback, but wikilinks are what make Obsidian's rename tracking, backlinks and graph work.
Seeing only Links and Backlinks? Then none of your notes carry these frontmatter fields yet —
grstill works, it is just showing the untyped links it found. Relations are read from frontmatter, not from a## Relatedsection in the body.
A search you want back tomorrow can be kept. Run it, then press s and give it a name. gv lists
what you saved:
w In progress, mine file names · status: 3 In Progress · priority: P1 High
r Read later full text · "reading" · in 400 QuickNote
· Blocked file names · blocked
A view stores the words, conditions and scope — not the results, so it is re-run every time and
always reflects the vault as it is now. In the list, Enter runs one; e opens it in the search
card for editing — the card says which view you are editing, Enter saves the changed conditions
back to that view and returns to the list, Esc discards; R renames it; x deletes it (after a
y); and m plus a letter assigns a shortcut — after which that letter runs it straight from the
list.
Saving the same conditions again does not create a twin: it asks whether to rename the existing
view instead, and y opens the name box with the old name filled in. Saving under an existing
name replaces that view's conditions.
Views are editable under Settings → Relations & views too — name, letter, words, folder, and removing conditions. Adding a condition is deliberately only possible in the explorer: there the candidate values are read from your vault and come with counts, so you can see that a value actually exists rather than typing one and hoping.
One hotkey for Open file explorer is enough to use everything, but each list is also its own
command, so you can bind the ones you reach for — with Obsidian's own hotkeys, or a leader-key
plugin, where these work well as ,-sequences:
| command | opens |
|---|---|
| Open file explorer | where you are |
| Open saved views | the view list |
| Open outline / Open relations | the current note's headings / links |
| Open tab list · bookmarks · recent files · most visited | those lists |
| Search: full text · file names · folders | straight into that search |
Three keys leave things, and they mean three different things:
h |
back to where you came from — the list or folder you were in before. At the first layer it stays put. After jumping into a folder from a list, h in that folder returns to the list; deeper folders step up one level as usual. |
Esc |
dismiss the innermost thing first (a suggestion, an input, the help page, a search card…); with nothing left, back one layer; at the first layer, close |
q |
close the explorer, from anywhere |
So if a command opened you straight into a list, that list is the first layer: Esc closes,
h has nowhere to go. If you walked into it from the file view, both take you back there.
Press ? for the full list, then / to search it — matches are highlighted, and typing a section
name (preview, bookmarks) keeps that whole section. Esc clears the search, Esc again closes
the page.
Selection survives folder changes: Space selects and moves down, v / V select a range, ^a /
^r select all or invert. Walk somewhere else, select more, then act on the lot.
y copies, x cuts, p pastes, P pastes over a same-named file. Cut-then-paste is a move, and it
goes through Obsidian, so your [[links]] follow the file. a / A create a note or a folder, R
renames, D deletes to the system trash (it asks first, and deletes the whole selection).
Copy paths with cc (absolute), cd (its folder), cf (file name), cn (name without extension),
cr (vault-relative — what [[links]] use).
m bookmarks the item under the cursor, M the current folder. Both ask what to call it, with the
file name filled in — bookmarks are for recognising, and a file called 2026-09-18.md is not how you
think of it. In the bookmark list, R renames one, m plus a letter assigns a shortcut, and then
' plus that letter jumps there from anywhere.
The same selection keys work inside these lists: Space picks a row and moves down, v / V pick a
range, ^a / ^r select all or invert, and x then removes every selected row — closing tabs,
deleting bookmarks or views, forgetting most-visited entries. Removing more than one always asks
first, including tabs, because X only brings back the last one you closed.
T lists open tabs, b bookmarks, rf recent files (newest first), and z shows most visited —
folders you enter and files you open, ranked together by how often × how recently. The two are easy
to confuse, so: rf is time only and files only; z counts visits, includes folders, and lets what
you stopped using sink.
O opens a menu of openers, and the menu changes depending on whether a file or a folder is under
the cursor. Out of the box you get “default app”, “open the folder” and “show in the file manager”.
Everything else you configure — see below.
Settings → Yazi Explorer. Every section exports and imports as JSON, so a setup can be shared.
O menuEach opener is a label, a key, what it applies to, and an action:
| Action | What it does | Mobile |
|---|---|---|
system |
hand the path to the OS (default app for files, file manager for folders) | ✓ |
reveal |
show it in the file manager, selected | ✓ |
obsidian-command |
open the file, then run an Obsidian command by id | ✓ |
command |
run a command line | desktop only |
command openers take an executable and a list of arguments, one per line. Each line is passed
as a single argument, so paths containing spaces need no quoting or escaping. Placeholders:
{{path}}, {{dir}}, {{relPath}}, {{name}}, {{basename}}, {{ext}}, {{vaultPath}},
{{vaultName}}.
e)Label Visual Studio Code
Key e
Applies to Files only
Action command
Executable code
Arguments --reuse-window
{{path}}
t, Windows)Label Terminal
Key t
Applies to Files and folders
Platform Windows
Action command
Executable wt.exe
Arguments -d
{{dir}}
Use Platform when the command differs per OS: add one entry per platform with the same key, and
each one only appears on its own platform.
A rule says “when this frontmatter field has this value, draw the row like this.” It can replace the bullet with an icon, replace the file name with a field, put a field on the right, add status badges, and dim or pin rows.
Applies when type = diary
Icon field → mood (the emoji you recorded that day)
Title field → title fallback: filename
Secondary basename (the date, small, on the right)
2026-09-13.md then reads as 🤩 A weekend in Jiufen 2026-09-13.
Icon from a kind field via a lookup table, a status badge with a sort order, and low priorities
hidden because drawing them on every row is noise. The status table, priority order, dim/pin
conditions and the preview-panel field order live in the Advanced (JSON) box of the rule —
they are nested data, and a form for them would be thirty input boxes.

The settings tab ships a gallery of examples — each one shows the row it produces before you add
it. The first few match on “this field has a value” rather than on a particular word, so they work
whatever your status or icon values happen to be.
Every part is optional except when. Rules are matched top to bottom and the first match wins, so
put the specific ones above the general ones.
| Key | What it does |
|---|---|
when |
{field} — the field has any value · {field, equals} · {field, in: [...]} |
icon |
{from:"fixed", value} · {from:"field", field} · {from:"map", field, map:{value:icon}, fallback} |
title |
{from:"field", field, fallback:"filename"} — replaces the file name |
subtitle |
{from:"basename"} or {from:"field", field} — small text on the right, drawn only when the title came from a field |
status |
{field} shows the raw value · {field, map:{value:{text, order, dim}}} gives it a glyph, a sort weight, and can dim the row |
priority |
{field, order:{value:0,…}, showMaxOrder} — only values at or below showMaxOrder are drawn |
group |
{field, order:[…]} — the sort weight used by “sort by category” |
dimWhen / pinWhen |
[{field, equals}] — dim the row, or float it to the top |
statusWhen |
[{field, equals, text, dim}] — override the badge (e.g. a file whose source has gone) |
overdue |
{field} — a date field; marks ⏰ when it is in the past and the row is not dimmed |
panel |
["field", {field, label}] — field order in the preview pane's metadata table |
The four fields with tables (icon.map, status.map, priority.order, group.order) are edited as
JSON in the Advanced box of a rule — they are nested data, and a form for them would be thirty
input boxes.
Values outside your table show as ❓ plus the raw value. That is deliberate: it makes drift
visible instead of quietly falling back to a normal-looking icon. A status with no table has
nothing to drift from, so it just shows the value.
Which frontmatter fields become search conditions, each with a direct key (pressed with Ctrl while
searching) and an icon.
The keys inside the explorer are fixed, because they are an interlocking set: d/u are half-page,
which is why delete is D; y yanks files, which is why copying a path lives under c. Change one
in isolation and the reason for the others disappears. Press ? inside the explorer for the full
list, and / in that page to search it.
What you can do is alias one key to another, the same idea as Surfingkeys' map:
map J gt # J now does what g then t did (full-text search)
map w O # w now does what O did
unmap S # S does nothing
# lines starting with # are comments
Keys you cannot type go in angle brackets: <Space> <Enter> <Esc> <Tab> <Backspace> <Up> <Down> <Left> <Right> <PageUp> <PageDown>. The left side is always a single key; the right side may be a
sequence. A line that does not parse is listed under the box with the reason, rather than silently
doing nothing.
| Move | j k h l g(gg gt gf gd go gr gb) G d u |
| Open | o t s i O(then an opener key) |
| Lists | T b z r(rf) |
| Bookmarks | m M ' |
| Select | <Space> v V Esc |
| Clipboard | y x p P Y X |
| Files | a A R D c(cc cd cf cn cr) |
| Sort | S(then a sort key) |
| Views | gv s(in search results) |
| Preview | J K PageDown PageUp ,(,x ,X ,p) |
| Other | / ? q |
Mapping the first key of a sequence remaps the whole sequence: map z g makes zt behave as gt.
Three things overrides never touch: typing (the filter, search and rename boxes), bookmark
letters (' and m take the letter literally, so every letter stays usable as a shortcut), and
Ctrl combinations.
Render on/off, render delay, scroll step, wheel behaviour, cursor wrap-around, half-page size, and the interface language. The full-text index has its own section: on/off, a file-size cap, excluded folders, how large the cache currently is, and a button to clear it.
command openers, which spawn an external
process; those are filtered out of the menu there.child_process.spawn for
command openers, and Electron's shell for “default app” and “show in the file manager”. Obsidian
exposes no public API for either..obsidian. It is built the first time
you use gt, and the settings tab shows its size with a button to clear it.npm install
cp .env.local.example .env.local # point it at your vault's plugin folder
npm run dev # watch, and copy the build into that vault
npm test # node test suite — no Obsidian required
npm run build # production build
The source is plain JavaScript on purpose: the code carries a lot of why in its comments, and a type-driven rewrite would churn all of it.
src/ is bundled by esbuild into main.js. The tests stub the Obsidian API so they run in plain Node,
and cover the things worth covering: key dispatch, preview sanitising, opener resolution and command
building, path handling across platforms, decoration rules, and the i18n invariants (no orphan keys,
matching placeholders, every key used in code exists in English).
Translations live in src/i18n/. English is the reference; a language file may be incomplete and
anything missing falls back to English.
The interaction model is lifted wholesale from yazi by sxyazi. This plugin is not affiliated with that project.