Sign In

ink-file-picker

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ink-file-picker

Filesystem navigation and file selection component for Ink

latest
Source
npmnpm
Version
0.3.2
Version published
Maintainers
1
Created
Source

ink-file-picker

npm version CI license npm downloads

ink-file-picker demo

A filesystem navigation and file selection component for Ink -- the React renderer for CLIs.

Features

  • Directory navigation with breadcrumb path display
  • File details (file size)
  • Single and multi-select modes
  • Type filtering (files only, directories only, or all)
  • Glob pattern and predicate function filters
  • Virtual scrolling for large directories with scroll indicators
  • Symlink support with target resolution and correct back-navigation
  • Type-ahead filtering to quickly find entries
  • Customizable theme (colors, icons, layout)
  • Keyboard-driven with intuitive shortcuts

Install

npm install ink-file-picker

Peer dependencies: ink (>= 6.0.0) and react (>= 19.0.0). Requires Node.js >= 20.

Quick Start

import React from 'react';
import { render } from 'ink';
import { FilePicker } from 'ink-file-picker';

function App() {
  return (
    <FilePicker
      initialPath={process.cwd()}
      showDetails
      onSelect={(paths) => {
        console.log('Selected:', paths);
        process.exit(0);
      }}
      onCancel={() => {
        console.log('Cancelled');
        process.exit(1);
      }}
    />
  );
}

render(<App />);

Props

PropTypeDefaultDescription
initialPathstringprocess.cwd()Starting directory path
rootPathstringundefinedSandbox navigation to this directory and its descendants. The user cannot navigate above it (parent navigation is a no-op at the boundary, escaping symlinks are not followed, an out-of-root initialPath is clamped to the root). Read once at mount.
filterstring | (entry: FileEntry) => booleanundefinedGlob pattern or predicate function to filter visible entries. Glob is matched against entry names. Directories are always shown for navigation unless fileTypes is 'directories'.
showHiddenbooleanfalseShow hidden files (dotfiles)
showDetailsbooleanfalseShow file size column
multiSelectbooleanfalseEnable multi-select mode (Space to toggle, Enter to confirm)
fileTypes'files' | 'directories' | 'all''all'Which entry types to show and allow selection of
maxHeightnumber10Maximum number of entries visible at once (virtual scrolling window)
themePartial<FilePickerTheme>undefinedCustom theme overrides merged with defaults (see Theme Customization)
onSelect(paths: string[]) => voidundefinedCalled when selection is confirmed. Single-select returns a 1-element array. Multi-select returns all selected paths.
onCancel() => voidundefinedCalled when the user presses Escape (outside of filter mode)
onDirectoryChange(path: string) => voidundefinedCalled whenever the current directory changes
isDisabledbooleanfalseWhen true, all user input is ignored

Keyboard Shortcuts

KeyAction
Up / DownMove focus between entries
HomeJump to first entry
EndJump to last entry
EnterOpen focused directory, or select focused file
Right ArrowOpen focused directory
Left ArrowNavigate to parent directory
BackspaceNavigate to parent (or delete filter character when filtering)
SpaceToggle selection in multi-select mode
EscapeCancel filtering, or trigger onCancel
/Activate filter mode
Any printable characterAuto-enter filter mode and start typing
r (in error mode)Retry reading the current directory

Scroll Indicators

When virtual scrolling is active and there are entries above or below the visible window, the component displays indicators like "3 more above" and "12 more below" so users know there is additional content to scroll through.

Filtering

When you start typing (or press /), the component enters filter mode. All entries are filtered in real-time by a case-insensitive substring match against entry names. Press Backspace to remove characters, or Escape to clear the filter and return to browsing mode.

You can also provide a filter prop for persistent filtering:

Glob pattern -- uses picomatch syntax:

<FilePicker filter="*.{ts,tsx}" />

Predicate function -- full control over which entries appear:

<FilePicker filter={(entry) => entry.size > 1024} />

In both cases, directories (and symlinks to directories) are always shown so you can still navigate into them.

Sandboxing Navigation

Pass rootPath to confine the user to a directory subtree. Once at the root, Backspace, Left Arrow, and parent navigation become no-ops, so the user can never browse above it:

// User can browse anywhere under /home/user/project but not escape it
<FilePicker initialPath="/home/user/project/src" rootPath="/home/user/project" />

The sandbox also blocks the other escape routes: a symlink whose target resolves outside the root is not followed, and an initialPath outside the root is clamped back to the root. rootPath is read once at mount.

Multi-Select Mode

Enable multiSelect to let users select multiple files before confirming:

<FilePicker
  multiSelect
  onSelect={(paths) => {
    console.log('Selected files:', paths);
  }}
