# 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.

> Pagina: https://ui.eneawork.it/blog/shadcn-command-menu
> Markdown: https://ui.eneawork.it/blog/shadcn-command-menu.md

*2026-11-01*

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.

Il catalogo EneUI misura 111.985 componenti, 1.556 blocchi, 12.808 icone da 1.472 librerie, letti da /api/stats il 1 novembre 2026. 19 blocchi su 1.556 hanno «command» nell'id, contati da data/blocks.json lo stesso giorno.

## 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.

## 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.

## FAQ

### 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.
