browsr: a terminal file browser for local and remote filesystems
🗂️ a pleasant file explorer in your terminal supporting all filesystems
At a glance
- What is it?
- browsr takes a path, and the path decides the filesystem, so the same terminal browser opens a local directory, a GitHub repository, an S3 bucket or an SSH host, with previews for code, images and data files. The URL grammar is the idea, and the previewers are what make it more than ls with arrows.
- Who is it for?
- browsr fits someone who works across several storage systems in a terminal and wants one browsing interface with previews rather than a set of per-provider listings, and who is willing to install the extras the remote backends need. It is the wrong tool for a task that only involves local files, where an ordinary file manager or pager is simpler, and the wrong tool for scripting, since a browser is an interactive view rather than a pipeline stage.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The path decides the filesystem
browsr is described as a pleasant file explorer in your terminal, a command line TUI application that lets you browse the contents of local and remote filesystems with your keyboard or mouse. The interesting part is the last clause: the remote filesystems are not a bolted-on feature, they are reached through the same argument as a local path.
The usage section says it plainly: give browsr a path to a local or remote file or directory. A local directory is a plain path. A GitHub repository is a URL with a scheme. An object store bucket is a URL with a scheme. An SSH host is a URL with a scheme:
browsr ~/Downloads/browsr ssh://[email protected]:22The dependency list explains the mechanism. The application is built on Textual, and two dependencies carry the abstraction: `universal-pathlib` and `textual-universal-directorytree`. The first is a path type that can represent remote locations in the same namespace as local ones, and the second is a directory tree widget that knows how to enumerate those remote locations. The TUI itself is assembled from `textual[syntax]`, with `rich` and `rich-click` for output and the command line, `rich-pixels` for images, and `orjson` for fast parsing.
So the architecture is a normal Textual application sitting on top of a path layer that has been widened to include object stores, repositories and remote hosts. That is why the same previews work everywhere.
github:// and s3:// are just arguments
The four usage forms in the documentation are short enough to read as one block. A GitHub repository:
browsr github://juftin:browsrA private repository, with a token in the environment and a branch named after the owner and repository:
export GITHUB_TOKEN="ghp_1234567890"
browsr github://juftin:browsr-private@mainAn object store bucket:
browsr s3://my-bucketAnd the cloud support is stated in a note: currently AWS S3, Google Cloud Storage, and Azure Blob Storage / Data Lake are supported. SSH and SFTP are covered by the `ssh://` form.
Two design consequences are worth drawing out. First, the credential story is the provider's own, not the application's. The GitHub example exports `GITHUB_TOKEN` and the URL carries the owner, the repository and optionally the branch, so the token stays in the environment and the URL stays readable. Second, because the scheme is explicit in the argument, there is no ambient provider selection and no configuration file listing which buckets you are allowed to see. What you can browse is what the credentials you exported can see, which is also a security property: a typo in a scheme cannot silently point the browser at a different backend.
The documentation site linked from the README is where the list of supported filesystems lives, and the README points there for more information rather than duplicating the table.
Previewers, not just a listing
The README lists what you can do once you are inside a file, and this is where browsr separates itself from a directory listing.
Code files are shown with syntax highlighting, which comes from the `syntax` extra of Textual and the `Pillow` and `pypdfium2` libraries in the dependency list. JSON files are formatted rather than dumped raw, with `orjson` doing the parsing. Images are rendered, and `rich-pixels` is the dependency that draws them in the terminal. Markdown is rendered too, with a screenshot of that in the documentation. Data files are converted into navigable datatables, and `pandas` plus `pyarrow` are the dependencies for that.
The datatable is the feature that changes how you use remote data. Instead of downloading a file to read it, you open it as a table and navigate it, which for a parquet dataset in a bucket is the difference between a one-line preview and an actual look at the data. The README mentions screenshots of a datatable, of a rendered image and of a rendered markdown page, so the intended workflow is a browsing session where you stop at each kind of file and see it.
There is also a `pyperclip` dependency, which suggests that copying a value out of a preview is part of the flow, and that matters more than it sounds in a browser whose entire purpose is looking at things.
The cost of the previewers is that each one drags in a library, which is why the extras exist.
pipx first, and extras for what you actually need
The installation advice is specific: it is recommended to use pipx instead of pip, because pipx installs the package in an isolated environment and makes it available everywhere. If you would rather use pip, the README says to replace pipx with pip in the command:
pipx install browsrThen there are the extras, and this is the part worth reading carefully. If you are looking to use browsr on remote file systems, like GitHub or AWS S3, you need the `remote` extra. If you want to browse parquet or feather files, you need the `data` extra. Or you can install the `all` extra to get all of them:
pipx install "browsr[all]"The mapping is visible in the packaging metadata. The `remote` extra pulls in `textual-universal-directorytree[remote]`, the extra of the directory tree library that adds the remote backends. The `data`, `parquet` and `all` extras all pull in `pyarrow`, and the `all` extra combines the two. So `remote` and `data` are genuinely different capabilities with different costs, and `all` is simply the union.
That is a sensible arrangement for a tool whose optional features are heavyweight, and it also means a plain install will not reach S3 or GitHub. For a user who only ever browses local files, the base install is enough and carries none of the remote machinery.
A Python 3.10+ project with a modern toolchain
The packaging metadata describes a project that keeps its tooling current. The build backend is hatchling, the console script is registered as `browsr` pointing at `browsr.__main__:browsr`, and `requires-python` is `>=3.10`, with classifiers for Python 3.10 through 3.14 and for both CPython and PyPy.
The development setup is where the current-generation tooling shows. The dev dependency group carries pytest with pytest-cov for coverage, pytest-vcr for recording HTTP interactions, `textual-dev` for the Textual developer tools, `pytest-textual-snapshot` for snapshot testing of a TUI, and pytest-asyncio. Linting is handled by `mypy` and `ruff`.
The snapshot testing entry is a detail that says a lot about the project. Snapshot testing of a terminal interface means the rendered output is captured and compared, which is the only practical way to assert on what a TUI actually painted, and the presence of `pytest-vcr` means the tests can replay recorded HTTP calls for the remote backends rather than hitting GitHub or S3 during a test run.
The repository root matches. There is a Taskfile.yaml, which is the Task runner for project commands, a uv.lock for the uv package manager, a .pre-commit-config.yaml for git hooks, a .releaserc.js for semantic-release, and a mkdocs.yaml with a docs directory for the documentation site. The release process is therefore automated through conventional commits, which is why the version numbers in the release history follow a semver scheme.
One URL grammar instead of one tool per provider
There is a real alternative to this approach, and it is the one most people start from: mount the remote storage into the local filesystem and point any local file tool at the mount. The difference in approach is straightforward.
With a mount, the remote system is made to look local, and you get whatever your local tool does, which for a terminal is a listing and for a pager is text. With browsr, the remote system is addressed directly, and the tool itself is the thing that knows how to talk to it. The consequences are practical. There is no mount to configure, no credentials file to write for a filesystem driver, and no daemon left running to serve the mount. The provider's own environment variables are the only credential involved, which is the same credential the provider's own command line tools use.
The other difference is the previewers. Because browsr owns the display, it can decide what a file looks like, and it makes the same decision for a local file and a file in a bucket, which is exactly the point of a browser that treats both as paths.
The trade is that browsr only knows the backends it has been given. AWS S3, Google Cloud Storage and Azure Blob Storage or Data Lake are the cloud backends currently supported, plus GitHub and SSH, so a provider outside that list is not reachable at all, and there is no plugin mechanism described in the documentation for adding one.
Version 1.24.0, beta status, and a branch last pushed in April
The project classifies itself as Development Status 4 - Beta, and the version in the packaging metadata is 1.24.0. The recent releases are v1.24.0 on 2026-04-22, v1.23.0 on 2026-03-31 and v1.22.1 on 2025-08-22, so the release line has been moving steadily through 2026 after a long gap.
The repository is not archived, and the last push was on 2026-04-22, the same day as the newest tag. That is a project being worked on, and the semantic-release setup in the repository root means the version number in the metadata is generated from commit messages rather than typed by hand.
The licence is MIT, with the LICENSE file at the repository root and the README stating that browsr is distributed under the terms of the MIT license.
Two limitations are worth stating plainly for an evaluator. The first is that this is a browser, not an editor: it is a view for inspecting what is there, and nothing in the documentation suggests you can change a file in place, so editing a remote file still means taking it out of the browser. The second is the credential exposure that comes with the design. Exporting `GITHUB_TOKEN` into the shell to browse a private repository is convenient, and it also means a token with more access than the repository needs sits in your environment, which is a decision to make on purpose rather than by default.
Editorial conclusion
browsr fits someone who works across several storage systems in a terminal and wants one browsing interface with previews rather than a set of per-provider listings, and who is willing to install the extras the remote backends need. It is the wrong tool for a task that only involves local files, where an ordinary file manager or pager is simpler, and the wrong tool for scripting, since a browser is an interactive view rather than a pipeline stage. Verify first which backends you actually need, since remote access requires the remote extra, and check that the preview you want has a dependency you installed, such as pyarrow for parquet files.
Frequently asked questions
How do I install browsr?
The README recommends pipx, which installs the package in an isolated environment and makes it available everywhere, with pip as an alternative. The command is pipx install browsr, and pipx install "browsr[all]" installs every optional extra.
Which filesystems can browsr browse?
Local paths, GitHub repositories over github://, SSH and SFTP over ssh://, and object stores, with the documentation noting that AWS S3, Google Cloud Storage and Azure Blob Storage or Data Lake are currently supported. Remote backends require the remote extra.
What extras does browsr have and what do they add?
The remote extra adds the remote backends through the remote extra of the directory tree library, and the data extra adds pyarrow so that parquet and feather files can be browsed. The all extra combines both.
Can browsr preview files or only list them?
It previews. The README describes viewing code files with syntax highlighting, formatting JSON files, rendering images, rendering markdown and converting data files to navigable datatables, with screenshots of those views in the documentation.
What licence is browsr released under?
MIT, with the LICENSE file at the repository root. The packaging metadata also lists the licence as MIT and the project requires Python 3.10 or newer, with classifiers for CPython and PyPy.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/juftin-browsr)Was this useful?