node-fzf: Fuzzy CLI List Selector
node-fzf is a Node.js utility inspired by the `fzf` command-line fuzzy finder, providing an interactive CLI for list selection. It enables developers to integrate fuzzy searching capabilities into their Node.js applications, offering both a standalone CLI and a programmatic API. The current stable version is 0.14.0, published approximately 10 months ago, suggesting a maintenance or slow release cadence rather than rapid iteration. Key differentiators include its promise-based and callback-based API for integration, support for piping input and output with other CLI tools, and customizable display options like height and pre/post-line hooks. Unlike `fzf-node` which are direct Go bindings, `node-fzf` is a pure JavaScript implementation.
Common errors
-
SyntaxError: Named export 'nfzf' not found. The requested module 'node-fzf' is a CommonJS module, which may not support all module.exports as named exports.
cause Attempting to `import { nfzf } from 'node-fzf'` or `import nfzf from 'node-fzf'` in an ES Module context.fixChange the import statement to `const nfzf = require('node-fzf');` -
Output is garbled or disappears unexpectedly during interaction.
cause Concurrent `stdout` writes from other parts of the application or external processes interfere with `node-fzf`'s terminal rendering.fixEnsure `node-fzf` has exclusive control over the terminal's `stdout` and `stdin` during its operation. Avoid `console.log` calls or other terminal output from your application while `nfzf` is running. Consider piping `node-fzf` output if mixing with other CLI tools. -
TypeError: opts.update is not a function
cause The `update` method is attached to the *list object itself* when using the array-based API (e.g., `nfzf(list, callback)`), or to the `opts.list` array when `opts` is passed to `nfzf`. It's not a global method on `nfzf` or implicitly available on a new array.fixEnsure you are calling `update()` on the *original list reference* that was passed to `nfzf`. For the object-based API, `opts.list.update(newList)` or if `api = nfzf(list, cb)`, then `api.update(newList)`. -
No match found for query: '...' (or selected is null)
cause The user did not select any item from the list, either by pressing `Escape` or by typing a query that yielded no matches.fixAlways check if `result.selected` is `null` or `undefined` after `nfzf` resolves. Provide appropriate user feedback or handle the `no selection` case gracefully in your application logic, as shown in the quickstart.
Warnings
- gotcha node-fzf directly interacts with and manipulates `stdout` and `stdin` to provide its interactive CLI. This can lead to messy or corrupted output if your application is concurrently writing to `stdout` or reading from `stdin` while `node-fzf` is active.
- gotcha The package officially states 'windows - unable to test automatically', indicating potential instability or untested behavior on Windows operating systems. Users might encounter platform-specific issues or unexpected rendering problems.
- gotcha As of version 0.14.0, `node-fzf` is distributed as a CommonJS module. Attempting to use ES Module `import` syntax (`import nfzf from 'node-fzf'`) in an ES Module context without proper transpilation or Node.js loader configuration will result in a runtime error.
- deprecated The callback-based API for `node-fzf` is less idiomatic in modern Node.js asynchronous programming. While still functional, the promise-based API is generally preferred for better error handling and readability.
Install
-
npm install node-fzf -
yarn add node-fzf -
pnpm add node-fzf
Imports
- nfzf
import nfzf from 'node-fzf'
const nfzf = require('node-fzf') - nfzf.getInput
import { getInput } from 'node-fzf'const { getInput } = require('node-fzf'); // or nfzf.getInput
Quickstart
const nfzf = require('node-fzf');
const opts = {
list: ['apple', 'banana', 'orange', 'grape', 'strawberry', 'blueberry', 'raspberry', 'pineapple', 'mango', 'kiwi'],
mode: 'fuzzy',
query: '',
selectOne: false,
height: 50, // Use 50% of the screen height
prelinehook: function (index) { return `[${index + 1}] `; },
postlinehook: function (index) { return ` (${this.list[index].length} chars)`; }
};
(async function () {
console.log('Starting fuzzy search. Use Ctrl-S to switch modes, Up/Down to navigate.');
const result = await nfzf(opts);
const { selected, query } = result;
if (!selected) {
console.log(`No match found for query: '${query}'`);
} else {
console.log(`\nSelected item: '${selected.value}' at index ${selected.index}`);
console.log(`Original list item: '${opts.list[selected.index]}'`);
}
process.exit(0); // Ensure the process exits after selection
})();