shadcn/ui'daki Command bileşeni, üstte bir arama kutusu ve altında gruplanmış seçeneklerden oluşan hızlı bir komut menüsüdür. Kullanıcı yazdıkça liste daralır, hiçbir sonuç kalmazsa "sonuç bulunamadı" mesajı görünür. Aynı bileşen bir sayfaya gömülü liste olarak, CommandDialog ile Ctrl+K ya da Cmd+K kısayoluyla açılan bir komut paleti olarak ya da bir Popover içinde aranabilir seçim kutusu olarak kullanılabilir. Kurulum için npx shadcn@latest add command komutu yeterlidir; bileşen cmdk kütüphanesine dayanır.

Bu yazıda Command'ın parçalarını, depodaki kaynak kodun varsayılanlarını, gruplar ve ayırıcılarla liste kurmayı, CommandDialog ile klavye kısayollu bir palet yapmayı ve seçim olaylarını ele alıyoruz. Aranabilir seçim kutusunun tamamı Combobox yazısının konusu; serinin bütünü için shadcn/ui öğrenme rehberi sayfasına bakabilirsiniz.

Command neye dayanır?

shadcn/ui: Command sayfası bileşeni hızlı, birleştirilebilir ve stilsiz bir React komut menüsü olarak tanımlar ve <Command /> bileşeninin pacocoursey'nin geliştirdiği cmdk bileşenini kullandığını yazar. "Stilsiz" ifadesi cmdk'nin kendisi içindir: cmdk davranışı, yani arama, süzme ve klavye gezinmesini sağlar; shadcn/ui ise bu davranışın üstüne tema renklerini, boşlukları ve simge düzenini ekler. Arama ve klavye davranışının ayrıntıları cmdk belgelerindedir.

bash
npx shadcn@latest add command
npm install cmdk

İlk satır CLI ile kurulumdur; ikinci satır elle kurulumda gereken bağımlılıktır. Depodaki kayıt tanımına göre command bileşeninin kayıt bağımlılığı dialog'dur, yani CLI ile kurduğunuzda components/ui/dialog.tsx dosyası da projenize gelir. Bunun nedeni CommandDialog parçasının Dialog bileşeni üzerine kurulmasıdır. Elle kurulumda Dialog bileşenini ayrıca eklemeyi unutmayın; Dialog'un kendisi Dialog ve Alert Dialog yazısında anlatılıyor.

Parçalar

Parçalar: Command, CommandInput, CommandList, CommandEmpty
ParçaGörevi
CommandKök bileşen; cmdk kökünü sarar
CommandInputArama kutusu; solunda arama simgesi bulunur
CommandListSonuç listesi; en fazla 300px yüksekliğinde, taşarsa kayar
CommandEmptyEşleşen sonuç yokken gösterilen içerik
CommandGroupBaşlıklı öğe grubu; heading prop'u alır
CommandItemSeçilebilir öğe; value, onSelect ve disabled alabilir
CommandSeparatorGruplar arasında ince çizgi
CommandShortcutÖğenin sağında kısayol metni
CommandDialogCommand'ı bir Dialog içinde açan hazır parça

Kaynak koda göre kök Command bileşeni bg-popover zeminli, dikey yerleşimli ve köşeleri yuvarlatılmış bir kutudur. CommandInput kendisini data-slot="command-input-wrapper" özniteliği taşıyan, alt kenarlıklı bir kutuya yerleştirir ve önüne yarı saydam bir arama simgesi koyar. CommandEmpty mesajı ortalanmış ve dikeyde boşluklu yazılır. CommandGroup başlıkları cmdk'nin cmdk-group-heading özniteliği üzerinden küçük ve soluk renkte biçimlenir.

CommandItem iki durumu cmdk'nin yazdığı özniteliklerle gösterir: klavyeyle ya da fareyle üzerinde durulan öğe data-selected=true olur ve accent rengiyle vurgulanır; disabled verilmiş öğe data-disabled=true olur, yarı saydam görünür ve tıklamaları almaz. Öğe içindeki simgeler, menü bileşenlerinde olduğu gibi kendiliğinden size-4 boyutuna ve soluk renge getirilir. CommandShortcut ise yalnızca bir span öğesidir ve kısayolu göstermekle yetinir.

