{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "combobox",
  "type": "registry:ui",
  "title": "Combobox",
  "description": "Autocomplete text input. Native <datalist> variant (zero JS) + APG role=combobox + htmx-filtered listbox variant.",
  "files": [
    {
      "path": "registry/ui/combobox.tsx",
      "type": "registry:ui",
      "target": "components/ui/combobox.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Combobox — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// **Native-first.** This component is just `<input list>` + `<datalist>`.\n// The browser handles:\n//   - The dropdown UI\n//   - Filtering as the user types\n//   - Click + keyboard selection\n//   - Focus management\n//   - aria-controls / aria-expanded wiring (implicit)\n//\n// No custom JS event handlers, no MutationObserver, no race conditions.\n// If you need server-driven suggestions, point htmx at the <datalist> and\n// have the server return `<option>` tags — the browser uses them\n// transparently.\n//\n// Refs:\n//   repos/mdn/files/en-us/web/html/reference/elements/datalist/index.md\n//   repos/mdn/files/en-us/web/html/reference/elements/input/index.md#list\n//   repos/aria-practices/content/patterns/combobox/\n\n// `disabled` marks an option non-checkable (browsers grey it out, it gets\n// no click/focus events).\n// repos/mdn/files/en-us/web/html/reference/elements/option/index.md:45\nexport type ComboboxOption = { value: string; label?: string; disabled?: boolean }\n\ntype ComboboxProps = {\n  // The input's id. The <datalist> gets `${id}-list`; if you wire htmx\n  // to fetch options dynamically, target that id.\n  id: string\n  name?: string\n  // Initial options. Server-filtered comboboxes can pass [] and let\n  // htmx populate the datalist on input.\n  options?: ComboboxOption[]\n  // `list` is valid on these 13 input types, not just text — a url/email/\n  // search combobox, or a number/date/time/range/color picker with suggested\n  // values, are all native datalist use cases.\n  // repos/mdn/files/en-us/web/html/reference/elements/input/index.md:492\n  type?:\n    | \"text\"\n    | \"search\"\n    | \"url\"\n    | \"tel\"\n    | \"email\"\n    | \"number\"\n    | \"date\"\n    | \"datetime-local\"\n    | \"month\"\n    | \"week\"\n    | \"time\"\n    | \"range\"\n    | \"color\"\n  placeholder?: string\n  value?: string\n  required?: boolean\n  disabled?: boolean\n  // The user can type any value that passes validation, even one not in the\n  // suggestion list, so constrain the free-typed value with these.\n  // repos/mdn/files/en-us/web/html/reference/elements/input/index.md:505\n  maxlength?: number\n  minlength?: number\n  pattern?: string\n  // Explains the pattern to AT / on validation failure (spec accessibility note).\n  title?: string\n  // Focusable + copy-selectable but not editable. Not supported on range/color.\n  // repos/mdn/files/en-us/web/html/reference/elements/input/index.md:588\n  readonly?: boolean\n  ariaLabel?: string\n  ariaLabelledby?: string\n  ariaDescribedby?: string\n  class?: ClassValue\n  // htmx attrs ride onto the input element. Typical server-filter setup:\n  //   hx-get=\"/api/search\"\n  //   hx-trigger=\"input changed delay:200ms\"\n  //   hx-target=\"#<id>-list\"       (points at the datalist)\n  //   hx-swap=\"innerHTML\"\n  [key: `hx-${string}`]: any\n}\n\nexport function Combobox(props: ComboboxProps) {\n  const {\n    id,\n    name,\n    options = [],\n    type = \"text\",\n    placeholder,\n    value,\n    required,\n    disabled,\n    maxlength,\n    minlength,\n    pattern,\n    title,\n    readonly,\n    ariaLabel,\n    ariaLabelledby,\n    ariaDescribedby,\n    class: className,\n    ...rest\n  } = props\n  const listId = `${id}-list`\n  return (\n    <span data-slot=\"combobox\" class={cn(\"inline-block w-full\", className)}>\n      <input\n        type={type}\n        id={id}\n        name={name}\n        list={listId}\n        value={value}\n        placeholder={placeholder}\n        required={required}\n        disabled={disabled}\n        maxlength={maxlength}\n        minlength={minlength}\n        pattern={pattern}\n        title={title}\n        readonly={readonly}\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabelledby}\n        aria-describedby={ariaDescribedby}\n        // autocomplete=\"off\" stops the browser from layering its own\n        // history-based suggestions on top of the datalist.\n        autocomplete=\"off\"\n        class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\"\n        {...rest}\n      />\n      <datalist id={listId} data-slot=\"combobox-list\">\n        {options.map((o) => (\n          <option value={o.value} label={o.label} disabled={o.disabled} />\n        ))}\n      </datalist>\n    </span>\n  )\n}\n\n// Server-rendered single option used by htmx endpoints. Lets the server\n// return a typed component instead of raw HTML strings.\nexport function ComboboxOption(props: ComboboxOption) {\n  return <option value={props.value} label={props.label} disabled={props.disabled} />\n}\n\n// Back-compat shim: the previous API exposed ComboboxNative for the\n// static-options use case and Combobox for the custom richer one. We\n// collapsed both into a single native Combobox; export the old name as\n// an alias so existing import sites don't break.\nexport const ComboboxNative = Combobox\n\n// Back-compat: ComboboxItem used to be the rich custom variant's option\n// renderer. With native datalist, the right primitive is a plain\n// <option> — exported via ComboboxOption above. Keeping the alias so\n// existing consumers don't break.\nexport const ComboboxItem = ComboboxOption\n"
    },
    {
      "path": "registry/jinja2/combobox.html",
      "type": "registry:file",
      "target": "templates/components/combobox.html",
      "content": "{# Combobox macro — shadcn-htmx, htmx v4 + Tailwind v4.\n   Native <input list> + <datalist>. Browser handles dropdown UI, filter,\n   click + keyboard selection, focus management. No custom JS required.\n\n   Usage (static options):\n     {% from \"components/combobox.html\" import combobox %}\n     {{ combobox(id=\"lang\", name=\"lang\",\n                 options=[{\"value\": \"JavaScript\"}, {\"value\": \"Python\"}]) }}\n\n   Usage (htmx-driven server-filter): target the <datalist> by id and\n   return <option> tags from the server.\n     <input list=\"user-list\" hx-get=\"/api/search\"\n            hx-trigger=\"input changed delay:200ms\"\n            hx-target=\"#user-list\" hx-swap=\"innerHTML\">\n     <datalist id=\"user-list\"></datalist>\n#}\n\n{# `type`: `list` is valid on 13 input types, not just text.\n   repos/mdn/files/en-us/web/html/reference/elements/input/index.md:492\n   maxlength/minlength/pattern constrain the free-typed value (datalist\n   suggestions are not requirements); readonly is not supported on range/color.\n   repos/mdn/files/en-us/web/html/reference/elements/input/index.md:505,588 #}\n{% macro combobox(\n    id,\n    name=none,\n    options=[],\n    type=\"text\",\n    placeholder=none,\n    value=none,\n    required=false,\n    disabled=false,\n    maxlength=none,\n    minlength=none,\n    pattern=none,\n    title=none,\n    readonly=false,\n    aria_label=none,\n    aria_labelledby=none,\n    aria_describedby=none,\n    extra_class=\"\",\n    **attrs\n) %}\n<span data-slot=\"combobox\" class=\"inline-block w-full {{ extra_class }}\">\n  <input\n    type=\"{{ type }}\"\n    id=\"{{ id }}\"\n    {%- if name %} name=\"{{ name }}\"{% endif %}\n    list=\"{{ id }}-list\"\n    {%- if value is not none %} value=\"{{ value }}\"{% endif %}\n    {%- if placeholder %} placeholder=\"{{ placeholder }}\"{% endif %}\n    {%- if required %} required{% endif %}\n    {%- if disabled %} disabled{% endif %}\n    {%- if maxlength is not none %} maxlength=\"{{ maxlength }}\"{% endif %}\n    {%- if minlength is not none %} minlength=\"{{ minlength }}\"{% endif %}\n    {%- if pattern %} pattern=\"{{ pattern }}\"{% endif %}\n    {%- if title %} title=\"{{ title }}\"{% endif %}\n    {%- if readonly %} readonly{% endif %}\n    {%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif %}\n    {%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif %}\n    {%- if aria_describedby %} aria-describedby=\"{{ aria_describedby }}\"{% endif %}\n    autocomplete=\"off\"\n    class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\"\n    {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n  >\n  <datalist id=\"{{ id }}-list\" data-slot=\"combobox-list\">\n    {% for opt in options %}<option value=\"{{ opt.value }}\"{% if opt.label %} label=\"{{ opt.label }}\"{% endif %}{% if opt.disabled %} disabled{% endif %}>{% endfor %}\n  </datalist>\n</span>\n{% endmacro %}\n\n{# A single <option> — useful when an htmx endpoint returns one.\n   `disabled` marks it non-checkable.\n   repos/mdn/files/en-us/web/html/reference/elements/option/index.md:45 #}\n{% macro combobox_option(value, label=none, disabled=false) -%}\n<option value=\"{{ value }}\"{% if label %} label=\"{{ label }}\"{% endif %}{% if disabled %} disabled{% endif %}>\n{%- endmacro %}\n"
    },
    {
      "path": "registry/go-templates/combobox.tmpl",
      "type": "registry:file",
      "target": "components/combobox.tmpl",
      "content": "{{/*\n  Combobox templates — shadcn-htmx, htmx v4 + Tailwind v4.\n  Native <input list> + <datalist>. Browser handles everything; no JS.\n\n      type ComboboxArgs struct {\n          ID, Name, Placeholder, Value, AriaLabel string\n          // Type: `list` is valid on 13 input types, not just text (default).\n          // repos/mdn/files/en-us/web/html/reference/elements/input/index.md:492\n          Type string\n          Options []ComboboxOption\n          Required, Disabled, Readonly bool\n          // Constrain the free-typed value (datalist suggestions are not\n          // requirements). Pattern explained to AT via Title.\n          // repos/mdn/files/en-us/web/html/reference/elements/input/index.md:505\n          MaxLength, MinLength int\n          Pattern, Title       string\n          AriaLabelledby, AriaDescribedby string\n          // For server-filter: pass HxGet/HxTrigger/HxTarget/HxSwap and\n          // an empty Options slice; the datalist will be populated by\n          // htmx response.\n          HxGet, HxTrigger, HxTarget, HxSwap string\n      }\n      // Disabled marks an option non-checkable.\n      // repos/mdn/files/en-us/web/html/reference/elements/option/index.md:45\n      type ComboboxOption struct{ Value, Label string; Disabled bool }\n*/}}\n\n{{define \"combobox\"}}\n<span data-slot=\"combobox\" class=\"inline-block w-full\">\n  <input type=\"{{if .Type}}{{.Type}}{{else}}text{{end}}\" id=\"{{.ID}}\" {{if .Name}}name=\"{{.Name}}\"{{end}} list=\"{{.ID}}-list\"\n         {{if .Value}}value=\"{{.Value}}\"{{end}}\n         {{if .Placeholder}}placeholder=\"{{.Placeholder}}\"{{end}}\n         {{if .Required}}required{{end}} {{if .Disabled}}disabled{{end}}\n         {{if .Readonly}}readonly{{end}}\n         {{if .MaxLength}}maxlength=\"{{.MaxLength}}\"{{end}}\n         {{if .MinLength}}minlength=\"{{.MinLength}}\"{{end}}\n         {{if .Pattern}}pattern=\"{{.Pattern}}\"{{end}}\n         {{if .Title}}title=\"{{.Title}}\"{{end}}\n         {{if .AriaLabel}}aria-label=\"{{.AriaLabel}}\"{{end}}\n         {{if .AriaLabelledby}}aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n         {{if .AriaDescribedby}}aria-describedby=\"{{.AriaDescribedby}}\"{{end}}\n         {{if .HxGet}}hx-get=\"{{.HxGet}}\"{{end}}\n         {{if .HxTrigger}}hx-trigger=\"{{.HxTrigger}}\"{{end}}\n         {{if .HxTarget}}hx-target=\"{{.HxTarget}}\"{{end}}\n         {{if .HxSwap}}hx-swap=\"{{.HxSwap}}\"{{end}}\n         autocomplete=\"off\"\n         class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\">\n  <datalist id=\"{{.ID}}-list\" data-slot=\"combobox-list\">\n    {{range .Options}}<option value=\"{{.Value}}\"{{if .Label}} label=\"{{.Label}}\"{{end}}{{if .Disabled}} disabled{{end}}>{{end}}\n  </datalist>\n</span>\n{{end}}\n\n{{define \"combobox_option\"}}\n<option value=\"{{.Value}}\"{{if .Label}} label=\"{{.Label}}\"{{end}}{{if .Disabled}} disabled{{end}}>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/combobox.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/combobox.ex",
      "content": "defmodule ShadcnHtmx.Components.Combobox do\n  @moduledoc \"\"\"\n  Combobox — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Native `<input list>` + `<datalist>`. The browser handles the dropdown\n  UI, filter, click + keyboard selection, focus management. No custom JS.\n\n  ## Examples\n\n      # Static options\n      <.combobox id=\"lang\" name=\"lang\" placeholder=\"Pick a language…\"\n        options={[%{value: \"JavaScript\"}, %{value: \"Python\"}, %{value: \"Go\"}]} />\n\n      # Server-filter via htmx — target the datalist by id.\n      <.combobox id=\"user\" name=\"user\" placeholder=\"Search users…\"\n        hx-get={~p\"/api/users/search\"}\n        hx-trigger=\"input changed delay:200ms\"\n        hx-target=\"#user-list\"\n        hx-swap=\"innerHTML\" />\n      # Endpoint returns: <.combobox_option value=\"ada\" />\n  \"\"\"\n\n  use Phoenix.Component\n\n  attr :id, :string, required: true\n  attr :name, :string, default: nil\n  # `list` is valid on 13 input types, not just text.\n  # repos/mdn/files/en-us/web/html/reference/elements/input/index.md:492\n  attr :type, :string, default: \"text\"\n  attr :placeholder, :string, default: nil\n  attr :value, :string, default: nil\n  attr :options, :list, default: []\n  attr :required, :boolean, default: false\n  attr :disabled, :boolean, default: false\n  # Focusable + selectable but not editable. Not supported on range/color.\n  # repos/mdn/files/en-us/web/html/reference/elements/input/index.md:588\n  attr :readonly, :boolean, default: false\n  # Constrain the free-typed value — datalist suggestions are not requirements.\n  # repos/mdn/files/en-us/web/html/reference/elements/input/index.md:505\n  attr :maxlength, :integer, default: nil\n  attr :minlength, :integer, default: nil\n  attr :pattern, :string, default: nil\n  attr :title, :string, default: nil\n  attr :\"aria-label\", :string, default: nil\n  attr :\"aria-labelledby\", :string, default: nil\n  attr :\"aria-describedby\", :string, default: nil\n  attr :class, :string, default: nil\n\n  attr :rest, :global,\n    include: ~w(hx-get hx-post hx-trigger hx-target hx-swap hx-vals hx-headers)\n\n  def combobox(assigns) do\n    ~H\"\"\"\n    <span data-slot=\"combobox\" class={[\"inline-block w-full\", @class]}>\n      <input\n        type={@type}\n        id={@id}\n        name={@name}\n        list={\"#{@id}-list\"}\n        value={@value}\n        placeholder={@placeholder}\n        required={@required}\n        disabled={@disabled}\n        readonly={@readonly}\n        maxlength={@maxlength}\n        minlength={@minlength}\n        pattern={@pattern}\n        title={@title}\n        aria-label={assigns[:\"aria-label\"]}\n        aria-labelledby={assigns[:\"aria-labelledby\"]}\n        aria-describedby={assigns[:\"aria-describedby\"]}\n        autocomplete=\"off\"\n        class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\"\n        {@rest}\n      />\n      <datalist id={\"#{@id}-list\"} data-slot=\"combobox-list\">\n        <option\n          :for={opt <- @options}\n          value={opt[:value]}\n          label={opt[:label]}\n          disabled={opt[:disabled]}\n        />\n      </datalist>\n    </span>\n    \"\"\"\n  end\n\n  attr :value, :string, required: true\n  attr :label, :string, default: nil\n  # Marks the option non-checkable (browsers grey it out).\n  # repos/mdn/files/en-us/web/html/reference/elements/option/index.md:45\n  attr :disabled, :boolean, default: false\n\n  def combobox_option(assigns) do\n    ~H\"\"\"\n    <option value={@value} label={@label} disabled={@disabled} />\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/combobox.html",
      "type": "registry:file",
      "target": "snippets/combobox.html",
      "content": "<!--\n  shadcn-htmx — raw HTML combobox snippet.\n\n  Native <input list> + <datalist>. The browser handles dropdown UI,\n  filter, click + keyboard selection, focus management. Zero JS.\n\n  Two patterns:\n    1. Static options — fill the <datalist> at render time.\n    2. Server-filter — leave the <datalist> empty and let htmx populate\n       it on input. Server returns <option> tags.\n\n  Native attrs you can add to the <input> (all optional):\n    - type: `list` is valid on 13 types (text/search/url/tel/email/number/\n      date/datetime-local/month/week/time/range/color), so swap type=\"text\"\n      for e.g. type=\"url\" to get a URL combobox.\n      repos/mdn/files/en-us/web/html/reference/elements/input/index.md:492\n    - maxlength / minlength / pattern / title: constrain the free-typed value\n      — datalist suggestions are not requirements, the user can type anything\n      that passes validation.\n      repos/mdn/files/en-us/web/html/reference/elements/input/index.md:505\n    - readonly: focusable + copy-selectable but not editable (not on range/color).\n    - aria-describedby: id of a hint / error element announced after the name.\n  An <option> can carry `disabled` to render a non-checkable suggestion.\n    repos/mdn/files/en-us/web/html/reference/elements/option/index.md:45\n-->\n\n<!-- 1. Static options -->\n<span data-slot=\"combobox\" class=\"inline-block w-full\">\n  <input type=\"text\" id=\"lang\" name=\"lang\" list=\"lang-list\"\n         placeholder=\"Pick a language…\" autocomplete=\"off\"\n         class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\">\n  <datalist id=\"lang-list\" data-slot=\"combobox-list\">\n    <option value=\"JavaScript\">\n    <option value=\"TypeScript\">\n    <option value=\"Python\">\n    <option value=\"Go\">\n    <option value=\"Rust\">\n    <!-- A non-checkable suggestion (greyed out, no click/focus). -->\n    <option value=\"COBOL\" disabled>\n  </datalist>\n</span>\n\n<!-- 2. Server-filter via htmx -->\n<span data-slot=\"combobox\" class=\"inline-block w-full\">\n  <input type=\"text\" id=\"user\" name=\"user\" list=\"user-list\"\n         placeholder=\"Search users…\" autocomplete=\"off\"\n         hx-get=\"/api/users/search\"\n         hx-trigger=\"input changed delay:200ms\"\n         hx-target=\"#user-list\"\n         hx-swap=\"innerHTML\"\n         class=\"flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\">\n  <datalist id=\"user-list\" data-slot=\"combobox-list\">\n    <!-- Server returns: -->\n    <!-- <option value=\"Ada Lovelace\"> -->\n    <!-- <option value=\"Grace Hopper\"> -->\n  </datalist>\n</span>\n"
    }
  ]
}
