{"name":"combobox","title":"Combobox","description":"Autocomplete input and command palette with a list of suggestions, filtered as the user types.","installCommand":"bunx marko-ui add combobox -y","usageTags":"<Combobox>","importSnippet":"import Combobox from \"@/components/ui/combobox/combobox.marko\";","usageSnippet":"static const frameworks = [\n  { value: \"marko\", label: \"Marko\" },\n  { value: \"svelte\", label: \"Svelte\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select framework...\"/>","tags":["Combobox"],"parts":[],"props":[{"name":"items","type":"ComboboxItem[]","required":true},{"name":"allowCustomValue","type":"boolean | undefined","required":false,"description":"Whether to allow typing custom values in the input"},{"name":"alwaysSubmitOnEnter","type":"boolean | undefined","required":false,"default":"false","description":"Whether to always submit on Enter key press, even if popup is open.\nUseful for single-field autocomplete forms where Enter should submit the form."},{"name":"autoFocus","type":"boolean | undefined","required":false,"description":"Whether to autofocus the input on mount"},{"name":"closeOnSelect","type":"boolean | undefined","required":false,"description":"Whether to close the combobox when an item is selected."},{"name":"composite","type":"boolean | undefined","required":false,"default":"true","description":"Whether the combobox is a composed with other composite widgets like tabs"},{"name":"defaultHighlightedValue","type":"string | null | undefined","required":false,"description":"The initial highlighted value of the combobox when rendered.\nUse when you don't need to control the highlighted value of the combobox."},{"name":"defaultInputValue","type":"string | undefined","required":false,"default":"","description":"The initial value of the combobox's input when rendered.\nUse when you don't need to control the value of the combobox's input."},{"name":"defaultOpen","type":"boolean | undefined","required":false,"description":"The initial open state of the combobox when rendered.\nUse when you don't need to control the open state of the combobox."},{"name":"defaultValue","type":"string[] | undefined","required":false,"default":"[]","description":"The initial value of the combobox's selected items when rendered.\nUse when you don't need to control the value of the combobox's selected items."},{"name":"dir","type":"'ltr' | 'rtl' | undefined","required":false,"default":"ltr","description":"The document's text/writing direction."},{"name":"disabled","type":"boolean | undefined","required":false,"description":"Whether the combobox is disabled"},{"name":"disableLayer","type":"boolean | undefined","required":false,"description":"Whether to disable registering this a dismissable layer"},{"name":"form","type":"string | undefined","required":false,"description":"The associate form of the combobox."},{"name":"getRootNode","type":"(() => ShadowRoot | Document | Node) | undefined","required":false,"description":"A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."},{"name":"groups","type":"ComboboxGroupData[]","required":false},{"name":"highlightedValue","type":"string | null | undefined","required":false,"description":"The controlled highlighted value of the combobox"},{"name":"ids","type":"ElementIds | undefined","required":false,"description":"The ids of the elements in the combobox. Useful for composition."},{"name":"inputBehavior","type":"'none' | 'autocomplete' | 'autohighlight' | undefined","required":false,"default":"none","description":"Defines the auto-completion behavior of the combobox.\n\n- `autohighlight`: The first focused item is highlighted as the user types\n- `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated"},{"name":"inputValue","type":"string | undefined","required":false,"description":"The controlled value of the combobox's input"},{"name":"invalid","type":"boolean | undefined","required":false,"description":"Whether the combobox is invalid"},{"name":"loopFocus","type":"boolean | undefined","required":false,"default":"true","description":"Whether to loop the keyboard navigation through the items"},{"name":"multiple","type":"boolean | undefined","required":false,"description":"Whether to allow multiple selection.\n\n**Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`.\nIt is recommended to render the selected items in a separate container."},{"name":"name","type":"string | undefined","required":false,"description":"The `name` attribute of the combobox's input. Useful for form submission"},{"name":"navigate","type":"((details: NavigateDetails) => void) | null | undefined","required":false,"description":"Function to navigate to the selected item"},{"name":"open","type":"boolean | undefined","required":false,"description":"The controlled open state of the combobox"},{"name":"openOnChange","type":"boolean | ((details: InputValueChangeDetails) => boolean) | undefined","required":false,"default":"true","description":"Whether to show the combobox when the input value changes"},{"name":"openOnClick","type":"boolean | undefined","required":false,"default":"false","description":"Whether to open the combobox popup on initial click on the input"},{"name":"openOnKeyPress","type":"boolean | undefined","required":false,"default":"true","description":"Whether to open the combobox on arrow key press"},{"name":"placeholder","type":"string | undefined","required":false,"description":"The placeholder text of the combobox's input"},{"name":"positioning","type":"PositioningOptions | undefined","required":false,"description":"The positioning options to dynamically position the menu"},{"name":"readOnly","type":"boolean | undefined","required":false,"description":"Whether the combobox is readonly. This puts the combobox in a \"non-editable\" mode\nbut the user can still interact with it"},{"name":"required","type":"boolean | undefined","required":false,"description":"Whether the combobox is required"},{"name":"scrollToIndexFn","type":"((details: ScrollToIndexDetails) => void) | undefined","required":false,"description":"Function to scroll to a specific index"},{"name":"selectionBehavior","type":"'clear' | 'replace' | 'preserve' | undefined","required":false,"default":"replace","description":"The behavior of the combobox input when an item is selected\n\n- `replace`: The selected item string is set as the input value\n- `clear`: The input value is cleared\n- `preserve`: The input value is preserved"},{"name":"showClear","type":"boolean","required":false,"description":"Show the clear (\"x\") button when a value is selected."},{"name":"translations","type":"IntlTranslations | undefined","required":false,"description":"Specifies the localized strings that identifies the accessibility elements and their states"},{"name":"value","type":"string[] | undefined","required":false,"description":"The controlled value of the combobox's selected items"}],"events":[{"name":"highlightChange","arg":"string"},{"name":"inputValueChange","arg":"string"},{"name":"onFocusOutside","arg":"FocusOutsideEvent","description":"Function called when the focus is moved outside the component"},{"name":"onHighlightChange","arg":"HighlightChangeDetails<T>","description":"Function called when an item is highlighted using the pointer\nor keyboard navigation."},{"name":"onInputValueChange","arg":"InputValueChangeDetails","description":"Function called when the input's value changes"},{"name":"onInteractOutside","arg":"InteractOutsideEvent","description":"Function called when an interaction happens outside the component"},{"name":"onOpenChange","arg":"OpenChangeDetails","description":"Function called when the popup is opened"},{"name":"onPointerDownOutside","arg":"PointerDownOutsideEvent","description":"Function called when the pointer is pressed down outside the component"},{"name":"onSelect","arg":"SelectionDetails","description":"Function called when an item is selected"},{"name":"onValueChange","arg":"ValueChangeDetails<T>","description":"Function called when a new item is selected"},{"name":"valueChange","arg":"string[]"}],"keyboard":[{"keys":"↓ / ↑","description":"Move the highlighted item down or up the (open) list."},{"keys":"Enter","description":"Select the highlighted item."},{"keys":"Escape","description":"Close the list without changing the selection."},{"keys":"Home / End","description":"Jump the highlight to the first or last item."}],"accessibilityNotes":["Built on `@zag-js/combobox`, which implements the [ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) (`role=\"combobox\"` on the input, `role=\"listbox\"` on the list, `aria-activedescendant` tracking the highlighted item).","`invalid` sets `aria-invalid` on the input and `data-invalid` throughout the part tree, so both assistive tech and the `has-aria-invalid:` styling hooks pick it up from one prop.","In `multiple` mode, each selected chip carries its own focusable remove button (`data-slot=\"combobox-chip-remove\"`) rather than only being removable by re-selecting in the list.","`dir=\"rtl\"` is a real machine prop, not just a CSS toggle — it also reverses Zag's arrow-key navigation and floating-list positioning to match reading direction."],"examples":[{"id":"combobox-demo","title":"Default","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n  { value: \"marko\", label: \"Marko\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select framework...\"/>","description":"Type to filter the list, or use the trigger to open the full set of items."},{"id":"combobox-basic","title":"Basic","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select a framework\"/>","description":"A simple combobox with a list of frameworks."},{"id":"combobox-multiple","title":"Multiple","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n];\n\n<div class=\"flex w-full max-w-xs flex-col gap-2\">\n  <FieldLabel for=\"combobox-multiple-input\">Frameworks</FieldLabel>\n  <Combobox\n    items=frameworks\n    multiple\n    inputBehavior=\"autohighlight\"\n    defaultValue=[\"next\"]\n    ids={ input: \"combobox-multiple-input\" }\n    class=\"w-full\"\n  />\n</div>","description":"Pass `multiple` for multi-select with chips. Pair with `inputBehavior=\"autohighlight\"` to highlight the first match while typing."},{"id":"combobox-clear","title":"Clear Button","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select a framework\" defaultValue=[\"next\"] showClear/>","description":"Pass `showClear` to show a clear (\"x\") button once a value is selected."},{"id":"combobox-groups","title":"Groups","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const groups = [\n  {\n    label: \"Americas\",\n    items: [\n      { value: \"new-york\", label: \"(GMT-5) New York\" },\n      { value: \"los-angeles\", label: \"(GMT-8) Los Angeles\" },\n      { value: \"chicago\", label: \"(GMT-6) Chicago\" },\n      { value: \"toronto\", label: \"(GMT-5) Toronto\" },\n      { value: \"vancouver\", label: \"(GMT-8) Vancouver\" },\n      { value: \"sao-paulo\", label: \"(GMT-3) São Paulo\" },\n    ],\n  },\n  {\n    label: \"Europe\",\n    items: [\n      { value: \"london\", label: \"(GMT+0) London\" },\n      { value: \"paris\", label: \"(GMT+1) Paris\" },\n      { value: \"berlin\", label: \"(GMT+1) Berlin\" },\n      { value: \"rome\", label: \"(GMT+1) Rome\" },\n      { value: \"madrid\", label: \"(GMT+1) Madrid\" },\n      { value: \"amsterdam\", label: \"(GMT+1) Amsterdam\" },\n    ],\n  },\n  {\n    label: \"Asia/Pacific\",\n    items: [\n      { value: \"tokyo\", label: \"(GMT+9) Tokyo\" },\n      { value: \"shanghai\", label: \"(GMT+8) Shanghai\" },\n      { value: \"singapore\", label: \"(GMT+8) Singapore\" },\n      { value: \"dubai\", label: \"(GMT+4) Dubai\" },\n      { value: \"sydney\", label: \"(GMT+11) Sydney\" },\n      { value: \"seoul\", label: \"(GMT+9) Seoul\" },\n    ],\n  },\n];\n\nstatic const items = groups.flatMap((group) => group.items);\n\n<Combobox items=items groups=groups placeholder=\"Select a timezone\"/>","description":"Pass `groups` alongside `items` to render a separated, labeled grouping of the same values."},{"id":"combobox-invalid","title":"Invalid","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select a framework\" invalid/>","description":"Pass `invalid` to mark the combobox invalid (sets `aria-invalid`/`data-invalid` throughout).","essential":true},{"id":"combobox-disabled","title":"Disabled","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n  { value: \"marko\", label: \"Marko\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select framework...\" disabled/>","description":"Pass `disabled` to disable the input and trigger."},{"id":"combobox-auto-highlight","title":"Auto Highlight","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n];\n\n<Combobox items=frameworks placeholder=\"Select a framework\" inputBehavior=\"autohighlight\"/>","description":"Pass `inputBehavior=\"autohighlight\"` to automatically highlight the first matching item while filtering."},{"id":"combobox-rtl","title":"RTL","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\nimport Field from \"@marko-ui/shadcn/ui/field/field.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\n\nstatic const categories = [\n  { value: \"technology\", label: \"التكنولوجيا\" },\n  { value: \"design\", label: \"التصميم\" },\n  { value: \"business\", label: \"الأعمال\" },\n  { value: \"marketing\", label: \"التسويق\" },\n  { value: \"education\", label: \"التعليم\" },\n  { value: \"health\", label: \"الصحة\" },\n];\n\n<Field dir=\"rtl\" class=\"mx-auto w-full max-w-xs\">\n  <FieldLabel for=\"combobox-rtl-input\">الفئات</FieldLabel>\n  <Combobox\n    items=categories\n    multiple\n    inputBehavior=\"autohighlight\"\n    defaultValue=[\"technology\"]\n    placeholder=\"أضف فئات\"\n    ids={ input: \"combobox-rtl-input\" }\n    dir=\"rtl\"\n  />\n</Field>","description":"`dir` is a real `@zag-js/combobox` prop — it drives both the DOM attribute and Zag's RTL-aware keyboard/positioning logic."},{"id":"combobox-controlled","title":"Controlled","source":"import Combobox from \"@marko-ui/shadcn/ui/combobox/combobox.marko\";\n\nstatic const frameworks = [\n  { value: \"next\", label: \"Next.js\" },\n  { value: \"sveltekit\", label: \"SvelteKit\" },\n  { value: \"nuxt\", label: \"Nuxt.js\" },\n  { value: \"remix\", label: \"Remix\" },\n  { value: \"astro\", label: \"Astro\" },\n  { value: \"marko\", label: \"Marko\" },\n];\n\n<let/value=[\"marko\"]/>\n\n<div class=\"flex flex-col gap-2\">\n  <Combobox\n    items=frameworks\n    placeholder=\"Select framework...\"\n    value=value\n    valueChange=(next) => { value = next; }\n  />\n  <span class=\"text-sm text-muted-foreground\">\n    value: ${value.join(\", \") || \"(none)\"}\n  </span>\n</div>","description":"Zag machines are controlled: a `value` prop without a change handler never moves. Pair it with `valueChange`.","essential":true}],"importInstallCommand":"bun add @marko-ui/shadcn marko-zag","nativeAttributes":"div","itemTypes":[{"prop":"items","typeName":"ComboboxItem","fields":[{"name":"value","type":"string","required":true},{"name":"label","type":"string","required":true},{"name":"disabled","type":"boolean","required":false}]},{"prop":"groups","typeName":"ComboboxGroupData","fields":[{"name":"label","type":"string","required":true},{"name":"items","type":"ComboboxItem[]","required":true}]}],"composition":"Our `<Combobox>` is a single tag, not a compound-component tree — everything\nupstream expresses as nested parts (`ComboboxInput`, `ComboboxContent`,\n`ComboboxItem`, `ComboboxChips`...) is a prop or a rendered slot here,\ndriven by one `@zag-js/combobox` machine.\n\n**Simple** (see [Default](#default)): pass `items`. The tag renders its\nown input, trigger button, and a floating list — no assembly required.\n\n```text\nCombobox (items=)\n├── input           (data-slot=\"combobox-input\")\n├── trigger button  (data-slot=\"combobox-trigger\")\n└── positioner      (data-slot=\"combobox-positioner\", portalled)\n    └── list        (data-slot=\"combobox-list\")\n        └── item    (data-slot=\"combobox-item\", one per entry)\n```\n\n**With chips** (see [Multiple](#multiple)): pass\n`multiple`. The single input is swapped for a chip row with an inline text\ninput — selected items render as `data-slot=\"combobox-chip\"` pills, each\nwith its own remove button.\n\n```text\nCombobox (items= multiple)\n├── chips            (data-slot=\"combobox-chips\")\n│   ├── chip         (data-slot=\"combobox-chip\", one per selected value)\n│   └── chip input   (data-slot=\"combobox-chip-input\")\n└── positioner → list → item  (same as Simple)\n```\n\n**With groups** (see [Groups](#groups)): pass `groups` alongside\nthe flat `items` (items still drives the filterable collection; groups is a\ndisplay-only regrouping of the same values). Each group renders a label and\nits own item block, separated visually.\n\n```text\nCombobox (items= groups=)\n└── positioner → list\n    ├── group        (data-slot=\"combobox-group\")\n    │   ├── label    (data-slot=\"combobox-label\")\n    │   └── collection (data-slot=\"combobox-collection\")\n    │       └── item (one per group entry)\n    ├── separator    (data-slot=\"combobox-separator\", between groups)\n    └── group ...\n```"}