Temel kullanım: sayfaya gömülü komut listesi

Belgedeki kullanım örneği iki gruplu bir listedir. CommandEmpty ve gruplar CommandList içinde yer alır; gruplar arasına CommandSeparator konur.

typescript
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"

export function KomutListesi() {
  return (
    <Command className="rounded-lg border shadow-md md:min-w-[450px]">
      <CommandInput placeholder="Bir komut yazın ya da arayın..." />
      <CommandList>
        <CommandEmpty>Sonuç bulunamadı.</CommandEmpty>
        <CommandGroup heading="Öneriler">
          <CommandItem>Takvim</CommandItem>
          <CommandItem>Emoji ara</CommandItem>
          <CommandItem disabled>Hesap makinesi</CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Ayarlar">
          <CommandItem>
            Profil
            <CommandShortcut>⌘P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            Faturalar
            <CommandShortcut>⌘B</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

Bu kod kenarlıklı bir kutu çizer: üstte arama kutusu, altında "Öneriler" ve "Ayarlar" başlıklı iki grup. Belgedeki demo dosyası da hesap makinesi öğesini disabled ile devre dışı gösterir ve ayarlar grubundaki öğelerin sağına kısayol metinleri koyar. Kullanıcı arama kutusuna yazdıkça cmdk eşleşmeyen öğeleri gizler; hiçbir öğe kalmazsa "Sonuç bulunamadı." mesajı görünür. Kök bileşene verdiğiniz className, kaynak koddaki varsayılan sınıflarla cn() üzerinden birleşir.

Simgeli öğeler

Belgedeki dialog örneği öğelere lucide-react simgeleri ekler. Simge metinden önce gelir, metin de bir span içine alınır:

typescript
import { Calendar, Settings, Smile, User } from "lucide-react"

<CommandGroup heading="Öneriler">
  <CommandItem>
    <Calendar />
    <span>Takvim</span>
  </CommandItem>
  <CommandItem>
    <Smile />
    <span>Emoji ara</span>
  </CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Ayarlar">
  <CommandItem>
    <User />
    <span>Profil</span>
  </CommandItem>
  <CommandItem>
    <Settings />
    <span>Ayarlar</span>
  </CommandItem>
</CommandGroup>

CommandDialog ile Ctrl+K komut paleti

CommandDialog ile Ctrl+K komut paleti: Kısayolu kullanıcıya göstermek, Komutları bir diziden üretmek, CommandDialog'un kaynak kodu

Belgeye göre komut menüsünü bir pencerede göstermek için <CommandDialog /> bileşeni kullanılır. Açık durumu bir React durumunda tutulur ve open ile onOpenChange prop'larıyla pencereye verilir. Klavye kısayolu için belgedeki örnek, belgenin tamamına bir keydown dinleyicisi ekler: basılan tuş k ise ve aynı anda Mac'te Cmd (metaKey) ya da diğer sistemlerde Ctrl (ctrlKey) basılıysa, tarayıcının varsayılan davranışı engellenir ve pencere açıksa kapanır, kapalıysa açılır.

typescript
"use client"

import * as React from "react"

import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"

export function CommandMenu() {
  const [open, setOpen] = React.useState(false)

  React.useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    document.addEventListener("keydown", down)
    return () => document.removeEventListener("keydown", down)
  }, [])

  return (
    <CommandDialog open={open} onOpenChange={setOpen}>
      <CommandInput placeholder="Bir komut yazın ya da arayın..." />
      <CommandList>
        <CommandEmpty>Sonuç bulunamadı.</CommandEmpty>
        <CommandGroup heading="Öneriler">
          <CommandItem>Takvim</CommandItem>
          <CommandItem>Emoji ara</CommandItem>
          <CommandItem>Hesap makinesi</CommandItem>
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  )
}

