Core API

cmdk-engine is plain TypeScript with zero runtime dependencies. The React provider is built from the pieces on this page. Use them directly to drive another UI, in tests, or on the server. For the provider, hooks and the command object, see the API Reference. The type declarations import React's types for the icon field. Without them in your project, and with skipLibCheck off, icon is typed any instead of failing to compile.

ts
import { createRegistry, createFuzzySearch } from 'cmdk-engine'

const registry = createRegistry()
const unregister = registry.register({ id: 'home', label: 'Home', href: '/' })

const results = createFuzzySearch().search('ho', registry.getAll())
// [{ item: { id: 'home', ... }, score: 0.95 }]

unregister() // remove the command again

createRegistry()

The command store. It is a small publish and subscribe store that works with React's useSyncExternalStore. Subscribers are notified once per microtask, however many changes happened. When several registrations share an id, the newest one is visible. Removing it brings back the one it replaced, so two components can register the same id and either can unmount first.

MethodDescription
register(command)Add a command. Returns a function that removes only this registration. If the id is already registered, the newest registration is the visible one, and removing it brings the older one back. To change a registered command, use update().
registerMany(commands)Add several, with the same rules as register(). Returns a function that removes only this call's registrations. Calling it again does nothing.
update(id, partial)Merge fields into the visible command for an id. The id cannot change. Does nothing for an unknown id. The update belongs to that registration: if it is removed, the command it replaced comes back without the update.
unregister(id)Remove a command, including every registration of that id.
getAll()The registered commands.
getById(id)One command, or undefined.
getByGroup(groupId)The commands whose group is groupId.
subscribe(listener)Call listener after changes. Returns a function that unsubscribes.
getSnapshot()A stable array that changes only when the commands change.

createFuzzySearch()

The built-in search engine: an object with search(query, items) that returns ScoredItem[]. How it scores is on the Search page, together with createMatchSorterSearch and how to write your own engine.

createKeywordEngine(synonyms?, userAliases?)

Synonym expansion and aliases. synonyms is a SynonymMap; userAliases is an optional Map<commandId, string[]>.

ts
import { createKeywordEngine } from 'cmdk-engine'

const keywords = createKeywordEngine({ billing: ['money', 'payment'] })
keywords.expandQuery('money') // ['money', 'billing']
keywords.expandQuery('billing') // ['billing', 'money', 'payment']
MethodDescription
expandQuery(query)The query plus its synonyms. The lookup ignores case, accents and extra spaces. An empty query gives an empty array.
enrichItem(item), enrichAll(items)Copies of the commands with synonym-derived terms added, which the search engine weights lower than your own keywords.
addAlias(commandId, alias), removeAlias(commandId, alias)Add or remove a keyword for one command.
getAliases()A copy of the alias map.
setSynonyms(map)Replace the synonym dictionary. It only reads its argument, so a frozen dictionary works, and the dictionary you passed to createKeywordEngine is never changed.

Inside React, the provider owns a keyword engine built from config.synonyms; it is reachable as useEngineContext().keywords. An alias added there shows up at the next results update. Adding one does not itself re-render anything.

Access control

FunctionDescription
createAccessFilter(provider, mode?)Returns (items) => items that removes commands the user may not see. mode is 'any' (the default) or 'all'; a command's own accessMode overrides it. Commands without permissions always pass.
createSimpleAccessProvider(permissions)An AccessControlProvider from an array or a Set of permission strings. For dynamic permissions, write your own object with hasPermission, hasAnyPermission and hasAllPermissions.
isCommandVisible(item)Resolves a command's when gate: true when there is none, or it resolves truthy.
filterVisible(items)Removes commands whose when is false.

This is a UI filter, not a security boundary. Enforce permissions on the server.

Frecency

createFrecencyEngine(options?)

Ranks by frequency and recency. A command's score is count * 2^(-daysSinceLastUse / halfLife), so ten uses yesterday beat a hundred a month ago.

