{"id":16792424,"url":"https://github.com/huntabyte/cmdk-sv","last_synced_at":"2025-05-15T16:02:43.216Z","repository":{"id":203441033,"uuid":"708538535","full_name":"huntabyte/cmdk-sv","owner":"huntabyte","description":"cmdk, but for Svelte ✨","archived":false,"fork":false,"pushed_at":"2025-03-14T15:56:48.000Z","size":1697,"stargazers_count":534,"open_issues_count":17,"forks_count":21,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-31T21:43:58.994Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://cmdk-sv.com","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/huntabyte.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["huntabyte"],"patreon":null,"open_collective":null,"ko_fi":"huntabyte","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2023-10-22T21:02:28.000Z","updated_at":"2025-03-25T16:04:22.000Z","dependencies_parsed_at":"2023-10-27T05:34:52.929Z","dependency_job_id":"70363ea8-6830-4aff-8828-261edd504eb6","html_url":"https://github.com/huntabyte/cmdk-sv","commit_stats":null,"previous_names":["huntabyte/cmdk-sv"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huntabyte%2Fcmdk-sv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huntabyte%2Fcmdk-sv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huntabyte%2Fcmdk-sv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huntabyte%2Fcmdk-sv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/huntabyte","download_url":"https://codeload.github.com/huntabyte/cmdk-sv/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247737770,"owners_count":20987718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T08:45:50.427Z","updated_at":"2025-04-07T22:04:18.328Z","avatar_url":"https://github.com/huntabyte.png","language":"Svelte","funding_links":["https://github.com/sponsors/huntabyte","https://ko-fi.com/huntabyte"],"categories":["Svelte"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"./static/og.png\" /\u003e\n\u003c/p\u003e\n\n# ⌘K-sv [![cmdk package version](https://img.shields.io/npm/v/cmdk-sv.svg?colorB=green)](https://www.npmjs.com/package/cmdk-sv)\n\n\u003c!-- automd:badges license name=\"cmdk-sv\" color=\"green\" github=\"huntabyte/cmdk-sv\" --\u003e\n\n[![npm version](https://flat.badgen.net/npm/v/cmdk-sv?color=green)](https://npmjs.com/package/cmdk-sv)\n[![npm downloads](https://flat.badgen.net/npm/dm/cmdk-sv?color=green)](https://npmjs.com/package/cmdk-sv)\n[![license](https://flat.badgen.net/github/license/huntabyte/cmdk-sv?color=green)](https://github.com/huntabyte/cmdk-sv/blob/main/LICENSE)\n\n\u003c!-- /automd --\u003e\n\nA port of [cmdk](https://cmdk.paco.me), to Svelte.\n\n⌘K-sv is a command menu Svelte component that can also be used as an accessible combobox. You render items, it filters and sorts them automatically.\n\nDemo and examples: [cmdk-sv.com](https://cmdk-sv.com)\n\n## Install\n\n```bash\nnpm install cmdk-sv\n```\n\n## Use\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\u003c/script\u003e\n\n\u003cCommand.Root label=\"Command Menu\"\u003e\n\t\u003cCommand.Input /\u003e\n\t\u003cCommand.List\u003e\n\t\t\u003cCommand.Empty\u003eNo results found.\u003c/Command.Empty\u003e\n\n\t\t\u003cCommand.Group heading=\"Letters\"\u003e\n\t\t\t\u003cCommand.Item\u003ea\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Item\u003eb\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Separator /\u003e\n\t\t\t\u003cCommand.Item\u003ec\u003c/Command.Item\u003e\n\t\t\u003c/Command.Group\u003e\n\n\t\t\u003cCommand.Item\u003eApple\u003c/Command.Item\u003e\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\nOr in a dialog:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\u003c/script\u003e\n\n\u003cCommand.Dialog label=\"Command Menu\"\u003e\n\t\u003cCommand.Input /\u003e\n\t\u003cCommand.List\u003e\n\t\t\u003cCommand.Empty\u003eNo results found.\u003c/Command.Empty\u003e\n\n\t\t\u003cCommand.Group heading=\"Letters\"\u003e\n\t\t\t\u003cCommand.Item\u003ea\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Item\u003eb\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Separator /\u003e\n\t\t\t\u003cCommand.Item\u003ec\u003c/Command.Item\u003e\n\t\t\u003c/Command.Group\u003e\n\n\t\t\u003cCommand.Item\u003eApple\u003c/Command.Item\u003e\n\t\u003c/Command.List\u003e\n\u003c/Command.Dialog\u003e\n```\n\n## Styling\n\nEach part has a specific data-attribute (starting with `data-cmdk-`) that can be used for styling.\n\n### Command `[cmdk-root]`\n\nRender this to show the command menu inline, or use [Dialog](#dialog-cmdk-dialog-cmdk-overlay) to render in a elevated context. Can be controlled by binding to the `value` prop.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\n\tlet value = 'apple';\n\u003c/script\u003e\n\n\u003cCommand.Root bind:value\u003e\n\t\u003cCommand.Input /\u003e\n\t\u003cCommand.List\u003e\n\t\t\u003cCommand.Item\u003eOrange\u003c/Command.Item\u003e\n\t\t\u003cCommand.Item\u003eApple\u003c/Command.Item\u003e\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\nBy default, this uses a scoring algorithm to filter and rank items based on the user's search input.\nThe algorithm takes into account various factors like continuous matches, word and character jumps among other things.\n\nYou can provide a custom `filter` function that is called to rank each item. Both strings are normalized as lowercase and trimmed.\n\nThe following example implements a strict substring match:\n\n```svelte\n\u003cCommand.Root\n\tfilter={(value, search) =\u003e {\n\t\tif (value.includes(search)) return 1;\n\t\treturn 0;\n\t}}\n/\u003e\n```\n\nIn this strict substring match example, the filter function returns a score of 1 if the item's value contains the search string as a substring, and 0 otherwise, removing it from the result list.\n\nOr disable filtering and sorting entirely:\n\n```svelte\n\u003cCommand.Root shouldFilter={false}\u003e\n\t\u003cCommand.List\u003e\n\t\t{#each filteredItems as item}\n\t\t\t\u003cCommand.Item value={item}\u003e\n\t\t\t\t{item}\n\t\t\t\u003c/Command.Item\u003e\n\t\t{/each}\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\nYou can make the arrow keys wrap around the list (when you reach the end, it goes back to the first item) by setting the `loop` prop:\n\n```svelte\n\u003cCommand.Root loop /\u003e\n```\n\nThis component also exposes two additional slot props for `state` (the current reactive value of the command state) and `stateStore` (the underlying writable state store). These can be used to implement more advanced use cases, such as debouncing the search updates with the `stateStore.updateState` method:\n\n```svelte\n\u003cCommand.Root {state} let:stateStore\u003e\n\t{@const handleUpdateState = debounce(stateStore.updateState, 200)}\n\t\u003cCustomCommandInput {handleUpdateState} /\u003e\n\u003c/Command.Root\u003e\n```\n\n### Dialog `[cmdk-dialog]` `[cmdk-overlay]`\n\nProps are forwarded to [Command](#command-cmdk-root). Composes Bits UI's Dialog component. The overlay is always rendered. See the [Bits Documentation](https://bits-ui.com/docs/) for more information. Can be controlled by binding to the `open` prop.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\tlet open = false;\n\tlet value: string;\n\u003c/script\u003e\n\n\u003cCommand.Dialog bind:value bind:open\u003e\n\t\u003c!-- ... --\u003e\n\u003c/Command.Dialog\u003e\n```\n\nYou can provide a `portal` prop that accepts an HTML element that is forwarded to Bits UI's Dialog Portal component to specify which element the Dialog should portal into (defaults to `body`). To disable portalling, pass `null` as the `portal` prop.\n\n```svelte\n\u003cCommand.Dialog portal={null} /\u003e\n```\n\n### Input `[cmdk-input]`\n\nAll props are forwarded to the underlying `input` element. Can be controlled as a normal input by binding to its `value` prop.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\n\tlet search = '';\n\u003c/script\u003e\n\n\u003cCommand.Input bind:value={search} /\u003e\n```\n\n### List `[cmdk-list]`\n\nContains items and groups. Animate height using the `--cmdk-list-height` CSS variable.\n\n```css\n[data-cmdk-list] {\n\tmin-height: 300px;\n\theight: var(--cmdk-list-height);\n\tmax-height: 500px;\n\ttransition: height 100ms ease;\n}\n```\n\nTo scroll item into view earlier near the edges of the viewport, use scroll-padding:\n\n```css\n[data-cmdk-list] {\n\tscroll-padding-block-start: 8px;\n\tscroll-padding-block-end: 8px;\n}\n```\n\n### Item `[cmdk-item]` `[data-disabled?]` `[data-selected?]`\n\nItem that becomes active on pointer enter. You should provide a unique `value` for each item, but it will be automatically inferred from the `.textContent` if you don't. Text content is normalized as lowercase and trimmed.\n\n```svelte\n\u003cCommand.Item\n\tonSelect={(value) =\u003e {\n\t\tconsole.log('Selected', value);\n\t\t// Value is implicity \"apple\" because of the provided text content\n\t}}\n\u003e\n\tApple\n\u003c/Command.Item\u003e\n```\n\nYou can force an item to always render, regardless of filtering, by passing the `alwaysRender` prop.\n\n### Group `[cmdk-group]` `[hidden?]`\n\nGroups items together with the given `heading` (`[cmdk-group-heading]`).\n\n```svelte\n\u003cCommand.Group heading=\"Fruit\"\u003e\n\t\u003cCommand.Item\u003eApple\u003c/Command.Item\u003e\n\u003c/Command.Group\u003e\n```\n\nGroups will not be removed from the DOM, rather the `hidden` attribute is applied to hide it from view. This may be relevant in your styling.\n\nYou can force a group to always be visible, regardless of filtering, by passing the `alwaysRender` prop.\n\n### Separator `[cmdk-separator]`\n\nVisible when the search query is empty or `alwaysRender` is true, hidden otherwise.\n\n### Empty `[cmdk-empty]`\n\nAutomatically renders when there are no results for the search query.\n\n### Loading `[cmdk-loading]`\n\nYou should conditionally render this with `progress` while loading asynchronous items.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\n\tlet loading = false;\n\u003c/script\u003e\n\n\u003cCommand.List\u003e\n\t{#if loading}\n\t\t\u003cCommand.Loading progress={0.5}\u003eLoading…\u003c/Command.Loading\u003e\n\t{/if}\n\u003c/Command.List\u003e;\n```\n\n### `createState(initialState?: State)`\n\nCreate a state store which can be passed and used by the component. This is provided for more advanced use cases and should not be commonly used.\n\nA good use case would be to render a more detailed empty state, like so:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command, createState } from 'cmdk-sv';\n\n\tconst state = createState();\n\u003c/script\u003e\n\n\u003cCommand.Root {state}\u003e\n\t\u003cCommand.Empty\u003e\n\t\t{#if $state.search}\n\t\t\tNo results found for \"{state.search}\".\n\t\t{:else}\n\t\t\tNo results found.\n\t\t{/if}\n\t\u003c/Command.Empty\u003e\n\u003c/Command.Root\u003e\n```\n\n## Examples\n\nCode snippets for common use cases.\n\n### Nested items\n\nOften selecting one item should navigate deeper, with a more refined set of items. For example selecting \"Change theme…\" should show new items \"Dark theme\" and \"Light theme\". We call these sets of items \"pages\", and they can be implemented with simple state:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\tlet open = false;\n\tlet search = '';\n\tlet pages: string[] = [];\n\tlet page: string | undefined = undefined;\n\n\t$: page = pages[pages.length - 1];\n\n\tfunction changePage(newPage: string) {\n\t\tpages = [...pages, newPage];\n\t}\n\u003c/script\u003e\n\n\u003cCommand\n\tonKeyDown={(e) =\u003e {\n\t\t// Escape goes to previous page\n\t\t// Backspace goes to previous page when search is empty\n\t\tif (e.key === 'Escape' || (e.key === 'Backspace' \u0026\u0026 !search)) {\n\t\t\te.preventDefault();\n\t\t\tconst newPages = pages.slice(0, -1);\n\t\t\tpages = newPages;\n\t\t}\n\t}}\n\u003e\n\t\u003cCommand.Input bind:value={search} /\u003e\n\t\u003cCommand.List\u003e\n\t\t{#if !page}\n\t\t\t\u003cCommand.Item onSelect={() =\u003e changePage('projects')}\u003eSearch projects…\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Item onSelect={() =\u003e changePage('teams')}\u003eJoin a team…\u003c/Command.Item\u003e\n\t\t{:else if page === 'projects'}\n\t\t\t\u003cCommand.Item\u003eProject A\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Item\u003eProject B\u003c/Command.Item\u003e\n\t\t{:else if page === 'teams'}\n\t\t\t\u003cCommand.Item\u003eTeam 1\u003c/Command.Item\u003e\n\t\t\t\u003cCommand.Item\u003eTeam 2\u003c/Command.Item\u003e\n\t\t{/if}\n\t\u003c/Command.List\u003e\n\u003c/Command\u003e\n```\n\n### Show sub-items when searching\n\nIf your items have nested sub-items that you only want to reveal when searching, render based on the search state:\n\n```svelte\n\u003c!-- SubItem.svelte --\u003e\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\n\ttype $$Props = Command.ItemProps \u0026 {\n\t\tsearch?: string;\n\t};\n\u003c/script\u003e\n\n{#if search}\n\t\u003cCommand.Item {...$$restProps}\u003e\n\t\t\u003cslot /\u003e\n\t\u003c/Command.Item\u003e\n{/if}\n```\n\nUsing the state store:\n\n```svelte\n\u003c!-- CommandMenu.svelte --\u003e\n\u003cscript lang=\"ts\"\u003e\n\timport { Command, createState } from 'cmdk-sv';\n\timport SubItem from './SubItem.svelte';\n\tconst state = createState();\n\u003c/script\u003e\n\n\u003cCommand.Root {state}\u003e\n\t\u003cCommand.Input /\u003e\n\t\u003cCommand.List\u003e\n\t\t\u003cCommand.Item\u003eChange theme…\u003c/Command.Item\u003e\n\t\t\u003cSubItem search={$state.search}\u003eChange theme to dark\u003c/SubItem\u003e\n\t\t\u003cSubItem search={$state.search}\u003eChange theme to light\u003c/SubItem\u003e\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\nor\n\nUsing the input value:\n\n```svelte\n\u003c!-- CommandMenu.svelte --\u003e\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\timport SubItem from './SubItem.svelte';\n\tlet search: string;\n\u003c/script\u003e\n\n\u003cCommand.Root\u003e\n\t\u003cCommand.Input bind:value={search} /\u003e\n\t\u003cCommand.List\u003e\n\t\t\u003cCommand.Item\u003eChange theme…\u003c/Command.Item\u003e\n\t\t\u003cSubItem {search}\u003eChange theme to dark\u003c/SubItem\u003e\n\t\t\u003cSubItem {search}\u003eChange theme to light\u003c/SubItem\u003e\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\n### Asynchronous results\n\nRender the items as they become available. Filtering and sorting will happen automatically.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\n\tlet loading = false;\n\tlet items: string[] = [];\n\n\tonMount(async () =\u003e {\n\t\tloading = true;\n\t\tconst res = await api.get('/dictionary');\n\t\titems = res;\n\t\tloading = false;\n\t});\n\u003c/script\u003e\n\n\u003cCommand.Root\u003e\n\t\u003cCommand.Input /\u003e\n\t\u003cCommand.List\u003e\n\t\t{#if loading}\n\t\t\t\u003cCommand.Loading\u003eFetching words…\u003c/Command.Loading\u003e\n\t\t{:else}\n\t\t\t{#each items as item}\n\t\t\t\t\u003cCommand.Item value={item}\u003e\n\t\t\t\t\t{item}\n\t\t\t\t\u003c/Command.Item\u003e\n\t\t\t{/each}\n\t\t{/if}\n\t\u003c/Command.List\u003e\n\u003c/Command.Root\u003e\n```\n\n### Use inside Popover\n\nWe recommend using the [Bits UI popover](https://www.bits-ui.com/docs/components/popover) component. ⌘K-sv relies on the Bits UI Dialog component, so this will reduce the number of dependencies you'll need.\n\n```bash\nnpm install bits-ui\n```\n\nRender `Command` inside of the popover content:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n\timport { Command } from 'cmdk-sv';\n\timport { Popover } from 'bits-ui';\n\u003c/script\u003e\n\n\u003cPopover.Root\u003e\n\t\u003cPopover.Trigger\u003eToggle popover\u003c/Popover.Trigger\u003e\n\n\t\u003cPopover.Content\u003e\n\t\t\u003cCommand.Root\u003e\n\t\t\t\u003cCommand.Input /\u003e\n\t\t\t\u003cCommand.List\u003e\n\t\t\t\t\u003cCommand.Item\u003eApple\u003c/Command.Item\u003e\n\t\t\t\u003c/Command.List\u003e\n\t\t\u003c/Command.Root\u003e\n\t\u003c/Popover.Content\u003e\n\u003c/Popover.Root\u003e\n```\n\n### Drop in stylesheets\n\nYou can find global stylesheets to drop in as a starting point for styling. See [src/styles/cmdk](src/styles/cmdk) for examples.\n\n### Render Delegation\n\nEach of the components (except the dialog) accept an `asChild` prop that can be used to render a custom element in place of the default. When using this prop, you'll need to check the components slot props to see what attributes \u0026 actions you'll need to pass to your custom element.\n\nComponents that contain only a single element will just have `attrs` \u0026 `action` slot props, or just `attrs`. Components that contain multiple elements will have an `attrs` and possibly an `actions` object whose properties are the attributes and actions for each element.\n\n## FAQ\n\n**Accessible?** Yes. Labeling, aria attributes, and DOM ordering tested with Voice Over and Chrome DevTools. [Dialog](#dialog-cmdk-dialog-cmdk-overlay) composes an accessible Dialog implementation.\n\n**Filter/sort items manually?** Yes. Pass `shouldFilter={false}` to [Command](#command-cmdk-root). Better memory usage and performance. Bring your own virtualization this way.\n\n**Unstyled?** Yes, use the listed CSS selectors.\n\n**Weird/wrong behavior?** Make sure your `Command.Item` has a unique `value`.\n\n**Listen for ⌘K automatically?** No, do it yourself to have full control over keybind context.\n\n## History\n\nWritten in 2019 by Paco ([@pacocoursey](https://twitter.com/pacocoursey)) to see if a composable combobox API was possible. Used for the Vercel command menu and autocomplete by Rauno ([@raunofreiberg](https://twitter.com/raunofreiberg)) in 2020. Re-written independently in 2022 with a simpler and more performant approach. Ideas and help from Shu ([@shuding\\_](https://twitter.com/shuding_)).\n\nPorted to Svelte in 2023 by Huntabyte ([@huntabyte](https://twitter.com/huntabyte))\n\n## Sponsors\n\nThis project is supported by the following beautiful people/organizations:\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/sponsors/huntabyte\"\u003e\n    \u003cimg src='https://github.com/huntabyte/static/blob/main/sponsors.svg?raw=true' alt=\"Logos from Sponsors\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## License\n\n\u003c!-- automd:contributors license=MIT author=\"huntabyte\" github=\"huntabyte/cmdk-sv\" --\u003e\n\nPublished under the [MIT](https://github.com/huntabyte/cmdk-sv/blob/main/LICENSE) license.\nMade by [@huntabyte](https://github.com/huntabyte) and [community](https://github.com/huntabyte/cmdk-sv/graphs/contributors) 💛\n\u003cbr\u003e\u003cbr\u003e\n\u003ca href=\"https://github.com/huntabyte/cmdk-sv/graphs/contributors\"\u003e\n\u003cimg src=\"https://contrib.rocks/image?repo=huntabyte/cmdk-sv\" /\u003e\n\u003c/a\u003e\n\n\u003c!-- /automd --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuntabyte%2Fcmdk-sv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhuntabyte%2Fcmdk-sv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuntabyte%2Fcmdk-sv/lists"}