Bu kalıbın üç ayrıntısı önemlidir. Birincisi, dinleyici useEffect içinde eklenir ve temizleme fonksiyonunda kaldırılır; böylece bileşen sayfadan kalktığında dinleyici bellekte kalmaz. İkincisi, e.preventDefault() çağrısı tarayıcının aynı tuş birleşimine bağlı kendi davranışını engellemek içindir. Üçüncüsü, setOpen((open) => !open) yazımı önceki değeri fonksiyon parametresi olarak alır; böylece efekt yalnızca bir kez kurulsa bile her basışta güncel değer tersine çevrilir. Bileşen durum ve efekt kullandığı için dosyanın başında "use client" bulunur.

Depodaki command-dialog demo dosyası aynı kalıbı k yerine j tuşuyla kurar ve pencerenin yanında kullanıcıya hangi tuşa basacağını anlatan küçük bir tuş etiketi gösterir. Hangi harfi seçeceğiniz size kalmıştır; ancak tarayıcıların ve işletim sistemlerinin kendi kısayollarıyla çakışmamasına dikkat edin. Tuş etiketini göstermek için Ekim 2025'te eklenen Kbd bileşenini kullanabilirsiniz; ayrıntılar Item, Empty, Kbd ve Avatar yazısında.

Kısayolu kullanıcıya göstermek

Klavye kısayolu ne kadar kullanışlı olursa olsun, kullanıcı onu bilmiyorsa işe yaramaz. Demo dosyası pencerenin yanına "Press ⌘J" biçiminde küçük bir metin koyar. Aynı göstergeyi Kbd bileşeniyle kurmak için tuşları bir KbdGroup içine alabilirsiniz; Kbd sayfasındaki örnek tuşları bu şekilde gruplar.

typescript
import { Kbd, KbdGroup } from "@/components/ui/kbd"

<p className="text-muted-foreground text-sm">
  Komut paletini açmak için{" "}
  <KbdGroup>
    <Kbd>Ctrl</Kbd>
    <span>+</span>
    <Kbd>K</Kbd>
  </KbdGroup>{" "}
  tuşlarına basın.
</p>

Bu satırı, palet açılmadan önce görünen bir arama düğmesinin içine ya da sayfanın üst çubuğuna koyabilirsiniz. Kullanıcı göstergeye tıklayarak da paleti açabilsin istiyorsanız aynı setOpen(true) çağrısını bir düğmenin onClick olayına bağlayın; böylece klavye kullanmayan kullanıcılar da palete ulaşır.

Komutları bir diziden üretmek

Gerçek bir uygulamada komutlar genellikle bir diziden gelir: sayfa listesi, son açılan dosyalar ya da kullanıcı eylemleri. Combobox örneği de öğeleri bir dizi üzerinde map ile üretir ve her öğeye key ile value verir. Aynı yaklaşımı gruplu bir palete uygulayabilirsiniz:

typescript
const sayfalar = [
  { value: "panel", label: "Kontrol paneli" },
  { value: "siparisler", label: "Siparişler" },
  { value: "musteriler", label: "Müşteriler" },
]

<CommandGroup heading="Sayfalar">
  {sayfalar.map((sayfa) => (
    <CommandItem
      key={sayfa.value}
      value={sayfa.value}
      onSelect={() => setOpen(false)}
    >
      {sayfa.label}
    </CommandItem>
  ))}
</CommandGroup>

Burada key React'in liste öğelerini ayırt etmesi için, value ise cmdk'nin öğeyi tanıması ve onSelect geri çağrısına iletmesi içindir. Değerleri benzersiz tutmanız, aynı metne sahip iki öğenin birbirine karışmasını önler. Liste çok uzarsa CommandList bileşeninin max-h-[300px] sınırı devreye girer ve liste kendi içinde kayar; daha uzun bir alan istiyorsanız bu sınıfı className ile ezebilirsiniz.

CommandDialog'un kaynak kodu

CommandDialog bileşeni üç ek prop alır ve varsayılanları kaynakta tanımlıdır:

PropVarsayılanGörevi
title"Command Palette"Pencere başlığı; ekranda görünmez, erişilebilir ad olarak kullanılır
description"Search for a command to run..."Pencere açıklaması; ekranda görünmez
showCloseButtontrueDialog'daki köşe kapatma düğmesini gösterir
classNameyokİçerik kutusuna eklenen sınıflar