/>
  • Press Space to toggle the focused entry in/out of the selection
  • Press Enter to confirm and submit all selected paths
  • If no entries are toggled, pressing Enter on a file submits just that file
  • Selected entries are highlighted in the list

If a filter, fileTypes, or showHidden change removes an already-selected entry from the list, that selection is dropped (it will not be returned by onSelect). Type-ahead filtering does not drop selections, since it only narrows the current view.

File Types

The fileTypes prop controls which entries can be selected:

  • 'all' (default) -- all entries are visible and selectable
  • 'files' -- only files can be selected; directories are still shown for navigation
  • 'directories' -- only directories are visible and selectable
// Only allow selecting directories
<FilePicker fileTypes="directories" onSelect={(dirs) => console.log(dirs)} />

Symlinks are handled correctly: a symlink pointing to a directory is treated as a directory, and a symlink pointing to a file is treated as a file.

Show Details

When showDetails is enabled, each file entry displays its size (human-readable) alongside the name:

<FilePicker showDetails />

Theme Customization

Pass a theme prop to override any part of the default theme. The theme object has two keys: styles (functions returning Ink BoxProps/TextProps) and config (icon strings and separators).

import type { FilePickerTheme } from 'ink-file-picker';

const customTheme: Partial<FilePickerTheme> = {
  styles: {
    headerPath: () => ({ bold: true, color: 'green' }),
    entryName: ({ isFocused, kind }) => ({
      color: isFocused ? 'yellow' : kind === 'directory' ? 'green' : undefined,
      bold: isFocused,
    }),
  },
  config: {
    directoryIcon: '+',
    fileIcon: '-',
  },
};

<FilePicker theme={customTheme} />

You only need to provide the keys you want to override; everything else falls back to the default theme. See the FilePickerTheme and FilePickerThemeStyles types for the full set of customizable style functions and config values.

Theme Config

KeyTypeDescription
directoryIconstringIcon shown before directory names
fileIconstringIcon shown before file names
symlinkIconstringIcon shown before symlink names
separatorCharstringCharacter used for visual separators
directoryTrailstringTrailing indicator for directories (e.g. /)
symlinkIndicatorstringIndicator appended to symlink names

Callbacks

onSelect(paths: string[])

Fired when the user confirms their selection. In single-select mode the array has one element. In multi-select mode it contains all toggled paths (or the focused file if none were toggled).

onCancel()

Fired when the user presses Escape while in browsing mode (not filtering). Use this to exit your CLI or return to a parent view.

onDirectoryChange(path: string)

Fired whenever the user navigates to a new directory. Only fires when the path actually changes, not on internal mode transitions. Useful for syncing external state or displaying the current location elsewhere in your UI.

TypeScript

The package exports all relevant types for consumers:

import type {
  FileEntry,
  FilePickerProps,
  EntryKind,
  FileTypeFilter,
  EntryFilter,
  OnSelectCallback,
  OnCancelCallback,
  OnDirectoryChangeCallback,
  FilePickerTheme,
  FilePickerThemeStyles,
  FilePickerMode,
  FilePickerStateAPI,
} from 'ink-file-picker';

Key Types

FileEntry -- represents a single filesystem entry:

type FileEntry = {
  name: string;           // basename, e.g. "package.json"
  path: string;           // absolute path
  kind: EntryKind;        // 'file' | 'directory' | 'symlink'
  size: number;           // bytes (0 for directories)
  modifiedAt: number;     // ms since epoch
  isHidden: boolean;      // true for dotfiles
  symlinkTarget?: string; // resolved target path (symlinks only)
  symlinkTargetKind?: EntryKind; // kind of the target (symlinks only)
};

FilePickerStateAPI -- returned by the useFilePickerState hook for building custom file picker UIs.

Advanced Hooks

For custom file picker UIs, the package exposes the underlying hooks:

import {
  useFilePickerState,
  useFilePicker,
  useDirectoryReader,
} from 'ink-file-picker';
  • useFilePickerState(props) -- manages all state and actions (reducer, focus, filtering, navigation)
  • useFilePicker({ state, onSelect, onCancel }) -- wires keyboard input to the state API
  • useDirectoryReader({ mode, currentPath, dispatch }) -- reads directory contents when mode is 'loading' and dispatches results

Contributing

Contributions are welcome. Please open an issue to discuss larger changes before submitting a PR.

git clone https://github.com/costajohnt/ink-file-picker.git
cd ink-file-picker
npm install
npm run build
npm test

Changelog

See GitHub Releases.

License

MIT

Keywords

ink

FAQs

Package last updated on 25 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts