Blog

UI SDK ve WebMCP ile React uygulamanızı AI ajanlarının kullanabileceği hâle getirin

Vercel’in UI SDK’sıyla AI ajanları React arayüzünüzü okuyup kullansın: createAgent, snapshot, act, allow, source agent, definePrimitive ve WebMCP registerTools.

Preet Suthar · · 10 dk okuma

  • AI
  • React
  • UI SDK
  • WebMCP

Çoğu AI ajanı bir web uygulamasını, yavaş bir ekran okuyucu kullanan bir insan gibi kullanır. Ekran görüntüsü alır ya da DOM’u okur, hangi öğenin "Region" açılır menüsü olduğunu tahmin eder, ona tıklar, sayfayı yeniden okur ve bir seçeneğe tıklar. Her gidiş dönüş token ve zaman harcar ve her tahmin yanlış olabilir.

Vercel Labs’in 12 Kasım 2026’da yayınladığı UI SDK başka bir yol izliyor. Bileşenleri; bir ajana sayfada ne olduğunu, her bileşenin hangi durumda olduğunu ve neler yapabileceğini söyleyen bir köprüye kaydolur, sonra bu eylemleri bileşenin kendi API’si üzerinden çalıştırır. Aynı köprü araçlarını WebMCP aracılığıyla tarayıcıya yerleşik bir ajana da yayınlayabilir.

Bu yazı React’te küçük bir ayarlar sayfası kuruyor, ona bir ajan bağlıyor ve API’nin önemli kısımlarını ele alıyor: createAgent, snapshot(), act(), allow callback’i, change callback’lerinde source: "agent", definePrimitive ile yapılmış özel bir bileşen ve registerTools. Aşağıdaki her çıktı, React 19.3, Vite 8 ve 0.1.0-beta.1 sürümündeki @ui-sdk/react ile @ui-sdk/core kullanan bir deneme uygulamasındaki çalıştırmadan kopyalandı. UI SDK beta aşamasında, bu yüzden adlar ve davranış değişebilir.

Köprü nasıl çalışır

React uygulamanız

  • Input
  • Select
  • Switch
  • definePrimitive parçalarınız

Köprü: createAgent()

  • snapshot() bileşenleri, durumu ve eylemleri listeler
  • query() bileşenleri ve seçeneklerini bulur
  • act() adımları her bileşenin API’si üzerinden çalıştırır
  • allow her adımı onaylar ya da reddeder

Kendi ajanınız

Model SDK’nıza bağlanan, agentTools’tan gelen araç tanımları

Tarayıcının ajanı

registerTools() araçları ikinci bir allow ile document.modelContext (WebMCP) üzerinde yayınlar

Bir ajanın yaptığı her değişiklik, change callback’lerinize details.source === "agent" ile ulaşır ve details.cancel() onu yine reddedebilir.

Bir ajan bileşenlerinize UI SDK üzerinden nasıl ulaşır

Köprü, createAgent()’ın döndürdüğü bir nesnedir. Sayfadaki UI SDK bileşenleri mount edilirken ona kaydolur. Bir ajanın kullandığı üç metodu vardır:

  • snapshot(), kayıtlı her bileşeni c3 gibi kısa bir ref, türü, erişilebilir etiketi, durumu ve şu anda kabul ettiği eylemlerle döndürür.
  • query() bileşenleri metne göre bulur ya da birinin içindeki seçenekleri listeler, örneğin bir Select’in seçeneklerini.
  • act() bir adım listesini sırayla çalıştırır. Her adım bir bileşeni ve setValue gibi bir eylemi adlandırır, köprü de o eylemi bileşen üzerinde çağırır.

agentTools, bu üç metodu kendi model döngünüz için JSON Schema’lı araç tanımları olarak tanımlar. registerTools() ise onları tarayıcıya kaydeder.

Önemli tasarım tercihi, act()’in tıklamaları simüle etmemesi. Bir controller’ın çağıracağı API’nin aynısını çağırır, böylece değişiklik bileşenin change callback’inden "agent" değerinde bir source ile geçer. Uygulamanız ajan değişikliklerini bir insanınkileri gördüğü yerde görür ve orada reddedebilir.

Vercel’in benchmark’ı

Vercel’in dokümanlarında bir benchmark var. Ajanlar bir projeyi yeniden adlandırdı, üyeleri ve bildirim ayarlarını güncelledi, sonra kaydetti. Bir ajan başka bir bileşen kütüphanesiyle kurulmuş bir sayfada tarayıcı araçlarını kullandı. Diğeri UI SDK ile kurulmuş aynı ekranda ajan köprüsünü kullandı.

Tarayıcı araçlarıUI SDK ajan köprüsü
Araç çağrıları−69%
165
Okunan metin (karakter)−83%
39.8k6.8k
Süre−61%
41 s16 s
Maliyet−56%
$0.073$0.032

Kaynak: Vercel’in UI SDK dokümanları. Yaklaşım başına üç çalıştırmanın medyanları, Claude Code üzerinden Sonnet. HextaUI tarafından yeniden üretilmedi.

Vercel’in proje ayarları benchmark’ı: tarayıcı araçları ve UI SDK’nın ajan köprüsü

Bunlar benim değil, Vercel’in rakamları. Yaklaşım başına üç çalıştırmanın medyanları; iki ajan da aynı görev ve sistem istemiyle Claude Code üzerinden Sonnet kullandı ve her çalıştırma görevi tamamladı. "Okunan metin", araçların döndürdüğü karakterleri sayar. Benchmark’ı yeniden üretmedim. Ölçek vermek için, aşağıdaki demonun tam snapshot’ı 729 karakterlik JSON. Sayfanın Vite dev sunucusundaki HTML’i yaklaşık 28.000 karakter. Aynı sayfanın Playwright ARIA snapshot’ı daha da kısaydı, yaklaşık 370 karakter, ama yalnızca rolleri ve adları listeliyor. Her kontrolün hangi eylemleri kabul ettiğini, kapalı bir Select’in hangi seçenekleri tuttuğunu ya da hangi düğmenin bir insana ihtiyaç duyduğunu söylemiyor. Bir ajanın takip çağrılarından tasarruf etmesini sağlayan ham boyut değil, bu ek yapı.

Demoyu kurun

Vite’ın React ve TypeScript şablonuyla başlayın ve iki paketi ekleyin:

npm create vite@latest agent-demo -- --template react-ts
cd agent-demo
npm install
npm install @ui-sdk/react@0.1.0-beta.1 @ui-sdk/core@0.1.0-beta.1

Köprüyü kendi modülünde oluşturun. Uygulama render edilmeden önce main.tsx içinde import edin ki bileşenleri oluşturuldukları anda görsün. Dokümanlar sayfada zaten var olan bileşenleri de bulduğunu söylüyor, bu yüzden onu daha sonra, bir asistan açıldığında da oluşturabilirsiniz.

import { createAgent, registerTools } from "@ui-sdk/react/agent"

const agent = createAgent({
  allow: (step) => {
    if (step.confirm) return window.confirm(step.confirm)
    return step.action !== "clear"
  },
})

const unregister = registerTools(agent, {
  allow: (step) => step.action !== "press",
})

Object.assign(window, { agent })

if (import.meta.hot) import.meta.hot.dispose(unregister)

export { agent }

İki allow callback’i aşağıda yeniden karşımıza çıkacak. Object.assign(window, { agent }) köprüyü window üzerine koyar, böylece onu konsoldan deneyebilirsiniz.

Sayfa; bir metin alanı, bir Select, bir Switch, özel bir koltuk sayacı ve bir Save düğmesi içeren bir ayarlar formu. Bu alıntı sınıfları, yerleşimi ve log bileşenini çıkarıyor, ajanın önemsediği parçaları tutuyor:

<Field>
  <FieldLabel>Project name</FieldLabel>
  <Input
    defaultValue="Apollo"
    onValueChange={(value, details) =>
      record(`Project name → ${value}`, details.source)
    }
  />
</Field>

<Select
  defaultValue="europe"
  items={regions}
  onValueChange={(value, details) => {
    if (details.source === "agent" && value === "asia-pacific") {
      details.cancel()
      return
    }
    record(`Region → ${value}`, details.source)
  }}
>
  <SelectLabel>Region</SelectLabel>
  <SelectTrigger>
    <SelectValue />
  </SelectTrigger>
  <SelectContent>
    {regions.map((region) => (
      <SelectItem key={region.value} value={region.value}>
        {region.label}
      </SelectItem>
    ))}
  </SelectContent>
</Select>

<label>
  Email notifications
  <Switch
    defaultChecked
    onCheckedChange={(checked, details) =>
      record(`Email notifications → ${checked}`, details.source)
    }
  >
    <SwitchThumb />
  </Switch>
</label>

<Button
  data-agent-confirm="Saves the settings for every member of Atlas"
  onClick={(_event, { source }) => record("Save pressed", source)}
>
  Save
</Button>

record sayfadaki bir log’a satır ekler, böylece kimin neyi değiştirdiğini görebilirsiniz. Etiketler burada her zamankinden daha önemli. Köprü bileşenleri erişilebilir adlarıyla adlandırır; bir ajanın tahmin etmek yerine "Region" diyebilmesini sağlayan şey FieldLabel, SelectLabel ve sarmalayan <label> öğesidir.

snapshot() ile sayfayı okuyun

Sayfa render edildiğinde agent.snapshot() şunu döndürdü:

[
  { "ref": "c1", "component": "input", "actions": ["setValue", "clear"], "label": "Project name", "value": "Apollo" },
  {
    "ref": "c3",
    "component": "select",
    "actions": ["setOpen", "setValue", "clear", "setQuery"],
    "label": "Region",
    "open": false,
    "value": "europe",
    "query": "",
    "items": [
      { "value": "europe", "label": "Europe" },
      { "value": "north-america", "label": "North America" },
      { "value": "asia-pacific", "label": "Asia Pacific" }
    ]
  },
  { "ref": "c4", "component": "switch", "actions": ["setChecked"], "label": "Email notifications", "checked": true, "value": "on" },
  { "ref": "c5", "component": "seat-stepper", "actions": ["add", "remove"], "label": "Seats", "value": 3 },
  { "ref": "c6", "component": "button", "actions": ["press"], "label": "Save", "confirm": "Saves the settings for every member of Atlas" }
]

Dikkat edilecek birkaç şey:

  • c2 yok. Field de kaydoldu, ama bir Field snapshot’ta yalnızca hataları varken görünür, çünkü input’u etiketi zaten taşır. Ref’lerdeki boşluk zararsız.
  • Demo items geçtiği için Select öğelerini kapalıyken de listeler. Onlar olmadan query({ within: "Region" }) seçenekleri okur ve köprü bunun için listeyi kısa süreliğine açabilir.
  • Save düğmesi data-agent-confirm’den gelen confirm metnini taşır, böylece ajan denemeden önce bir insanın onaylaması gerektiğini bilir.
  • seat-stepper benim kendi bileşenim. add ve remove listeleniyor çünkü tanımında öyle belirttim; bu aşağıda ele alınıyor.

act() ile değişiklik yapın

Tek bir act() çağrısı, ajanın zaten bildiği her adımı taşıyabilir. Adımlar bileşenleri ref ile ya da etiketle adlandırır ve köprü büyük/küçük harf ve aksanlardan bağımsız olarak tam kelimeleri eşleştirir. Bir öğe de etiketiyle adlandırılabilir:

const result = await agent.act({
  steps: [
    { ref: "Project name", action: "setValue", args: ["Atlas"] },
    { ref: "Region", action: "setValue", args: ["North America"] },
    { ref: "Email notifications", action: "setChecked", args: [false] },
    { ref: "Seats", action: "add", args: [2] },
  ],
})

Sonuç, her adımın değişikliklerini ve her alanın geçerli değerini bildirir:

{
  "ok": true,
  "steps": [
    { "step": 0, "ref": "c1", "changes": [{ "component": "input", "label": "Project name", "key": "value", "value": "Atlas" }] },
    { "step": 1, "ref": "c3", "changes": [{ "component": "select", "label": "Region", "key": "value", "value": "north-america" }] },
    { "step": 2, "ref": "c4", "changes": [{ "component": "switch", "label": "Email notifications", "key": "checked", "value": false }] },
    { "step": 3, "ref": "c5", "changes": [{ "component": "seat-stepper", "label": "Seats", "key": "value", "value": 5 }] }
  ],
  "appeared": [],
  "values": {
    "c1 Project name": "Atlas",
    "c3 Region": "north-america",
    "c4 Email notifications": false,
    "c5 Seats": 5
  }
}

"North America", öğenin değeri olan north-america’ya çözümlendi. values her çağrıda geri döndüğü için ajanın işini kontrol etmek için başka bir snapshot’a ihtiyacı yok.

Hatalar bir modelin okuması için yazılmış. Bilerek tetiklediğim iki tanesi:

setChecked on Email notifications (c4) takes true or false, not "yes".
seat-stepper c5 takes no keydown now. It takes: add, remove.

Başarısız bir adım çağrıyı durdurur ve önceki adımlar uygulanmış olarak kalır. Geri alma yok.

Bir eşleştirme kuralı beni şaşırttı. ref: "Name" içeren bir adım "Project name" alanını değiştirdi, çünkü etiketler tam kelimeyle eşleşir. Bu dokümante edilmiş ve köprü birden fazla bileşenle eşleşen bir adı reddediyor, ama bir ajan tahmin yürütecekse bileşenlere birbirinden farklı etiketler verin.

İzinler: allow, onay işaretleri ve cancel

Hayır demek için üç yer var ve farklı işler yapıyorlar.

createAgent üzerindeki allow, her adımı çalışmadan önce ref, component, label, action, args ve confirm değerleriyle görür. Reddetmek için false, kendi onay arayüzünüz için bir promise döndürün. Benim politikam her clear adımını reddediyor ve sonuç (values burada çıkarıldı) şöyle:

{ "ok": false, "error": { "step": 0, "message": "The app refused clear on c1." }, "steps": [] }

data-agent-confirm, kurallar ne derse desin her zaman bir insan gerektiren bir kontrolü işaretler. allow callback’i yoksa bu kontrol üzerindeki bir adım "Needs a person" mesajıyla başarısız olur. Varsa step.confirm cümleyi taşır, böylece onu gösterebilirsiniz. Benim allow’um onu window.confirm’e iletiyor ve çalıştırmada Save’e basılmadan önce diyalogda "Saves the settings for every member of Atlas" yazıyordu. İşaret, öğesinin içindeki her şeyi kapsar, bu yüzden onu bütün bir forma ya da diyaloğa koyabilirsiniz.

Bir değerde son söz change callback’inizindir. Region Select, bir ajanın Asia Pacific isteğini iptal ediyor:

{
  "ok": true,
  "steps": [{ "step": 0, "ref": "c3", "result": false }],
  "appeared": [],
  "values": {
    "c1 Project name": "Atlas",
    "c3 Region": "north-america",
    "c4 Email notifications": false,
    "c5 Seats": 5
  }
}

ok değerinin true olduğuna dikkat edin. Köprü adımı çalıştırdı, callback ise değişikliği iptal etti; bu yüzden adım result: false bildirir ve değer değişmez. Dokümanlar bunu açıkça söylüyor: ok: true her değerin kabul edildiği anlamına gelmez. changes ve values değerlerini kontrol edin.

Bir ajanın neyi görebileceği için de iki öznitelik var. data-agent-hidden bir bölgeyi snapshot ve query sonuçlarından çıkarır. data-agent-secret bir alanı, değeri "[redacted]" olacak şekilde gösterir. Parola, kart ve tek kullanımlık kod alanları işaret olmadan da gizli kabul edilir.

Ajan değişikliklerini source ile ayırt edin

UI SDK’daki her change callback details.source alır: keyboard, mouse, touch, pen, virtual, programmatic ya da agent. Yukarıdaki çalıştırmadan sonra demonun log’u şöyle görünüyordu:

Project name Atlas, Region North America, bildirimler kapalı ve 5 koltuk gösteren bir Project settings formu; altında her satırı source: agent ile biten bir log
Ajanın çalıştırmasından sonra demo. Her log satırı bir change callback’inden geliyor ve her biri source: agent bildiriyor.

Sonrasında artı düğmesine elle tıklamak Seats → 2 (source: mouse) kaydını düştü. Bu tek alan, "asistanınız tarafından düzenlendi" rozeti yapmaya, ajan değişikliklerini bir inceleme kuyruğuna göndermeye ya da bir insanın tetiklemediği analitik olaylarını atlamaya yeter.

Dokümanlardan bir uyarı: bir şeyi await edip ardından durumu değiştiren bir Button işleyicisi "programmatic" bildirir, çünkü o noktada basma bitmiştir. Ajanı kaynak olarak korumak için sonraki değişikliği @ui-sdk/react/observe içinden gelen withSource(source, …) ile sarın.

Kendi bileşeniniz: definePrimitive ve agent anahtarı

Yerleşik bileşenler köprüye kendiliğinden katılır. definePrimitive ile yazdığınız bileşenler de katılır. İşte demodaki koltuk sayacı; bir ajanın çağırabileceği iki metodu adlandıran bir agent anahtarıyla:

import { createChange, definePrimitive, type ChangeDetails } from "@ui-sdk/core"

type SeatStepperOptions = {
  value?: number
  defaultValue?: number
  max?: number
  onValueChange?: (
    value: number,
    details: ChangeDetails<"add" | "remove">,
  ) => void
}

const seatStepper = definePrimitive({
  name: "seat-stepper",
  options: {
    value: "number",
    defaultValue: "number",
    max: "number",
    onValueChange: "callback",
  },
  controlled: ["value"],
  state: (options: SeatStepperOptions) => ({
    value: options.value ?? options.defaultValue ?? 1,
  }),
  sync: (options) =>
    options.value === undefined ? {} : { value: options.value },
  api: (context) => {
    const change = createChange(context)
    const max = () => context.options().max ?? 50
    return {
      add: (count = 1, event?: Event) =>
        change("value", Math.min(max(), context.store.get().value + count), {
          reason: "add",
          event,
        }),
      remove: (count = 1, event?: Event) =>
        change("value", Math.max(1, context.store.get().value - count), {
          reason: "remove",
          event,
        }),
    }
  },
  slots: (slot) => ({
    root: slot<{}, { value: number }>({
      role: "group",
      state: (state) => ({ value: state.value }),
    }),
    remove: slot({
      attrs: (state) => ({
        type: "button",
        "aria-label": "Remove a seat",
        disabled: state.value <= 1 ? "" : undefined,
      }),
      press: ({ api }, event) => api.remove(1, event),
    }),
    add: slot({
      attrs: () => ({ type: "button", "aria-label": "Add a seat" }),
      press: ({ api }, event) => api.add(1, event),
    }),
  }),
  agent: { actions: ["add", "remove"] },
})

export { seatStepper }

@ui-sdk/react/primitive içinden gelen createParts(seatStepper, { root: "div", remove: "button", add: "button" }) onu React bileşenlerine dönüştürür. Root, her öğe gibi aria-labelledby alır; snapshot’taki "Seats" etiketi de buradan geldi.

agent anahtarı neden önemli: o olmadan köprü eylemleri metot adlarından tahmin eder. setValue gibi ardından büyük harf gelen set adlarını ve open, toggle, clear gibi fiilleri tanır. add ya da remove’u tahmin etmez, bu yüzden stepper hiçbir eylem göstermezdi. Anahtarla yalnızca listelenen metotlar sunulur ve geri kalan her şey reddedilir; yukarıdaki keydown hatası bunu gösterdi. Dokümanlar, TypeScript o noktada API’yi okumuş olsun diye agent’ı en sona, slots’tan sonra koymanızı istiyor ve anahtarı deneysel olarak işaretliyor.

Değişiklik createChange üzerinden geçtiği için uygulamanın onValueChange’i yine source: "agent" görür ve iptal edebilir. Ajan üç koltuktan beşini çıkarmak istediğinde değer 1’e sabitlendi ve çıkarma düğmesi devre dışı kaldı; tam olarak bir insanın tıklamalarının bırakacağı gibi.

Yazarken iki not. attrs içindeki öznitelik değerleri string’dir, bu yüzden disabled bir boolean değil, "" ya da undefined olur; TypeScript bunu yakaladı. Core dokümanları role değerini asla attrs içine koymamayı ve slot’un role anahtarını kullanmayı söylüyor, ama bir Core örneği role’ü attrs içine koyuyor. Ben role anahtarını kullandım.

Araçları WebMCP için kaydedin

registerTools(agent, { allow }); snapshot, query ve act araçlarını WebMCP önerisindeki tarayıcı API’si olan document.modelContext üzerinde yayınlar. Bunu sağlayan bir tarayıcı araçları yerleşik ajanına verebilir. Sağlamayan bir tarayıcıda registerTools hiçbir şey yapmaz.

Test ettiğim headless Chromium document.modelContext sağlamıyor, bu yüzden neyin kaydedildiğini görmek için sayfa yüklenmeden önce yerine geçen bir nesne kurdum. Üç araç şu annotation’larla geldi:

[
  { "name": "snapshot", "annotations": { "readOnlyHint": true, "untrustedContentHint": true } },
  { "name": "act", "annotations": { "readOnlyHint": false, "untrustedContentHint": true } },
  { "name": "query", "annotations": { "readOnlyHint": true, "untrustedContentHint": true } }
]

untrustedContentHint orada, çünkü araç sonuçları bir tablo satırındaki ad gibi kullanıcıların yazdığı sayfa metnini taşır. Araç açıklamaları da modele bu metni talimat olarak değil, her zaman veri olarak ele almasını söyler. Bu bir garanti değil, bir ipucu; bu yüzden riskli her şeyi allow ve data-agent-confirm arkasında tutun.

