React
Command menu con shadcn
Il Command di shadcn è una lista filtrabile costruita su cmdk: mettila in un Dialog, aprila con ⌘K e assegna a ogni voce un value univoco.
In sintesi
- Il Command shadcn è un wrapper su cmdk: filtro, selezione e navigazione da tastiera arrivano dalla libreria.
- `value` è la stringa su cui cmdk cerca: due voci con lo stesso testo vanno separate con value espliciti.
- Con `shouldFilter={false}` il filtro passa al server e la lista resta corretta anche con dati asincroni.
Risposta breve: npx shadcn@latest add command installa un wrapper su cmdk; metti Command dentro CommandDialog, apri con ⌘K e assegna a ogni CommandItem un value univoco. Filtro e navigazione da tastiera li gestisce la libreria.
Le parti del menu
Il componente non è un menu a tendina: è una lista di comandi con un campo di ricerca. Ogni parte ha un ruolo preciso, e i ruoli ARIA li applica cmdk, non il tuo CSS.
| Parte | Ruolo |
|---|---|
| CommandDialog | Involucro modale: monta il Command dentro un Dialog e aggiunge titolo e descrizione per lo screen reader. |
| CommandInput | Campo di ricerca con ruolo combobox; riceve il focus quando il menu si apre. |
| CommandList | Contenitore con ruolo listbox, scorre se le voci superano l'altezza. |
| CommandItem | Una voce selezionabile; il testo o il value esplicito entrano nel filtro. |
| CommandEmpty | Messaggio mostrato quando nessuna voce corrisponde. |
| CommandGroup / CommandSeparator | Raggruppano le voci e separano blocchi di comandi. |
Aprire con ⌘K, senza sorprese
La scorciatoia si registra su document in un effetto. preventDefault() serve: senza, su Windows Ctrl+K e su macOS ⌘K attivano la ricerca del browser. La stessa gesture deve funzionare anche dentro un campo di testo, quindi non filtrare per event.target.
"use client";
import { useEffect, useState } from "react";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";
export function CommandMenu() {
const [open, setOpen] = useState(false);
useEffect(() => {
function onKeyDown(event: KeyboardEvent) {
if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
setOpen((value) => !value);
}
}
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
}, []);
return (
Nessun risultato.
setOpen(false)}>
Dashboard
G D
setOpen(false)}>
Impostazioni
setOpen(false)}>
Esporta CSV
);
}
Il filtro: dove si sbaglia
cmdk confronta ciò che scrivi con il value di ogni voce. Se value manca, usa il testo del nodo: due voci chiamate «Apri» producono due risultati identici e la selezione diventa ambigua. La correzione costa una riga: value descrittivo, testo visibile libero.
Quando le voci arrivano da una ricerca remota, il filtro locale combatte con i risultati del server: scrivi «re» e cmdk nasconde le voci che il server ha appena restituito. In quel caso passa shouldFilter={false} e gestisci tu lista, stato di caricamento e nessun risultato.
{loading && Caricamento…}
{!loading && results.length === 0 && (
Nessun utente trovato.
)}
{results.map((user) => (
pick(user)}>
{user.name}
))}
Accessibilità
Il CommandDialog di shadcn accetta title e description e li rende in un header nascosto, perché un Dialog Radix senza titolo rompe la navigazione assistita. Se costruisci il dialog a mano, aggiungi DialogTitle: il caso è descritto in DialogContent richiede DialogTitle. La lista invece è già a posto: frecce, Home, End e Invio funzionano senza codice.
Limite dichiarato
cmdk filtra in memoria, nel browser. Con qualche centinaio di voci è istantaneo; con decine di migliaia il costo si sposta sul thread principale e il filtro remoto non è più un'opzione. Il catalogo, misurato il 1 novembre 2026, contiene 19 blocchi command: tutti presuppongono una lista breve e locale.
Dove continuare
Nel catalogo trovi un blocco command menu, il record del componente command di Origin UI e la libreria shadcn. Per il montaggio leggi che cosa è shadcn/ui e come aggiungere un blocco shadcn; il confronto tra cataloghi è in 21st, shadcnspace e le altre.
Domande frequenti
Serve una libreria esterna per il Command?
Sì, cmdk: il componente shadcn è un wrapper con lo stile del design system. Installa con npx shadcn@latest add command e la dipendenza arriva con il file.
Perché alcune voci non compaiono nella ricerca?
Perché il filtro confronta il value, non solo il testo visibile. Se value è un id o una stringa diversa dalla label, cerca quella; oppure imposta value sulla label e tieni l'id in un campo separato.
Come apro il menu con ⌘K?
Aggiungi un listener keydown su document, controlla event.metaKey o event.ctrlKey con key === "k" e chiama preventDefault. Il listener va registrato dentro un effetto e rimosso al cleanup.
Posso filtrare i risultati sul server?
Sì. Passa shouldFilter={false}, porta query in uno stato controllato e gestisci tu lista, caricamento e stato vuoto. Il filtro locale altrimenti nasconde i risultati appena arrivati.
Il menu funziona con la tastiera?
Sì: cmdk gestisce frecce, Home, End, Invio ed Escape. Devi solo dare un titolo al Dialog, perché senza DialogTitle la navigazione assistita perde il contesto.