Bileşen, Dialog'un DialogHeader parçasını sr-only sınıfıyla görsel olarak gizler ve title ile description değerlerini bu başlığın içine yazar. Böylece pencere ekranda yalnızca arama kutusu ve listeden oluşur, ama ekran okuyucular pencerenin ne olduğunu yine öğrenir. Türkçe bir arayüzde bu iki değeri title="Komut paleti" ve description="Çalıştırılacak bir komut arayın" gibi kendi metinlerinizle vermeniz iyi olur; aksi halde İngilizce varsayılanlar kullanılır. İçerik kutusu overflow-hidden p-0 sınıflarını alır ve içindeki Command kökü, giriş kutusunu ve öğeleri pencere için biraz daha büyük gösteren ek sınıflar taşır.

Bu ek sınıflar, Command'ı kendi ihtiyacınıza göre nasıl özelleştirebileceğinizi de gösterir. Pencere sürümünde giriş kutusunun kabı **:data-[slot=command-input-wrapper]:h-12 sınıfıyla 3rem yüksekliğe çıkarılır, öğelere [&_[cmdk-item]]:py-3 ile daha fazla dikey boşluk verilir ve öğe simgeleri h-5 w-5 boyutuna büyütülür. Yani üst bileşen, alt parçalara data-slot ve cmdk öznitelikleri üzerinden ulaşır. Sayfaya gömülü bir Command'ı daha ferah göstermek isterseniz aynı seçicileri kök bileşenin className değerine yazabilirsiniz; bileşen dosyasını değiştirmeniz gerekmez. Bu seçicilerin Tailwind tarafı Tailwind CSS v4 yenilikleri yazısında, data-slot yaklaşımı ise cn() fonksiyonu ve data-slot yazısında ele alınıyor.

Seçim olayları: onSelect ve value

Bir öğe seçildiğinde, yani tıklandığında ya da üzerindeyken Enter tuşuna basıldığında, cmdk öğenin onSelect olayını çağırır. Belgedeki combobox örneğinde CommandItem bileşenine value verilir ve onSelect geri çağrısı bu değeri currentValue parametresiyle alır. Komut paletinde ise genellikle seçimden sonra bir işlem yapıp pencereyi kapatmak istersiniz:

typescript
<CommandItem
  value="ayarlar"
  onSelect={(currentValue) => {
    console.log(currentValue)
    setOpen(false)
  }}
>
  <Settings />
  <span>Ayarlar</span>
</CommandItem>

Bu parça önceki CommandMenu bileşeninin içinde kullanılmak üzere yazılmıştır: setOpen oradaki durum fonksiyonudur. console.log satırı yerine kendi işleminizi, örneğin bir sayfaya yönlendirmeyi ya da bir ayarı değiştirmeyi koyarsınız. Bir öğeyi kalıcı olarak seçili göstermek, yani seçilen değeri saklayıp tetikleyicide göstermek combobox kalıbının işidir; o kalıp Combobox yazısında adım adım anlatılıyor.

Command'ı menü ve popover içinde kullanmak

Command yalnızca sayfaya ya da pencereye gömülmez; başka bir katmanın içinde de çalışır. Combobox sayfasındaki "Dropdown menu" örneği, bir Dropdown Menu'nün "Apply label" alt menüsünün içine bir Command koyar. Alt menü içeriği p-0 ile boşluksuz yapılır, arama kutusuna autoFocus verilir ve öğe seçildiğinde onSelect içinde hem etiket kaydedilir hem de üst menü setOpen(false) ile kapatılır. Bu örnek, kontrollü kullanımın burada da işe yaradığını gösterir: menünün açık durumu bir değişkende tutulduğu için seçim bittiğinde bütün menü tek çağrıyla kapanır. Böyle iç içe kullanımlarda CommandDialog değil, düz Command kullanılır; çünkü dış katman zaten bir menü ya da popover sağlar.

Command, Combobox ve Dropdown Menu farkı

