shadcn/ui'da metin girişi için dört bileşen birlikte çalışır: tek satırlık alan için Input, çok satırlı metin için Textarea, alanın adını göstermek için Label ve bir alanın içine ikon, metin ya da buton yerleştirmek için InputGroup. İlk ikisi doğrudan HTML input ve textarea öğelerini stillendirir, Label Radix'in Label ilkelini kullanır, InputGroup ise Ekim 2025'te eklenen ve hiçbir dış pakete ihtiyaç duymayan bir yerleşim bileşenidir.
Bu yazıda her bileşeni ayrı bir başlık altında, belge sayfalarındaki örnekler ve depodaki kaynak kodla anlatıyoruz: kurulum, temel kullanım, etiket bağlama, dosya ve devre dışı alanlar, hata durumu, InputGroupAddon hizalamaları, InputGroupButton boyutları ve özel bir kontrolü gruba katmak. Bu alanları form doğrulamasıyla birlikte kullanmak için Field bileşeni yazısına ve React Hook Form ile doğrulama yazısına bakabilirsiniz. Tüm seri: shadcn/ui öğrenme rehberi.
Input: tek satırlık alan

Input bileşeni CLI ile npx shadcn@latest add input komutuyla eklenir. Kaynağı tek bir fonksiyondur ve aldığı bütün özellikleri HTML input öğesine geçirir; yani type, placeholder, value, onChange, name, required gibi her şey bildiğiniz gibi çalışır. Bileşen yalnızca sınıflar ve data-slot="input" özniteliği ekler.
npx shadcn@latest add inputimport { Input } from "@/components/ui/input"
<Input type="email" placeholder="E-posta adresiniz" />Kaynaktaki sınıflar birkaç davranışı tanımlar. Alan h-9 w-full ile tam genişlikte ve 9 birim yüksekliğindedir. Yazı boyutu mobilde text-base, orta ve büyük ekranlarda md:text-sm olur. Odakta focus-visible:ring-[3px] ile halka çıkar. aria-invalid özniteliği verildiğinde çerçeve ve halka hata rengine döner. Devre dışı alanlarda disabled:cursor-not-allowed disabled:opacity-50 uygulanır. Dosya seçici için de file: önekli sınıflar vardır; böylece type="file" verdiğinizde dosya düğmesi de temaya uyar.
Bileşen type değerini olduğu gibi geçirdiği için password, number, search ya da tel gibi türler de aynı görünümle çalışır; shadcn/ui bunlar için ayrı bileşen sunmaz. Doğrulama kodu gibi her hanesi ayrı kutuda gösterilen bir giriş gerekiyorsa ayrı bir bileşen vardır: Input OTP.
Input sayfasının değişiklik günlüğünde 18 Eylül 2025 tarihli bir not bulunur: input.tsx içindeki flex sınıfı artık gerekli değildir ve kaldırılmalıdır. Bu tarihten önce eklediğiniz bir dosyanız varsa bu küçük düzeltmeyi elle yapabilirsiniz.
Label: alanı etiketlemek
Label, npx shadcn@latest add label ile eklenir; elle kurulumda @radix-ui/react-label paketi gerekir. Belgedeki tanımı, kontrollerle ilişkilendirilmiş erişilebilir bir etiket üretmesidir. İlişkiyi kuran şey htmlFor özelliğidir: değeri, etiketlenen alanın id değeriyle aynı olmalıdır. Bu sayede ekran okuyucular alanın adını okur, etikete tıklamak da alanı odaklar.
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export function EpostaAlani() {
return (
<div className="grid w-full max-w-sm items-center gap-3">
<Label htmlFor="email">E-posta</Label>
<Input type="email" id="email" placeholder="E-posta" />
</div>
)
}Kaynakta iki küçük ama kullanışlı kural var: peer-disabled:opacity-50 ve group-data-[disabled=true]:opacity-50. İlki, etiketten önce gelen ve peer sınıfı taşıyan bir kontrol (örneğin Checkbox) devre dışıysa etiketi soluklaştırır. İkincisi, etiketi saran bir öğede data-disabled="true" varsa aynı etkiyi verir. Bu yüzden devre dışı alanların etiketleri için ayrıca sınıf yazmanız gerekmez. Etiketlemenin erişilebilirlikteki yeri için WCAG temelleri yazısına bakabilirsiniz.
Dosya, devre dışı ve buton yanında Input
Belgedeki örnekler üç yaygın durumu gösterir. Dosya alanı, type="file" verilmiş sıradan bir Input'tur ve yanında bir Label bulunur. Devre dışı alan için disabled özelliği yeterlidir. Butonlu alan ise bir flex kapsayıcının içinde Input ve Button yan yana konarak kurulur:
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export function Ornekler() {
return (
<div className="grid gap-6">
<div className="grid w-full max-w-sm items-center gap-3">
<Label htmlFor="picture">Fotoğraf</Label>
<Input id="picture" type="file" />
</div>
<Input disabled type="email" placeholder="E-posta" />
<div className="flex w-full max-w-sm items-center gap-2">
<Input type="email" placeholder="E-posta" />
<Button type="submit" variant="outline">
Abone ol
</Button>
</div>
</div>
)
}Alanla butonu tek bir çerçevede birleştirmek isterseniz iki seçeneğiniz var: alanı ve butonu bir ButtonGroup içine koymak ya da aşağıda anlatılan InputGroup ile butonu alanın içine almak.
Textarea: çok satırlı metin
Textarea, npx shadcn@latest add textarea ile eklenir ve Input ile aynı mantıkla HTML textarea öğesini sarar. Odak, hata ve devre dışı sınıfları Input ile aynıdır. Farklı olan iki sınıf dikkat çekicidir: min-h-16 en az yüksekliği belirler, field-sizing-content ise alanın yüksekliğinin içeriğe göre ayarlanmasını sağlayan CSS özelliğini açar. Bu CSS özelliğinin tarayıcı desteğini kendi hedef tarayıcılarınız için kontrol etmeniz iyi olur; desteklenmeyen yerde alan sabit yükseklikte davranır.
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
export function MesajAlani() {
return (
<div className="grid w-full gap-3">
<Label htmlFor="message-2">Mesajınız</Label>
<Textarea placeholder="Mesajınızı buraya yazın." id="message-2" />
<p className="text-muted-foreground text-sm">
Mesajınız destek ekibine iletilecek.
</p>
</div>
)
}Belgedeki diğer örnekler aynı yapıyı tekrarlar: devre dışı textarea, etiketli textarea ve altına tam genişlikte bir gönder butonu konmuş textarea. Açıklama metni için kullanılan text-muted-foreground sınıfı temanın soluk metin rengidir; açıklama ve hata metinlerini düzenli biçimde göstermek için FieldDescription ve FieldError bileşenleri de vardır.
InputGroup: alanın içine ikon, metin ve buton

InputGroup, değişiklik günlüğünün tarifiyle "input'larınızın etrafına her zaman ihtiyaç duyduğunuz küçük parçaları" eklemenizi sağlar: arama ikonu, para birimi simgesi, kopyala butonu, karakter sayacı gibi. CLI ile npx shadcn@latest add input-group komutu kullanılır. Bileşen ailesi altı parçadan oluşur:
| Bileşen | Görevi |
|---|---|
| InputGroup | Kapsayıcı; çerçeveyi, odak halkasını ve hata rengini taşır |
| InputGroupInput | Grup içinde Input yerine kullanılır, kendi çerçevesi yoktur |
| InputGroupTextarea | Grup içinde Textarea yerine kullanılır |
| InputGroupAddon | İkon, metin veya butonları alanın bir kenarına yerleştirir |
| InputGroupButton | Grup içine uygun küçük buton |
| InputGroupText | Soluk renkli düz metin (ör. para birimi, alan adı uzantısı) |
import { SearchIcon } from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
export function AramaKutusu() {
return (
<InputGroup>
<InputGroupInput placeholder="Ara..." />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupButton>Ara</InputGroupButton>
</InputGroupAddon>
</InputGroup>
)
}Bu örnekte ilginç bir ayrıntı var: arama ikonu solda görünse de kodda input'tan sonra yazılmıştır. Belgedeki "Focus Navigation" notu bunu açıkça ister: doğru odak sırası için InputGroupAddon her zaman input'tan sonra yerleştirilmeli, görsel konum align özelliğiyle belirlenmelidir. Kaynakta inline-start hizası order-first, inline-end hizası order-last sınıfını uygular; yani görsel sıralama CSS ile yapılır, DOM sırası değişmez.
align değerleri ve textarea ile kullanım
| align | Konum | Hangi kontrolle |
|---|---|---|
| inline-start (varsayılan) | Satırın başı (solda) | InputGroupInput |
| inline-end | Satırın sonu (sağda) | InputGroupInput |
| block-start | Alanın üstü, tam genişlik | InputGroupTextarea |
| block-end | Alanın altı, tam genişlik | InputGroupTextarea |
Belge kuralı kalın harflerle yazar: InputGroupInput için inline-start ya da inline-end, InputGroupTextarea için block-start ya da block-end kullanın. Aşağıdaki örnek, belgedeki "Text" örneğinden uyarlanmıştır ve bir textarea'nın altına kalan karakter sayısını yazar:
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@/components/ui/input-group"
export function MetinliGruplar() {
return (
<div className="grid w-full max-w-sm gap-6">
<InputGroup>
<InputGroupAddon>
<InputGroupText>₺</InputGroupText>
</InputGroupAddon>
<InputGroupInput placeholder="0,00" />
<InputGroupAddon align="inline-end">
<InputGroupText>TRY</InputGroupText>
</InputGroupAddon>
</InputGroup>
<InputGroup>
<InputGroupTextarea placeholder="Mesajınızı yazın" />
<InputGroupAddon align="block-end">
<InputGroupText className="text-muted-foreground text-xs">
120 karakter kaldı
</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>
)
}İlk grupta InputGroupAddon input'tan önce yazılmış; belgedeki "Text" örneği de böyledir ve order sınıfları sayesinde görünüm değişmez. Ancak odak sırası notunu dikkate alarak kendi kodunuzda addon'ları input'tan sonra yazmanız daha güvenlidir. Bir addon'a tıklandığında, tıklanan yer bir buton değilse kaynak kod gruptaki input öğesini odaklar; bu yüzden ikonun üstüne tıklamak da alanı aktif hâle getirir.
Mini örnek: bülten kayıt kutusu
Şimdi parçaları bir araya getirelim: görünür bir etiket, solda zarf ikonu, sağda abone ol butonu ve altında kısa bir açıklama. Etiket Label ile yazılır ve htmlFor değeri InputGroupInput'un id'sine bağlanır. İkon ve buton, odak sırası notuna uygun olarak input'tan sonra gelir; görsel konumlarını align belirler. Buton bu kez formu gerçekten göndermesi gerektiği için type="submit" alır, çünkü InputGroupButton varsayılan olarak type="button"'dır.
import { MailIcon } from "lucide-react"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group"
import { Label } from "@/components/ui/label"
export function BultenKutusu() {
return (
<form className="grid w-full max-w-sm gap-3">
<Label htmlFor="bulten-eposta">E-posta adresiniz</Label>
<InputGroup>
<InputGroupInput id="bulten-eposta" type="email" placeholder="ad@ornek.com" required />
<InputGroupAddon>
<MailIcon />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<InputGroupButton type="submit" variant="default">
Abone ol
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
<p className="text-muted-foreground text-sm">İstediğiniz zaman abonelikten çıkabilirsiniz.</p>
</form>
)
}Butona variant="default" verdik, çünkü grup butonunun varsayılanı ghost'tur ve ana işlem için dolu bir görünüm daha belirgindir; belgedeki textarea örneğindeki "Run" butonu da aynı şekilde variant="default" kullanır. Grubun içindeki input odaklandığında halka bütün grubun çerçevesinde görünür; required gibi doğrulama öznitelikleri de doğrudan input'a geçer. Addon kaynağında ayrıca has-[>kbd] kuralları bulunur: addon'un içine bir klavye kısayolu (Kbd) koyarsanız kenar boşlukları ona göre ayarlanır. Kısayol bileşeni Item, Empty, Kbd ve Avatar yazısında anlatılıyor.
Özel bir kontrolü gruba katmak
Grubun odak halkası ve hata rengi, içindeki kontrolün data-slot="input-group-control" özniteliği taşımasına bağlıdır. Kaynakta odak kuralı has-[[data-slot=input-group-control]:focus-visible] seçicisiyle yazılmıştır: bu özniteliği taşıyan bir öğe odaklandığında halka grubun çerçevesinde görünür. InputGroupInput ve InputGroupTextarea bu özniteliği kendileri ekler. Başka bir kütüphanenin alanını kullanmak isterseniz belge, özniteliği o alana elle vermenizi söyler ve bu alana hiçbir stilin otomatik uygulanmadığını hatırlatır. Belgedeki örnek react-textarea-autosize paketiyle yapılmıştır:
import TextareaAutosize from "react-textarea-autosize"
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
export function OtomatikBoyutluAlan() {
return (
<InputGroup>
<TextareaAutosize
data-slot="input-group-control"
className="dark:bg-input/30 flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none"
placeholder="Kendiliğinden büyüyen alan..."
/>
<InputGroupAddon align="block-end">Not</InputGroupAddon>
</InputGroup>
)
}Hata durumu da aynı yoldan çalışır: gruptaki herhangi bir data-slot taşıyan öğede aria-invalid="true" varsa grup çerçevesi hata rengine geçer. InputGroup sayfasının değişiklik günlüğünde de 6 Ekim 2025 tarihli bir not var: InputGroup bileşenine min-w-0 sınıfı eklenmeli. Güncel kaynakta bu sınıf zaten bulunur.
Sık yapılan hatalar
- htmlFor ile id'yi eşleştirmemek.
Labeldeğerini alanınid'si ile aynı yazmazsanız etiket alana bağlanmaz. - InputGroup içinde düz Input kullanmak.
Inputkendi çerçevesini ve halkasını taşır; grup içinde çift çerçeve oluşur. Grup içindeInputGroupInputkullanın. - Addon'u input'tan önce yazmak. Belge doğru odak sırası için addon'u input'tan sonra koymayı ve konumu
alignile vermeyi ister. - Textarea ile inline hizalama kullanmak.
InputGroupTextareaiçinblock-startveyablock-endkullanılmalıdır. - Özel kontrolde data-slot'u unutmak. Öznitelik olmadan grup, odak ve hata durumlarını göremez.
- Hata durumunu yalnızca renkle göstermek.
aria-invalidçerçeveyi kırmızı yapar ama hatanın ne olduğunu söylemez; yanına okunabilir bir hata metni koyun. Bunun için Field bileşenindeki FieldError kullanılabilir.
Sık Sorulan Sorular
Input için ayrı bir npm paketi gerekiyor mu?
Hayır. Input, Textarea ve InputGroup yalnızca proje içindeki cn() yardımcısını kullanır; belgelerde bunlar için elle kurulacak bir paket listelenmez. Label ise @radix-ui/react-label ister.
InputGroup ile ButtonGroup arasındaki fark nedir?
InputGroup, öğeleri tek bir alanın çerçevesinin içine yerleştirir. ButtonGroup ise ayrı bileşenleri (input, buton, select) yan yana birleşik bir şerit hâline getirir. Belgelerde ikisinin birlikte kullanıldığı örnekler de var.
Hata durumunu nasıl gösteririm?
Alana aria-invalid özniteliğini verin; Input, Textarea ve InputGroup kaynakları bu özniteliğe göre hata rengini uygular. Form kütüphaneleriyle bu değeri doğrulama sonucuna bağlamak React Hook Form yazısında anlatılıyor.
Input yüksekliğini ya da köşelerini değiştirebilir miyim?
Evet. Tek bir alan için className="h-10 rounded-full" gibi sınıflar verirsiniz; cn() içindeki tailwind-merge, kaynaktaki h-9 ve rounded-md sınıflarını sizinkilerle değiştirir. Belgedeki InputGroup örneklerinden biri ise köşe yuvarlaklığını tek tek sınıf yazmak yerine gruba [--radius:9999px] vererek değiştirir; çünkü addon ve buton köşeleri --radius değişkeninden hesaplanır. Bütün projede değişiklik istiyorsanız input.tsx dosyasını düzenleyin.
Kontrollü bir Input nasıl yazılır?
Tıpkı HTML input gibi: value ve onChange verirsiniz, çünkü bileşen tüm özellikleri öğeye geçirir. React'te kontrollü alan kavramı için React yazısına bakabilirsiniz.
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 · Field ile form düzeni · Button Group · Checkbox, Radio, Switch, Slider · Input OTP
