@jeryfan/finder-ui
v0.5.0
Published
A macOS Finder-style file browser component for React
Maintainers
Readme
finder-ui
A macOS Finder-style file browser component for React.
Install
npm install finder-uiQuick Start
import { Finder } from 'finder-ui'
function App() {
return (
<Finder
style={{ height: '100vh' }}
tabs={[
{ key: 'files', label: 'Files', rootPath: '/' },
]}
onFetchFiles={async (path) => {
const res = await fetch(`/api/files?path=${path}`)
return (await res.json()).files
}}
/>
)
}Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| tabs | SidebarTab[] | Yes | Tab configuration for sidebar navigation |
| onFetchFiles | (path: string) => Promise<FileEntry[]> | Yes | Fetch files for a directory path |
| defaultTab | string | No | Key of the initially active tab |
| onOpenFile | (file: FileEntry) => Promise<string \| void> | No | Handle file open; return content string to show in preview |
| onDownload | (file: FileEntry) => void | No | Handle single file download |
| onBatchDownload | (files: FileEntry[]) => void | No | Handle batch file download |
| onUpload | (files: File[], targetPath?: string) => Promise<void> | No | Handle file upload |
| onSave | (path: string, content: string) => Promise<void> | No | Handle save of edited file content |
| onRename | (file: FileEntry, newName: string) => Promise<void> | No | Handle file/folder rename |
| onDelete | (files: FileEntry[]) => Promise<void> | No | Handle file/folder deletion |
| onCreateFolder | (parentPath: string, name: string) => Promise<void> | No | Handle new folder creation |
| editable | boolean | No | Enable file editing in preview panel |
| renderMarkdown | (content: string) => ReactNode | No | Custom markdown renderer |
| locale | Partial<FinderLocale> | No | Localization strings (defaults to English) |
| className | string | No | Additional CSS class for root element |
| style | CSSProperties | No | Inline styles for root element (e.g. dimensions) |
| theme | 'target' \| 'graphite' \| 'clean' | No | Theme variant (default: 'target') |
Keyboard Shortcuts
| Key | Action |
|-----|--------|
| ↑ / ↓ | Navigate file list |
| Enter | Open file or enter folder |
| Backspace | Go to parent directory |
| ⌘/Ctrl + A | Select all files |
| Delete | Delete selected files |
| Escape | Clear selection |
Sizing
The component fills its parent by default (width: 100%; height: 100%). Use the style or className prop to set explicit dimensions:
// Full viewport
<Finder style={{ height: '100vh' }} ... />
// Fixed height
<Finder style={{ height: 600 }} ... />
// Fill parent (parent must have explicit height)
<Finder ... />Multiple Instances
Each <Finder> creates an isolated store — multiple instances on the same page work independently with no shared state.
Examples
Run the development server and navigate to http://localhost:5173/#examples to see all examples:
npm run dev
# open http://localhost:5173/#examples| Example | Description | |---------|-------------| | Basic | Minimal setup — one tab, file listing only (~10 lines) | | With Preview | File preview for Markdown, code, CSV, images, and audio | | File Operations | Full CRUD — rename, delete, create folder, upload, and save | | Internationalization | Dynamic locale switching (English, Chinese, Japanese) | | Custom Theme | Theme switching (target/graphite/clean) and CSS variable overrides | | Multiple Instances | Two independent Finder instances side by side | | Kitchen Sink | All features combined — multi-tab, preview, editing, i18n, themes, CRUD |
All examples use frontend mock data with no backend required.
Peer Dependencies
{
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"zustand": "^5.0.0",
"marked": "^17.0.0",
"@codemirror/lang-json": "^6.0.0",
"@codemirror/theme-one-dark": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@uiw/react-codemirror": "^4.20.0"
}