Command, Combobox ve Dropdown Menu farkı: Command (gömülü), CommandDialog, Combobox (Popover + Command), Dropdown Menu
BileşenAramaTipik kullanım
Command (gömülü)VarSayfa içinde aranabilir eylem listesi
CommandDialogVarKlavye kısayoluyla açılan komut paleti
Combobox (Popover + Command)VarUzun listeden form değeri seçmek
Dropdown MenuYokBir düğmenin altında birkaç eylem

Command'ı ayıran özellik arama kutusudur. Eylem sayısı azsa ve kullanıcının aramasına gerek yoksa Dropdown Menu daha sade bir seçimdir. Form içinde kısa bir listeden değer seçtirecekseniz Select yeterlidir; liste uzunsa ve aranabilir olmalıysa Combobox kalıbına geçersiniz.

Sık yapılan hatalar

  • CommandEmpty'yi listenin dışına koymak: belgedeki bütün örneklerde CommandEmpty ve gruplar CommandList içinde yer alır; bu yapıyı koruyun.
  • Dialog bileşenini eksik bırakmak: CommandDialog, Dialog'a dayanır. CLI bunu kayıt bağımlılığı olarak getirir, elle kurulumda ise dialog.tsx dosyasını ayrıca eklemelisiniz.
  • Klavye dinleyicisini temizlememek: useEffect içinde eklenen keydown dinleyicisini dönüş fonksiyonunda kaldırın; belge örneği bunu yapar.
  • Ctrl tuşunu unutmak: yalnızca metaKey kontrol edilirse kısayol yalnızca Mac'te çalışır. Örnekteki gibi e.metaKey || e.ctrlKey yazın.
  • CommandShortcut'ın kısayolu çalıştırdığını sanmak: parça yalnızca metin gösterir; kısayolu ayrıca dinlemelisiniz.
  • Aynı value değerini birden fazla öğeye vermek: cmdk öğeleri bu değerle tanır ve onSelect geri çağrısına iletir; dizi ile ürettiğiniz öğelerde değerleri benzersiz tutun.
  • İç içe katmanda CommandDialog kullanmak: Popover ya da Dropdown Menu içinde düz Command yeterlidir; ikinci bir pencere açmanıza gerek yoktur.
  • İngilizce varsayılan başlıkları bırakmak: CommandDialog başlığı ve açıklaması ekranda görünmese de ekran okuyucular tarafından okunur; Türkçe arayüzde kendi metinlerinizi verin. Erişilebilirlik kavramları için ARIA rehberine bakın.

Sık Sorulan Sorular

Command bileşeni aramayı kendisi mi yapıyor?

Evet, arama ve süzme cmdk tarafından yapılır; siz yalnızca öğeleri listelersiniz. Eşleşen öğe kalmadığında CommandEmpty içeriği gösterilir. Süzme davranışının ayrıntıları ve özelleştirme seçenekleri cmdk belgelerinde anlatılır.

Komut paleti için hangi tuşu kullanmalıyım?

Belgedeki kullanım örneği Ctrl/Cmd+K, depodaki demo ise Ctrl/Cmd+J kullanır. Kod yapısı aynıdır; yalnızca e.key karşılaştırmasındaki harf değişir.

Öğeye tıklanınca pencereyi nasıl kapatırım?

CommandDialog açık durumunu sizin tuttuğunuz open değişkeninden alır. Öğenin onSelect olayında işinizi yaptıktan sonra setOpen(false) çağırmanız yeterlidir.

CommandDialog'daki çarpı düğmesini kaldırabilir miyim?

Kaynak koda göre CommandDialog bileşeni showCloseButton prop'unu alır ve Dialog içeriğine aktarır. showCloseButton={false} verdiğinizde köşedeki düğme çizilmez; pencere Dialog'un kendi kapanma yollarıyla kapanır.

Command'ı Popover içinde nasıl kullanırım?

Belge bu kullanımı combobox olarak adlandırır ve ayrı bir sayfada anlatır: Popover içine Command konur, içerik kutusu p-0 ile boşluksuz yapılır. Ayrıntılar Combobox yazısında.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: shadcn/ui öğrenme rehberi · Combobox ile aranabilir seçim · Dialog ve Alert Dialog · Dropdown Menu, Context Menu ve Menubar · Item, Empty, Kbd ve Avatar · Popover, Tooltip ve Hover Card