registerTools üzerindeki allow zorunludur ve köprünün kendi allow’undan sonra, yalnızca tarayıcının ajanından gelen adımlar için çalışır. Bu size iki politika verir: biri sizin yazdığınız ajan için, daha sıkı olanı da sizin yazmadığınız bir ajan için.

  1. Hedefi bulunrefc3 gibi bir ref, sonra bir etiket, bir öğe, sonra bir tür.
  2. Köprü allowcreateAgentHer çağıran için politikanız. False çağrıyı durdurur.
  3. WebMCP allowregisterToolsYalnızca tarayıcının ajanı için ikinci bir politika.
  4. Bileşen API’sisetValueBir controller’ın çağıracağı metodun aynısı.
  5. Sizin callback’inizdetails.source"agent" görür ve cancel() çağırabilir.
Tek bir act adımına ne olur

UI SDK’nın kılavuzu, araçları ChatGPT masaüstü uygulamasının yerleşik tarayıcısından kullanmayı adım adım anlatıyor; orada site araçlarının etkinleştirilmesi gerekiyor ve bunları yalnızca bazı modeller ve çalışma alanları destekliyor. O yolu test etmedim. Hedeflediğiniz tarayıcıda typeof document.modelContext?.registerTool değerini kontrol edin. Kaydın desteklendiği yerde "function" döndürür.

Bir beta bulgusu: suçu insana atan bir ret

Benim kurulumumda köprünün allow’u Save için kişiden onay istiyordu, WebMCP allow’u ise her press adımını reddediyor. Kayıtlı act aracını Save’e basmak için çağırdığımda onay diyaloğu çıktı, kabul ettim, ardından WebMCP politikası adımı reddetti. Hata şunu söylüyordu:

The person said no to "Saves the settings for every member of Atlas".

Kişi evet dedi. Kod hayır dedi. Adım reddedildi, bu doğru, ama bu mesajı okuyan bir model kullanıcıya onayladığı bir şeyi reddettiğini söyleyebilir. Ayrıca her durumda reddedecek bir politikayı kontrol etmeden önce kişiye sordu. Onay işaretlerini basmaları reddeden bir WebMCP allow’u ile birleştiriyorsanız daha erken reddedin: köprünün allow’unda da step.action değerini kontrol edin, böylece kişiye hiç sorulmaz. Bunu 0.1.0-beta.1 için bildiriyorum ve değişmesini bekliyorum.

Ne zaman değer

Köprü, bir ajan ürünün parçası olduğunda yerini hak eder: formları dolduran bir asistan paneli, kullanıcı için ayarları değiştiren bir destek aracı ya da sitenizde iyi çalışmasını istediğiniz bir tarayıcı ajanı. Testlerde de faydalıdır, çünkü act() bileşenleri seçiciler olmadan yönetmenin tipli bir yolunu verir.

Bu tek başına kararlı bir uygulamayı taşımak için bir neden değil. Köprü yalnızca UI SDK bileşenlerini görür, bu yüzden kütüphaneleri karıştıran bir sayfa ajana kısmi bir görünüm verir. Ayrıca bu bir beta; agent anahtarı ve geçmiş yardımcıları deneysel olarak işaretli.

Bunun üzerine kuruyorsanız birkaç alışkanlık ajanları faydalı ve güvenli tutar:

  • her kontrole erişilebilir bir ad verin, çünkü ajanın yazdığı şey odur;
  • seçenekler kapalıyken okunabilsin diye Select ve Combobox’lara items geçin;
  • bir ajanın asla yapmaması gerekenleri allow içinde reddedin ve bir insan gerektirenleri data-agent-confirm ile işaretleyin;
  • değerleri change callback’lerinde doğrulayın, çünkü setValue API’yi doğrudan çağırır ve klavye ve işaretçi işleyicilerinizi atlar;
  • ok’a güvenmek yerine her act() sonucundan values değerini okuyun.

HextaUI, bileşenlerinin stillendirilmiş bir UI SDK sürümü üzerinde çalışıyor; bunlar o sürümü kurarken temel aldığım desenler. UI SDK’nın ajanların ötesinde Radix ve Base UI ile karşılaştırması için UI SDK, Base UI ve Radix yazısına bakın. Bileşenleri Tailwind ile stillendirmek için Vercel’in UI SDK’sını Tailwind ile stillendirmek yazısına bakın.