Componentcommand
Command
Fast, composable, unstyled command menu for Go and templ.
Preview
Fast, composable, unstyled command menu for Go and templ.
Add component
@ui.Command(ui.CommandProps{DOMProps: ui.DOMProps{Class: "rounded-lg border shadow-md md:min-w-[450px]"}}) {
@ui.CommandInput(ui.InputProps{Placeholder: "Type a command or search..."})
@ui.CommandList(ui.DOMProps{}) {
@ui.CommandEmpty(ui.DOMProps{}) { No results found. }
@ui.CommandGroup(ui.DOMProps{}) {
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "calendar"}) { Calendar }
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "search-emoji"}) { Search Emoji }
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "calculator"}) { Calculator }
}
}
}Installation
Add this component to your project using the CLI.
templcn add commandUsage
1. Import the component in your Go or templ file:
import "your-module/ui"2. Use the component in your template:
@ui.Command(ui.CommandProps{DOMProps: ui.DOMProps{Class: "rounded-lg border shadow-md md:min-w-[450px]"}}) {
@ui.CommandInput(ui.InputProps{Placeholder: "Type a command or search..."})
@ui.CommandList(ui.DOMProps{}) {
@ui.CommandEmpty(ui.DOMProps{}) { No results found. }
@ui.CommandGroup(ui.DOMProps{}) {
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "calendar"}) { Calendar }
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "search-emoji"}) { Search Emoji }
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "calculator"}) { Calculator }
}
}
}Composition
Use these parts together to build the component.
Command
├── CommandInput
├── CommandList
├── CommandEmpty
├── CommandGroup
├── CommandItemExamples
Named examples and common variations.
Default
Command palette with input and item groups.
Add component
@ui.Command(ui.CommandProps{}) {
@ui.CommandInput(ui.InputProps{Placeholder: "Search commands"})
@ui.CommandList(ui.DOMProps{}) {
@ui.CommandGroup(ui.DOMProps{}) {
@ui.CommandItem(ui.DropdownMenuItemProps{Value: "settings"}) { Settings }
}
}
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| DefaultValue | string | "" | Default search value. |
View source
package ui
import (
"context"
"io"
"github.com/a-h/templ"
)
type CommandProps struct {
DOMProps
Value string
DefaultValue string
Open bool
Placeholder string
Title string
Description string
ShowCloseButton bool
}
func Command(props CommandProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props.DOMProps, "command", "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground")
value := props.Value
if value == "" {
value = props.DefaultValue
}
if value != "" {
attrs["data-value"] = value
}
if props.DefaultValue != "" {
attrs["data-default-value"] = props.DefaultValue
}
if props.Open {
attrs["data-open"] = "true"
}
if props.Placeholder != "" {
attrs["data-placeholder"] = props.Placeholder
}
if props.Title != "" {
attrs["data-title"] = props.Title
}
if props.Description != "" {
attrs["data-description"] = props.Description
}
if props.ShowCloseButton {
attrs["data-show-close-button"] = "true"
}
attrs["role"] = "combobox"
attrs["aria-expanded"] = "true"
return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
})
}
func CommandDialog(props CommandProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props.DOMProps, "command-dialog", "fixed inset-0 z-50 grid place-items-center")
if props.Open {
attrs["data-open"] = "true"
}
return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
})
}
func CommandInput(props InputProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props.DOMProps, "command-input", "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50")
if props.Type == "" {
props.Type = "text"
}
attrs["type"] = props.Type
if props.Name != "" {
attrs["name"] = props.Name
}
if props.Value != "" {
attrs["value"] = props.Value
}
if props.Placeholder != "" {
attrs["placeholder"] = props.Placeholder
}
if _, ok := attrs["role"]; !ok {
attrs["role"] = "searchbox"
}
if _, ok := attrs["aria-autocomplete"]; !ok {
attrs["aria-autocomplete"] = "list"
}
input := templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
if _, err := io.WriteString(w, `<svg xmlns="http://www.w3.org/2000/svg" class="size-4 shrink-0 opacity-50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle></svg>`); err != nil {
return err
}
return renderVoidElement(ctx, w, "input", attrs)
})
return renderElement(ctx, w, "div", templ.Attributes{"data-slot": "command-input-wrapper", "class": "flex h-9 items-center gap-2 border-b px-3"}, input)
})
}
func CommandList(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props, "command-list", "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto")
if _, ok := attrs["role"]; !ok {
attrs["role"] = "listbox"
}
return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
})
}
func CommandEmpty(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
return renderElement(ctx, w, "div", attrsFromDOMProps(props, "command-empty", "py-6 text-center text-sm"), templ.GetChildren(ctx))
})
}
func CommandGroup(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
return renderElement(ctx, w, "div", attrsFromDOMProps(props, "command-group", "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground"), templ.GetChildren(ctx))
})
}
func CommandItem(props DropdownMenuItemProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
className := "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground"
attrs := attrsFromDOMProps(props.DOMProps, "command-item", className)
if _, ok := attrs["type"]; !ok {
attrs["type"] = "button"
}
if _, ok := attrs["role"]; !ok {
attrs["role"] = "option"
}
if props.Value != "" {
attrs["data-value"] = props.Value
}
if props.Disabled {
attrs["disabled"] = true
attrs["aria-disabled"] = "true"
}
return renderElement(ctx, w, "button", attrs, templ.GetChildren(ctx))
})
}
func CommandShortcut(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
return renderElement(ctx, w, "span", attrsFromDOMProps(props, "command-shortcut", "ml-auto text-xs tracking-widest text-muted-foreground"), templ.GetChildren(ctx))
})
}
func CommandSeparator(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
return renderElement(ctx, w, "div", attrsFromDOMProps(props, "command-separator", "-mx-1 h-px bg-border"), nil)
})
}