OptionDefaultDescription
storagein memoryA FrecencyStorage. The React provider defaults to localStorage instead.
halfLife7 daysHow fast old usage decays.
maxAge30 daysDays after its last use before an entry leaves getRecent(). recordUsage() removes older entries from storage.

FrecencyOptions also has a storageKey, but only the provider reads it. In the core, pass createLocalStorageFrecencyStorage(key) as storage.

MethodDescription
recordUsage(id)Count one use now. Also removes entries older than maxAge.
getScore(id)The decayed score, or 0 if the command was never used.
getRecent(count = 5)The most recently used ids, newest first. Entries older than maxAge are skipped.
rank(items, weight = 0.3)Blends each result's search score with its frecency: score * (1 - weight) + normalized * weight. normalized is the result's frecency divided by the strongest in the list, but never by less than 0.5 (one use, one half-life ago), so a command used only long ago does not get the full boost when it is the only used result. weight runs from 0 to 1. Returns the list sorted by the blended score.
cleanup()Remove entries last used more than maxAge days ago. recordUsage() calls it for you.
clear()Remove all usage.

Storage backends

FunctionDescription
createInMemoryStorage()Keeps usage in memory. Good for tests and SSR.
createLocalStorageFrecencyStorage(storageKey = "cmdk-frecency")Keeps usage in window.localStorage under one full key (not a prefix). Where that is unavailable (missing, null or throwing) it does nothing, and data it cannot read is ignored.

A custom backend implements FrecencyStorage: get(key), set(key, entry), getAll(), clear() and, optionally, delete(key).

createGroupManager(groups?)

Holds the group definitions and groups scored results under them. The rules are on Nested Commands and Groups.

MethodDescription
addGroup(group), removeGroup(id)Add or replace a group, or remove one.
getGroup(id), getAllGroups()Look up a group, or list all defined groups by priority.
groupResults(items, query?)Group scored items. Returns GroupedResult[]: ordered by priority for an empty query, by best match while searching, with ungrouped items last.
extractGroups(commands)The distinct groups that at least one command uses.

createContextEngine(boostWeight = 0.2)

boost(items, context) adds boostWeight (capped at a score of 1) to each scored command whose scope matches the CommandContext. Commands without a scope are unchanged. The matching rules are under context in the API Reference.

Search history

createSearchHistory(config?) keeps past queries in localStorage; createInMemorySearchHistory(config?) keeps them in memory. Both take a SearchHistoryConfig and return the same methods. The localStorage one reads and writes window.localStorage only, and treats a null value as unavailable.

MethodDescription
record(query, resultCount)Save a query. Repeats move to the front, queries shorter than minQueryLength are skipped, and only maxEntries are kept.
getRecent(count?)Past queries, newest first.
remove(query)Forget one query.
clear()Forget all of them.

Translations

createDefaultTranslation() returns the English t function: the string for a known key, or the key itself. getTranslationKeys() lists every key; the table is in the API Reference.

Route helpers

The scanners use these to turn a path into a command. They are exported so your own scanner can match.

FunctionExample
pathToId(path)/billing/overview gives billing--overview, and / gives home. Letters, marks and digits of any script are kept (/設定 gives 設定) and other characters except - are dropped, so /a_b, /a.b and /ab share the id ab. A path with only dropped characters keeps them: only / and an empty path become home.
pathToLabel(path)/billing/overview gives "Overview", / gives "Home", and /configuración gives "Configuración"
pathToGroup(path)/billing/overview gives "Billing"; a one-segment path gives undefined
pathSegmentToLabel(segment)user-settings and userSettings give "User Settings", APIKeys gives "API Keys", and über gives "Über"

defineConfig(config)

Returns its argument, typed as CmdkEngineConfig, so your cmdk-engine.config.ts is type-checked. That is the CLI config, not the provider's CommandEngineConfig. See CLI.