npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lp-labs/react-file-explorer

v0.1.0

Published

Accessible React file trees with search, rename and multi-selection.

Readme

@lp-labs/react-file-explorer

React file trees with keyboard navigation, search, validated inline rename and multi-selection. React 18+ peer only; no runtime dependencies or CSS imports. Includes TypeScript, ESM, CommonJS and browser entries. This is a UI over application data, not a filesystem API.

Installation

npm install @lp-labs/react-file-explorer

Requires React 18 or later in the host application and a modern browser for interactive navigation. Server-side rendering is supported. Node.js consumers require Node.js 18 or later.

Quick Start

import { useState } from 'react';
import FileExplorer from '@lp-labs/react-file-explorer';

export default function Example() {
  const [entries, setEntries] = useState([
    { id: 'docs', name: 'Documents', type: 'folder', children: [
      { id: 'notes', name: 'Notes.txt', type: 'file' },
    ] },
  ]);
  return (
    <FileExplorer
      entries={entries}
      onEntriesChange={setEntries}
      defaultExpandedIds={['docs']}
      onOpen={(file) => console.log(file)}
    />
  );
}

Replace onOpen with your application's file-opening or preview action. No filesystem operation happens automatically.

Entry Data

Entries require unique string/finite numeric id and nonempty name. Optional type is file or folder; children imply a folder when type is omitted. Folders accept array children; an empty type:'folder' remains expandable. Custom data is preserved. disabled prevents interaction and readOnly prevents renaming. Array order is preserved. Inputs are validated iteratively for duplicates and cycles.

FileExplorer API

| Prop | Default | Description | | --- | --- | --- | | entries / defaultEntries | [] | Controlled or initial entry tree | | onEntriesChange(entries, change) | none | Renamed tree; change contains type, id, and name | | expandedIds / defaultExpandedIds | [] | Expanded folder IDs | | onExpandedChange(ids, entry) | none | Requested expansion state | | selectedIds / defaultSelectedIds | [] | Selected entry IDs | | onSelectionChange(ids) | none | Requested selection state | | query / defaultQuery | '' | Search text | | onQueryChange(query) | none | Requested search text | | selectionMode | 'multiple' | 'single' or 'multiple' | | showCheckboxes | true | Show entry selection controls | | readOnly | false | Disable renaming, not navigation or selection | | caseSensitiveNames | false | Case-sensitive sibling-name collision checks | | onRename(entry, name, { signal }) | none | Optional async action before local rename | | onOpen(entry) | none | Open a file through application code | | height | 360 | Tree viewport height in pixels | | indent | 20 | Indentation per depth level in pixels | | label | 'Files' | Accessible tree label | | emptyContent | 'No files' | Empty-result content |

renderEntry(entry, { selected, expanded, depth, folder }), className, and style are also supported. Numeric height must be positive; indent must be nonnegative. Replace input arrays immutably when data changes. default* props initialize internal state only.

Selection And Keyboard Navigation

Use controlled entries/onEntriesChange or defaultEntries. Selection uses selectedIds/defaultSelectedIds and onSelectionChange; expansion uses expandedIds/defaultExpandedIds and onExpandedChange. Search uses query/defaultQuery and onQueryChange. Apply callbacks for all controlled values. The selectionMode default is multiple; single is also supported. showCheckboxes defaults true. Plain clicks replace selection; Ctrl/Cmd-click and checkboxes toggle; Shift-click and Shift-arrow navigation extend a visible range. Ctrl/Cmd+A selects enabled visible entries. Selection does not cascade to children.

Arrow Up/Down, Home/End move focus; Right expands or enters a folder; Left collapses or moves to its parent. Enter opens files through onOpen(entry) or toggles folders; Space toggles selection; F2 starts rename. Double-click also opens/toggles. The tree exposes ARIA hierarchy/selection and uses an active-descendant tab stop. Custom interactive renderEntry(entry,{selected,expanded,depth,folder}) content must supply its own accessibility behavior.

Search

Search matches case-insensitive name substrings and retains/opens ancestor paths without modifying data. Matching paths stay forced open until search is cleared. Hidden entries are not removed from source data. The tree is not virtualized: use a virtualized-tree library for very large visible node counts.

Rename

Rename uses a toolbar command or F2. Enter/Save submits; Escape/Cancel discards. Names are trimmed, cannot be empty, ./.., contain slashes or control characters, or duplicate sibling names. Sibling comparison is case-insensitive unless caseSensitiveNames is true. These are UI-level checks, not a complete OS filename policy. Optional onRename(entry,name,{signal}) runs before applying the immutable update; reject to keep the editor open with an error. Cancellation/unmount aborts the signal and ignores late completion, but server operations must honor the signal to be canceled remotely. Validation runs again against current data after async completion. Read-only mode disables renaming, not navigation or selection.

Helpers And Limits

Exports include pure getVisibleEntries, renameEntry, validateName, and isFolder. Rename copies only changed ancestors and preserves unrelated data. No network loader, create/delete/move, drag/drop, OS file access, previews, context menus or download operations are included. SSR emits initial rows without accessing browser globals.

Support

Support maintenance with a PayPal donation.

Author

Created by Libin Prasanth.