autocompleter
A dependency-free, 1KB JavaScript/TypeScript autocomplete widget for building fast, accessible input suggestions.
Repository Health
Technical Analysis
autocompleter is a zero-dependency autocomplete widget for native HTML input and textarea elements, distributed as a single ~1KB gzipped bundle. It renders suggestion dropdowns driven entirely by a user-supplied fetch callback, so it works equally well with static in-memory arrays, debounced AJAX calls, or any other asynchronous data source.
The library ships as ESM, CommonJS, and UMD builds alongside TypeScript type definitions, and exposes full customization hooks for rendering items, grouping results, and repositioning the dropdown — while wiring up ARIA combobox attributes for keyboard and screen-reader accessibility out of the box.
What You Get
- A single default-exported
autocomplete()factory function that wires up an input element and returns{ destroy, fetch }handles for full lifecycle control - ESM (
autocomplete.mjs), CommonJS (autocomplete.cjs), and minified UMD (autocomplete.min.js) builds plus bundled TypeScript declarations (autocomplete.d.ts) - Built-in keyboard navigation (arrow keys, Enter, Escape) and ARIA combobox/listbox attributes applied automatically to the input and suggestion container
- Customization hooks —
render,renderGroup,customize,className— for controlling suggestion markup, group headers, and dropdown positioning - Debounced fetch support (
debounceWaitMs) and aPreventSubmitenum to control ENTER-key form submission behavior
Common Use Cases
- Search-as-you-type inputs - filtering a small in-memory list (e.g. countries, tags) as the user types, using a synchronous
fetchcallback - Remote search suggestions - debouncing keystrokes and calling a backend search API, updating the dropdown asynchronously via the
updatecallback - Grouped suggestion lists - rendering categorized results (e.g. suggestions grouped by type) using the
groupproperty andrenderGrouphook - Framework-agnostic form fields - dropping the widget into a React, Vue, or vanilla JS form without pulling in a UI framework dependency
Under The Hood
Architecture
autocompleter exports a single default factory function, autocomplete<T>(settings), that closes over mutable module-local state (items, selected, fetchCounter, debounceTimer) rather than relying on a class or external state container. The suggestion dropdown is a plain <div> attached/detached from the DOM on demand (attach/detach/clear), positioned via manual getBoundingClientRect math in updatePosition, and repainted from scratch on every update() call using a DocumentFragment for batched DOM writes. User input flows through native keyup/keydown/input/click/blur/focus listeners into a single fetch(trigger) entry point that debounces via setTimeout and guards against out-of-order async responses with an incrementing fetchCounter compared inside the startFetch callback. The returned { destroy, fetch } handle is the only public surface beyond configuration, so the update/render pair is the main thing that would ripple through the rest of the file if changed.
Tech Stack
The library has zero runtime dependencies and is authored as a single strict-mode TypeScript file (autocomplete.ts, ES2015 target/module) compiled by Rollup (@rollup/plugin-typescript, @rollup/plugin-terser) into ESM (autocomplete.mjs), CommonJS (autocomplete.cjs), and a minified UMD bundle (autocomplete.min.js) alongside bundled .d.ts declarations, plus a small companion CSS file processed with clean-css-cli. Linting runs through a modern flat ESLint config (eslint.config.mjs) built on typescript-eslint’s recommended rules. There is no test runner, framework dependency, or database/network layer — the entire surface area is DOM APIs plus the caller-supplied fetch callback.
Code Quality
No test files or test framework are present anywhere in the repository, and there is no CI workflow beyond a stale-issue bot (.github/stale.yml) — linting and building are npm scripts a contributor would have to run manually. Within the source itself, quality is otherwise solid: strict TypeScript (strict: true, noImplicitAny, noImplicitThis) is used throughout, public interfaces carry JSDoc comments, naming is consistent (functions grouped by concern — position, selection, fetch, DOM attach/detach), and several inline comments reference specific historical bug numbers (e.g. “Fixes #104”, “Fixes #26”) showing the code has been hardened against real browser edge cases over time. The lack of any automated test coverage is the clearest gap.
API Design
The public API is a single function call — autocomplete({ input, fetch, onSelect, ... }) — that returns a minimal { destroy, fetch } handle, so integrating the widget into any framework requires no subclassing, no virtual DOM adapter, and no build-time codegen. Sensible defaults (a text-only render, minLength: 2, debounceWaitMs: 0) mean a working autocomplete needs only input, fetch, and onSelect, while power users can override rendering, grouping, positioning (customize), and ENTER-key form-submission behavior (PreventSubmit) without touching the core update loop. Accessibility (ARIA combobox/listbox/option roles and aria-activedescendant tracking) is wired up automatically rather than left to the consumer, a differentiator against many equally small competing widgets that skip it.