
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
clack-tree-select
Advanced tools
Beautiful, interactive, searchable tree selection prompt for Clack CLI apps
"Make your React & Vue apps multilingual in minutes!"
Beautiful, interactive tree selection prompt plugin for Clack-based command-line applications. Built for use specifically with Clack.

See navigation, hierarchical selection, and smart keyboard shortcuts in action.
npm install clack-tree-select
yarn add clack-tree-select
pnpm add clack-tree-select
Add tree selection to your existing Clack CLI:
import { treeSelect } from 'clack-tree-select';
import { intro, outro, text, isCancel } from '@clack/prompts';
async function main() {
intro('🚀 Project Setup');
// Your existing Clack prompts
const projectName = await text({
message: 'Project name?',
placeholder: 'my-project'
});
if (isCancel(projectName)) return;
// Add tree selection seamlessly
const files = await treeSelect({
message: 'Select files to process:',
tree: [
{
value: 'src',
name: 'src',
children: [
{ value: 'src/components', name: 'components' },
{ value: 'src/pages', name: 'pages' },
{ value: 'src/utils', name: 'utils' }
]
},
{ value: 'package.json', name: 'package.json' }
]
});
if (isCancel(files)) return;
outro(`✅ ${projectName}: Selected ${files.length} files`);
}
main();
Perfect integration - works alongside all your existing Clack prompts! 🎯
While clack-tree-select works great with file systems, it's designed as a generic tree selection tool. You can create hierarchical selection prompts for any data structure! Here are some powerful examples:
const components = await treeSelect({
message: 'Select UI components to include:',
tree: [
{
value: 'forms',
name: 'Form Components',
children: [
{ value: 'forms/input', name: 'Text Input' },
{ value: 'forms/select', name: 'Select Dropdown' },
{ value: 'forms/checkbox', name: 'Checkbox' },
{ value: 'forms/radio', name: 'Radio Button' }
]
},
{
value: 'navigation',
name: 'Navigation',
children: [
{ value: 'nav/header', name: 'Header' },
{ value: 'nav/sidebar', name: 'Sidebar' },
{ value: 'nav/breadcrumb', name: 'Breadcrumb' },
{ value: 'nav/pagination', name: 'Pagination' }
]
},
{
value: 'feedback',
name: 'Feedback',
children: [
{ value: 'feedback/alert', name: 'Alert' },
{ value: 'feedback/toast', name: 'Toast' },
{ value: 'feedback/modal', name: 'Modal' },
{ value: 'feedback/tooltip', name: 'Tooltip' }
]
}
]
});
interface Department {
id: string;
name: string;
manager?: string;
}
const departments = await treeSelect<Department>({
message: 'Select departments for the training program:',
tree: [
{
value: { id: 'eng', name: 'Engineering', manager: 'Alice' },
name: 'Engineering',
children: [
{ value: { id: 'frontend', name: 'Frontend' }, name: 'Frontend Team' },
{ value: { id: 'backend', name: 'Backend' }, name: 'Backend Team' },
{ value: { id: 'devops', name: 'DevOps' }, name: 'DevOps Team' }
]
},
{
value: { id: 'product', name: 'Product', manager: 'Bob' },
name: 'Product',
children: [
{ value: { id: 'design', name: 'Design' }, name: 'Design Team' },
{ value: { id: 'pm', name: 'Product Management' }, name: 'Product Managers' },
{ value: { id: 'research', name: 'User Research' }, name: 'Research Team' }
]
}
]
});
const categories = await treeSelect({
message: 'Select product categories to feature:',
tree: [
{
value: 'electronics',
name: 'Electronics',
children: [
{
value: 'computers',
name: 'Computers',
children: [
{ value: 'laptops', name: 'Laptops' },
{ value: 'desktops', name: 'Desktops' },
{ value: 'tablets', name: 'Tablets' }
]
},
{
value: 'phones',
name: 'Mobile Phones',
children: [
{ value: 'smartphones', name: 'Smartphones' },
{ value: 'accessories', name: 'Phone Accessories' }
]
}
]
},
{
value: 'clothing',
name: 'Clothing',
children: [
{ value: 'mens', name: "Men's Clothing" },
{ value: 'womens', name: "Women's Clothing" },
{ value: 'kids', name: "Kids' Clothing" }
]
}
]
});
interface Permission {
id: string;
scope: string;
level: 'read' | 'write' | 'admin';
}
const permissions = await treeSelect<Permission>({
message: 'Select permissions for this role:',
tree: [
{
value: { id: 'users', scope: 'users', level: 'admin' },
name: 'User Management',
children: [
{
value: { id: 'users-read', scope: 'users', level: 'read' },
name: 'View Users'
},
{
value: { id: 'users-write', scope: 'users', level: 'write' },
name: 'Edit Users'
},
{
value: { id: 'users-admin', scope: 'users', level: 'admin' },
name: 'Manage Users'
}
]
},
{
value: { id: 'content', scope: 'content', level: 'admin' },
name: 'Content Management',
children: [
{
value: { id: 'content-read', scope: 'content', level: 'read' },
name: 'View Content'
},
{
value: { id: 'content-write', scope: 'content', level: 'write' },
name: 'Edit Content'
}
]
}
]
});
name property controls what users see, value is what you get backThe generic TreeItem<T> interface makes it perfect for any hierarchical data structure in your applications!
treeSelect(options)Creates an interactive tree selection prompt.
| Option | Type | Default | Description |
|---|---|---|---|
message | string | required | The prompt message to display |
tree | TreeItem[] | required | The tree structure to display |
multiple | boolean | true | Allow multiple selections |
initialValues | T[] | [] | Pre-selected values |
required | boolean | false | Require at least one selection |
maxItems | number | undefined | Maximum visible items (scrolling) |
searchable | boolean | true | Enable inline search. Type to filter; Esc clears |
searchDirectoriesOnly | boolean | false | When searching, only match folders (items with children), not files |
icons | IconOptions | default icons | Custom icons for tree items |
showHelp | boolean | true | Show keyboard shortcuts in validation |
interface TreeItem<T = any> {
value: T; // Unique identifier
name?: string; // Display name (falls back to value)
open?: boolean; // Initially expanded
children?: TreeItem<T>[] | string[] | T[]; // Child items
}
interface IconOptions {
directory?: string; // Default: '📁'
file?: string; // Default: '📄'
expanded?: string; // Default: '▼'
collapsed?: string; // Default: '▶'
}
searchable optionType-to-search is enabled by default. You can explicitly enable/disable it per prompt:
import { treeSelect } from 'clack-tree-select';
// Explicitly enable (default)
await treeSelect({
message: 'Pick files (type to search, Esc to clear):',
tree: myTree,
searchable: true,
});
// Disable type-to-search
await treeSelect({
message: 'Pick files (search disabled):',
tree: myTree,
searchable: false,
});
Behavior when enabled:
searchDirectoriesOnly optionWhen you want search to only match folders (items with children) and not files, use searchDirectoriesOnly:
import { treeSelect } from 'clack-tree-select';
// Search matches ALL items (default behavior)
await treeSelect({
message: 'Select items:',
tree: myTree,
searchable: true,
searchDirectoriesOnly: false, // default
});
// Search matches ONLY folders/directories
await treeSelect({
message: 'Select folders to ignore:',
tree: myTree,
searchable: true,
searchDirectoriesOnly: true,
});
This is useful when you have a flat list of folders and want users to quickly find specific folders without matching file names. When searchDirectoriesOnly is true:
children property are matched during searchfileSystemTreeSelect(options)Creates a tree selection prompt from a file system directory.
const files = await fileSystemTreeSelect({
message: 'Select files from your project:',
root: './src',
includeFiles: true,
includeHidden: false,
maxDepth: 3,
filter: (path) => !path.includes('node_modules')
});
| Shortcut | Action |
|---|---|
↑ / ↓ | Navigate up/down |
← / → | Collapse/expand directory |
Space | Toggle selection |
type | Start search and filter visible items |
Backspace | Delete last character in search |
Esc | Clear search / exit search |
Shift+E | Toggle expand/collapse all |
Shift+A | Toggle select/deselect all |
Enter | Submit selection |
Ctrl+C | Cancel prompt |
const result = await treeSelect({
message: 'Choose components to generate:',
tree: components,
icons: {
directory: '📂',
file: '⚛️',
expanded: '📂',
collapsed: '📁'
},
multiple: true,
required: true
});
const configFile = await treeSelect({
message: 'Choose a configuration file:',
tree: [
{
value: 'config',
name: 'config',
open: true,
children: [
{ value: 'tsconfig.json', name: 'TypeScript Config' },
{ value: 'package.json', name: 'Package Config' },
{ value: 'vite.config.js', name: 'Vite Config' }
]
}
],
multiple: false,
required: true
});
Note: In single selection mode, only leaf nodes (files without children) can be selected. Parent directories are shown but not selectable, ensuring users select actual items rather than containers.
import { fileSystemTreeSelect } from 'clack-tree-select';
const selectedFiles = await fileSystemTreeSelect({
message: 'Select files to process:',
root: process.cwd(),
includeFiles: true,
includeHidden: false,
maxDepth: 3,
filter: (path) => {
// Exclude common non-source directories
return !path.includes('node_modules') &&
!path.includes('.git') &&
!path.includes('dist');
}
});
const result = await treeSelect({
message: 'Select at least 2 components:',
tree: componentTree,
validate: (selected) => {
if (!selected || selected.length < 2) {
return 'Please select at least 2 components to continue.';
}
}
});
// Ask user to select packages to install
const packages = await treeSelect({
message: 'Select packages to install:',
tree: [
{
value: 'react',
name: 'React',
children: [
{ value: 'react-dom', name: 'React DOM' },
{ value: 'react-router', name: 'React Router' }
]
},
{
value: 'build-tools',
name: 'Build Tools',
children: [
{ value: 'vite', name: 'Vite' },
{ value: 'typescript', name: 'TypeScript' }
]
}
]
});
// Select packages in a monorepo
const workspaces = await fileSystemTreeSelect({
message: 'Select workspaces to build:',
root: './packages',
includeFiles: false, // Only directories
maxDepth: 1,
filter: (path) => {
// Only include directories with package.json
return fs.existsSync(`${path}/package.json`);
}
});
The tree select automatically adapts to your terminal's color scheme and follows Clack's beautiful styling conventions:
This package is designed as a drop-in addition to your existing Clack CLI. No changes needed to your current setup!
// Your existing CLI
import { intro, text, select, multiselect, outro } from '@clack/prompts';
// Just add this import ↓
import { treeSelect } from 'clack-tree-select';
async function myCLI() {
intro('My Existing CLI');
const name = await text({ message: 'Project name?' });
const framework = await select({ /* ... */ });
// New: Add tree selection anywhere in your flow
const files = await treeSelect({
message: 'Select files to process:',
tree: myFileTree
});
const tools = await multiselect({ /* ... */ });
outro('Done!');
}
Zero breaking changes - just add treeSelect where you need hierarchical selection!
Check out the examples/ directory for interactive demos:
cd examples
pnpm install
# Basic tree-select functionality
pnpm tree-select
# Complete integration with other Clack prompts
pnpm integration
# Searchable demo (type to filter, Esc to clear)
pnpm run search
The integration demo shows a real CLI workflow mixing treeSelect with text, select, multiselect, and confirm prompts.
We welcome contributions! Please see our Contributing Guide for details.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
Beautiful, interactive, searchable tree selection prompt for Clack CLI apps
The npm package clack-tree-select receives a total of 18 weekly downloads. As such, clack-tree-select popularity was classified as not popular.
We found that clack-tree-select demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.