diff --git a/.changeset/api-keys-search-clear-button.md b/.changeset/api-keys-search-clear-button.md
new file mode 100644
index 00000000000..f60a7bab5e8
--- /dev/null
+++ b/.changeset/api-keys-search-clear-button.md
@@ -0,0 +1,9 @@
+---
+'@clerk/localizations': patch
+'@clerk/shared': patch
+'@clerk/ui': patch
+---
+
+Add a clear button to search inputs for quickly resetting the current query. It appears in the `` search and the `` members search.
+
+Search inputs now expose a shared `searchInput` appearance element (layered alongside any existing component-specific element), and the clear button is themeable via the new shared `searchInputClearButton` element. The clear button's label can be customized with the new shared `searchInput.action__clear` localization key.
diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts
index de94ee932ae..9b70f8d0a2d 100644
--- a/packages/localizations/src/ar-SA.ts
+++ b/packages/localizations/src/ar-SA.ts
@@ -1216,6 +1216,9 @@ export const arSA: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'إضافة حساب',
diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts
index 2203157ce23..3fb72ae5d54 100644
--- a/packages/localizations/src/be-BY.ts
+++ b/packages/localizations/src/be-BY.ts
@@ -1223,6 +1223,9 @@ export const beBY: LocalizationResource = {
title: 'Код TOTP',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Дадаць уліковы запіс',
diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts
index 8b6d0093b09..4e14def42ab 100644
--- a/packages/localizations/src/bg-BG.ts
+++ b/packages/localizations/src/bg-BG.ts
@@ -1220,6 +1220,9 @@ export const bgBG: LocalizationResource = {
title: 'Authenticator App MFA',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Add account',
diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts
index bf5acdc03a2..d8a33bdfa92 100644
--- a/packages/localizations/src/bn-IN.ts
+++ b/packages/localizations/src/bn-IN.ts
@@ -1228,6 +1228,9 @@ export const bnIN: LocalizationResource = {
title: 'যাচাইকরণ প্রয়োজন',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'অ্যাকাউন্ট যোগ করুন',
diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts
index a768b80edd1..fe24667d6a5 100644
--- a/packages/localizations/src/ca-ES.ts
+++ b/packages/localizations/src/ca-ES.ts
@@ -1228,6 +1228,9 @@ export const caES: LocalizationResource = {
title: 'Verificació per TOTP (2FA)',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Afegeix compte',
diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts
index e5566d4bce0..40ca97e4f47 100644
--- a/packages/localizations/src/cs-CZ.ts
+++ b/packages/localizations/src/cs-CZ.ts
@@ -1226,6 +1226,9 @@ export const csCZ: LocalizationResource = {
title: 'Vyžadováno ověření',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Přidat účet',
diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts
index d6a111c43a7..8f6cd90e1ce 100644
--- a/packages/localizations/src/da-DK.ts
+++ b/packages/localizations/src/da-DK.ts
@@ -1218,6 +1218,9 @@ export const daDK: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Tilføj konto',
diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts
index 970558a0f4f..f7c181cf898 100644
--- a/packages/localizations/src/de-DE.ts
+++ b/packages/localizations/src/de-DE.ts
@@ -1235,6 +1235,9 @@ export const deDE: LocalizationResource = {
title: 'TOTP-Verifizierung (MFA)',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Konto hinzufügen',
diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts
index 156e7e56130..fbc71c2473a 100644
--- a/packages/localizations/src/el-GR.ts
+++ b/packages/localizations/src/el-GR.ts
@@ -1226,6 +1226,9 @@ export const elGR: LocalizationResource = {
title: 'Επαλήθευση δύο παραγόντων',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Προσθήκη λογαριασμού',
diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts
index 27e0239b6ea..74fa7ea9e73 100644
--- a/packages/localizations/src/en-GB.ts
+++ b/packages/localizations/src/en-GB.ts
@@ -1219,6 +1219,9 @@ export const enGB: LocalizationResource = {
title: 'Verification required',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Add account',
diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts
index 6f4c008a9ae..d766d4ce383 100644
--- a/packages/localizations/src/en-US.ts
+++ b/packages/localizations/src/en-US.ts
@@ -1247,6 +1247,9 @@ export const enUS: LocalizationResource = {
title: 'Verification required',
},
},
+ searchInput: {
+ action__clear: 'Clear search',
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Add account',
diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts
index f48e0d8dab4..6c465f680a0 100644
--- a/packages/localizations/src/es-CR.ts
+++ b/packages/localizations/src/es-CR.ts
@@ -1224,6 +1224,9 @@ export const esCR: LocalizationResource = {
title: 'Verificación requerida',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Agregar cuenta',
diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts
index 7d08afe08cd..2d6895e5822 100644
--- a/packages/localizations/src/es-ES.ts
+++ b/packages/localizations/src/es-ES.ts
@@ -1229,6 +1229,9 @@ export const esES: LocalizationResource = {
title: 'Verificación por TOTP (2FA)',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Añadir cuenta',
diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts
index 18d06e8dc88..64b90aa3da2 100644
--- a/packages/localizations/src/es-MX.ts
+++ b/packages/localizations/src/es-MX.ts
@@ -1225,6 +1225,9 @@ export const esMX: LocalizationResource = {
title: 'Verificación requerida',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Agregar cuenta',
diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts
index 45a1012d1a7..b027e07069f 100644
--- a/packages/localizations/src/es-UY.ts
+++ b/packages/localizations/src/es-UY.ts
@@ -1223,6 +1223,9 @@ export const esUY: LocalizationResource = {
title: 'Verificación requerida',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Agregar cuenta',
diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts
index 2a28fa818b2..1cf0c875abf 100644
--- a/packages/localizations/src/fa-IR.ts
+++ b/packages/localizations/src/fa-IR.ts
@@ -1228,6 +1228,9 @@ export const faIR: LocalizationResource = {
title: 'تایید هویت الزامی است',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'افزودن حساب',
diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts
index 8f9779263f9..d02b810a53a 100644
--- a/packages/localizations/src/fi-FI.ts
+++ b/packages/localizations/src/fi-FI.ts
@@ -1230,6 +1230,9 @@ export const fiFI: LocalizationResource = {
title: 'Vahvistus vaaditaan',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Lisää tili',
diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts
index 0c8fc0af507..3ef7465f900 100644
--- a/packages/localizations/src/fr-FR.ts
+++ b/packages/localizations/src/fr-FR.ts
@@ -1236,6 +1236,9 @@ export const frFR: LocalizationResource = {
title: 'Vérification par application d’authentification',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Ajouter un compte',
diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts
index 991eba72e8e..b1763ec3337 100644
--- a/packages/localizations/src/he-IL.ts
+++ b/packages/localizations/src/he-IL.ts
@@ -1213,6 +1213,9 @@ export const heIL: LocalizationResource = {
title: 'אימות דו-שלבי',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'הוסף חשבון',
diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts
index 10ad49c33fd..98812de0410 100644
--- a/packages/localizations/src/hi-IN.ts
+++ b/packages/localizations/src/hi-IN.ts
@@ -1228,6 +1228,9 @@ export const hiIN: LocalizationResource = {
title: 'सत्यापन आवश्यक है',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'खाता जोड़ें',
diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts
index 9911ad376c8..db93f008960 100644
--- a/packages/localizations/src/hr-HR.ts
+++ b/packages/localizations/src/hr-HR.ts
@@ -1229,6 +1229,9 @@ export const hrHR: LocalizationResource = {
title: 'Potrebna verifikacija',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Dodaj račun',
diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts
index f0d727933c6..3a3aff4cfe8 100644
--- a/packages/localizations/src/hu-HU.ts
+++ b/packages/localizations/src/hu-HU.ts
@@ -1231,6 +1231,9 @@ export const huHU: LocalizationResource = {
title: 'Ellenőrzés szükséges',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Fiók hozzáadása',
diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts
index 0e8ad38dd4a..df03c826a49 100644
--- a/packages/localizations/src/id-ID.ts
+++ b/packages/localizations/src/id-ID.ts
@@ -1222,6 +1222,9 @@ export const idID: LocalizationResource = {
title: 'Verifikasi diperlukan',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Tambah akun',
diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts
index 0ca57d76698..a2f5087a28e 100644
--- a/packages/localizations/src/is-IS.ts
+++ b/packages/localizations/src/is-IS.ts
@@ -1230,6 +1230,9 @@ export const isIS: LocalizationResource = {
title: 'Staðfesting nauðsynleg',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Bæta við reikningi',
diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts
index 96a6a24fba0..305b7cc39d0 100644
--- a/packages/localizations/src/it-IT.ts
+++ b/packages/localizations/src/it-IT.ts
@@ -1228,6 +1228,9 @@ export const itIT: LocalizationResource = {
title: 'Verifica TOTP (MFA)',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Aggiungi account',
diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts
index 8eb8b07dfcf..ad9f53c787f 100644
--- a/packages/localizations/src/ja-JP.ts
+++ b/packages/localizations/src/ja-JP.ts
@@ -1229,6 +1229,9 @@ export const jaJP: LocalizationResource = {
title: '確認が必要です',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'アカウントを追加',
diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts
index 4978a338c80..f2ecf7e21fe 100644
--- a/packages/localizations/src/kk-KZ.ts
+++ b/packages/localizations/src/kk-KZ.ts
@@ -1212,6 +1212,9 @@ export const kkKZ: LocalizationResource = {
title: 'Растау қажет',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Есептік жазба қосу',
diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts
index 12aed128ad1..0bb35f7cee4 100644
--- a/packages/localizations/src/ko-KR.ts
+++ b/packages/localizations/src/ko-KR.ts
@@ -1217,6 +1217,9 @@ export const koKR: LocalizationResource = {
title: '인증이 필요해요',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: '계정 추가',
diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts
index 1c05a448f19..0d25f3cea12 100644
--- a/packages/localizations/src/mn-MN.ts
+++ b/packages/localizations/src/mn-MN.ts
@@ -1221,6 +1221,9 @@ export const mnMN: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Бүртгэл нэмэх',
diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts
index 9e015ceee8e..72e32d5764c 100644
--- a/packages/localizations/src/ms-MY.ts
+++ b/packages/localizations/src/ms-MY.ts
@@ -1232,6 +1232,9 @@ export const msMY: LocalizationResource = {
title: 'Pengesahan diperlukan',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Tambah akaun',
diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts
index 55692054367..edd764c32d5 100644
--- a/packages/localizations/src/nb-NO.ts
+++ b/packages/localizations/src/nb-NO.ts
@@ -1231,6 +1231,9 @@ export const nbNO: LocalizationResource = {
title: 'Verifisering påkrevd',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Legg til konto',
diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts
index 74cde0f3c28..4107509c7d1 100644
--- a/packages/localizations/src/nl-BE.ts
+++ b/packages/localizations/src/nl-BE.ts
@@ -1222,6 +1222,9 @@ export const nlBE: LocalizationResource = {
title: 'Tweestapsverificatie',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Account toevoegen',
diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts
index cb89363c510..b54ddc59780 100644
--- a/packages/localizations/src/nl-NL.ts
+++ b/packages/localizations/src/nl-NL.ts
@@ -1222,6 +1222,9 @@ export const nlNL: LocalizationResource = {
title: 'Tweestapsverificatie',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Account toevoegen',
diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts
index 6fede4db9eb..92adfd113a1 100644
--- a/packages/localizations/src/pl-PL.ts
+++ b/packages/localizations/src/pl-PL.ts
@@ -1220,6 +1220,9 @@ export const plPL: LocalizationResource = {
title: 'Wymagana weryfikacja',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Dodaj konto',
diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts
index ae61bfe88c4..0b9478c97dd 100644
--- a/packages/localizations/src/pt-BR.ts
+++ b/packages/localizations/src/pt-BR.ts
@@ -1230,6 +1230,9 @@ export const ptBR: LocalizationResource = {
title: 'Autenticação TOTP',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Adicionar conta',
diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts
index 7754da6a7fb..f378392dcb4 100644
--- a/packages/localizations/src/pt-PT.ts
+++ b/packages/localizations/src/pt-PT.ts
@@ -1231,6 +1231,9 @@ export const ptPT: LocalizationResource = {
title: 'Verificação necessária',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Adicionar conta',
diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts
index dec56853bd8..6a91ac6bbd0 100644
--- a/packages/localizations/src/ro-RO.ts
+++ b/packages/localizations/src/ro-RO.ts
@@ -1231,6 +1231,9 @@ export const roRO: LocalizationResource = {
title: 'Verificare în doi pași',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Adaugă cont',
diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts
index 201b23e3775..545061ad0e5 100644
--- a/packages/localizations/src/ru-RU.ts
+++ b/packages/localizations/src/ru-RU.ts
@@ -1227,6 +1227,9 @@ export const ruRU: LocalizationResource = {
title: 'Двухфакторная верификация',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Добавить учетную запись',
diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts
index c81c0238ae1..0c34697e47f 100644
--- a/packages/localizations/src/sk-SK.ts
+++ b/packages/localizations/src/sk-SK.ts
@@ -1221,6 +1221,9 @@ export const skSK: LocalizationResource = {
title: 'Vyžaduje sa overenie',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Pridať účet',
diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts
index 1544a7ae617..92aee810ba2 100644
--- a/packages/localizations/src/sr-RS.ts
+++ b/packages/localizations/src/sr-RS.ts
@@ -1218,6 +1218,9 @@ export const srRS: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Dodaj nalog',
diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts
index 8240c4fae21..56d3da38257 100644
--- a/packages/localizations/src/sv-SE.ts
+++ b/packages/localizations/src/sv-SE.ts
@@ -1220,6 +1220,9 @@ export const svSE: LocalizationResource = {
title: 'Tvåstegsverifiering',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Lägg till konto',
diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts
index a209297f22c..587a71d59e5 100644
--- a/packages/localizations/src/ta-IN.ts
+++ b/packages/localizations/src/ta-IN.ts
@@ -1234,6 +1234,9 @@ export const taIN: LocalizationResource = {
title: 'சரிபார்ப்பு தேவை',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'கணக்கைச் சேர்',
diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts
index d209049d175..de16deaba50 100644
--- a/packages/localizations/src/te-IN.ts
+++ b/packages/localizations/src/te-IN.ts
@@ -1231,6 +1231,9 @@ export const teIN: LocalizationResource = {
title: 'ధృవీకరణ అవసరం',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'ఖాతాను జోడించండి',
diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts
index 3db92e6126c..8c8b34e16ba 100644
--- a/packages/localizations/src/th-TH.ts
+++ b/packages/localizations/src/th-TH.ts
@@ -1220,6 +1220,9 @@ export const thTH: LocalizationResource = {
title: 'ต้องการการยืนยัน',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'เพิ่มบัญชี',
diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts
index 85ee6d51781..98e08e93e4a 100644
--- a/packages/localizations/src/tr-TR.ts
+++ b/packages/localizations/src/tr-TR.ts
@@ -1219,6 +1219,9 @@ export const trTR: LocalizationResource = {
title: 'TOTP Kodunu Girin',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Hesap ekle',
diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts
index cf4b56558fb..dc6f645e1c5 100644
--- a/packages/localizations/src/uk-UA.ts
+++ b/packages/localizations/src/uk-UA.ts
@@ -1218,6 +1218,9 @@ export const ukUA: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Add account',
diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts
index 2496a6447e9..4d61f937845 100644
--- a/packages/localizations/src/vi-VN.ts
+++ b/packages/localizations/src/vi-VN.ts
@@ -1228,6 +1228,9 @@ export const viVN: LocalizationResource = {
title: 'Xác minh yêu cầu',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: 'Thêm tài khoản',
diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts
index 4caee7c1c85..50fd9254b16 100644
--- a/packages/localizations/src/zh-CN.ts
+++ b/packages/localizations/src/zh-CN.ts
@@ -1209,6 +1209,9 @@ export const zhCN: LocalizationResource = {
title: undefined,
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: '添加账户',
diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts
index eb79aa38554..b79689fb355 100644
--- a/packages/localizations/src/zh-TW.ts
+++ b/packages/localizations/src/zh-TW.ts
@@ -1212,6 +1212,9 @@ export const zhTW: LocalizationResource = {
title: '輸入驗證碼',
},
},
+ searchInput: {
+ action__clear: undefined,
+ },
signIn: {
accountSwitcher: {
action__addAccount: '新增帳戶',
diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts
index 8c8f12fb467..9ca7ecee35e 100644
--- a/packages/shared/src/types/localization.ts
+++ b/packages/shared/src/types/localization.ts
@@ -87,6 +87,10 @@ export type __internal_LocalizationResource = {
/** Label for the “Last used” badge on authentication strategies. */
lastAuthenticationStrategy: LocalizationValue;
dividerText: LocalizationValue;
+ /** Shared strings for search inputs across the components. */
+ searchInput: {
+ action__clear: LocalizationValue;
+ };
formFieldLabel__emailAddress: LocalizationValue;
formFieldLabel__emailAddresses: LocalizationValue;
formFieldLabel__phoneNumber: LocalizationValue;
diff --git a/packages/ui/src/baseTheme.ts b/packages/ui/src/baseTheme.ts
index fef86197013..4cb07b6fc57 100644
--- a/packages/ui/src/baseTheme.ts
+++ b/packages/ui/src/baseTheme.ts
@@ -75,6 +75,18 @@ const checkboxBaseStyles = (theme: InternalTheme) => ({
},
});
+// Clerk theme tightens the trailing-button geometry (see `common.inputTrailingButton` for the
+// simple-theme base) so it sits flush inside the denser default-theme input. Apply to every
+// button anchored to the trailing edge of an input.
+const inputTrailingButtonStyles = (theme: InternalTheme) => ({
+ insetInlineEnd: theme.space.$0x75,
+ insetBlock: theme.space.$0x75,
+ borderRadius: `calc(${theme.radii.$md} - ${theme.space.$0x75})`,
+ '&::before': {
+ inset: `calc(${theme.space.$0x75} * -1)`,
+ },
+});
+
const inputStyles = (theme: InternalTheme) => ({
borderWidth: 0,
...inputShadowStyles(theme, {
@@ -305,14 +317,8 @@ const clerkTheme: Appearance = {
borderWidth: 0,
boxShadow: `0px 0px 2px 0px rgba(0, 0, 0, 0.08), 0px 1px 2px 0px rgba(25, 28, 33, 0.12), 0px 0px 0px 1px ${theme.colors.$borderAlpha100}`,
},
- formFieldInputShowPasswordButton: {
- insetInlineEnd: theme.space.$0x75,
- insetBlock: theme.space.$0x75,
- borderRadius: `calc(${theme.radii.$md} - ${theme.space.$0x75})`,
- '&::before': {
- inset: `calc(${theme.space.$0x75} * -1)`,
- },
- },
+ formFieldInputShowPasswordButton: inputTrailingButtonStyles(theme),
+ searchInputClearButton: inputTrailingButtonStyles(theme),
};
},
} satisfies Appearance;
diff --git a/packages/ui/src/components/APIKeys/APIKeys.tsx b/packages/ui/src/components/APIKeys/APIKeys.tsx
index bf018663e22..64a16abc1a3 100644
--- a/packages/ui/src/components/APIKeys/APIKeys.tsx
+++ b/packages/ui/src/components/APIKeys/APIKeys.tsx
@@ -1,30 +1,19 @@
import { isClerkAPIResponseError } from '@clerk/shared/error';
import { isOrganizationId } from '@clerk/shared/internal/clerk-js/organization';
-import { useAPIKeys, __internal_useOrganizationBase, useClerk, useUser } from '@clerk/shared/react';
+import { __internal_useOrganizationBase, useAPIKeys, useClerk, useUser } from '@clerk/shared/react';
import type { APIKeyResource } from '@clerk/shared/types';
import { lazy, useState } from 'react';
import { useProtect } from '@/ui/common';
import { useAPIKeysContext, withCoreUserGuard } from '@/ui/contexts';
-import {
- Box,
- Button,
- Col,
- descriptors,
- Flex,
- Flow,
- Icon,
- localizationKeys,
- useLocalizations,
-} from '@/ui/customizables';
+import { Box, Button, Col, descriptors, Flex, Flow, localizationKeys, useLocalizations } from '@/ui/customizables';
import { Action } from '@/ui/elements/Action';
import { useCardState, withCardStateProvider } from '@/ui/elements/contexts';
-import { InputWithIcon } from '@/ui/elements/InputWithIcon';
import { Pagination } from '@/ui/elements/Pagination';
+import { SearchInput } from '@/ui/elements/SearchInput';
import { useDebounce } from '@/ui/hooks';
-import { handleError } from '@/ui/utils/errorHandler';
-import { MagnifyingGlass } from '@/ui/icons';
import { mqu } from '@/ui/styledSystem';
+import { handleError } from '@/ui/utils/errorHandler';
import { APIKeysTable } from './ApiKeysTable';
import type { OnCreateParams } from './CreateAPIKeyForm';
@@ -161,22 +150,13 @@ export const APIKeysPage = ({ subject, perPage, revokeModalRoot }: APIKeysPagePr
elementDescriptor={descriptors.apiKeysHeader}
>
- ({ color: t.colors.$colorMutedForeground })}
- />
- }
value={searchValue}
- type='search'
- autoComplete='off'
- autoCapitalize='none'
- spellCheck={false}
onChange={e => setSearchValue(e.target.value)}
+ onClear={() => setSearchValue('')}
elementDescriptor={descriptors.apiKeysSearchInput}
/>
diff --git a/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx b/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx
index a882e3982c2..bcb7195e189 100644
--- a/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx
+++ b/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx
@@ -2,12 +2,10 @@ import type { useOrganization } from '@clerk/shared/react';
import type { GetMembersParams } from '@clerk/shared/types';
import { useEffect, useRef } from 'react';
-import { descriptors, Flex, Icon, localizationKeys, useLocalizations } from '@/customizables';
-import { MagnifyingGlass } from '@/icons';
-import { Spinner } from '@/primitives';
+import { descriptors, Flex, localizationKeys, useLocalizations } from '@/customizables';
import { mqu } from '@/styledSystem';
import { Animated } from '@/ui/elements/Animated';
-import { InputWithIcon } from '@/ui/elements/InputWithIcon';
+import { SearchInput } from '@/ui/elements/SearchInput';
import { ACTIVE_MEMBERS_PAGE_SIZE } from './OrganizationMembers';
@@ -63,6 +61,15 @@ export const MembersSearch = ({ query, value, memberships, onSearchChange, onQue
}, membersSearchDebounceMs);
}
+ const handleClear = () => {
+ // Cancel any pending debounce so it can't re-trigger the query with the stale value.
+ if (debounceTimer.current) {
+ clearTimeout(debounceTimer.current);
+ }
+ onSearchChange('');
+ onQueryTrigger('');
+ };
+
// If search is not performed on a initial page, resets pagination offset
// based on the response count
useEffect(() => {
@@ -88,26 +95,15 @@ export const MembersSearch = ({ query, value, memberships, onSearchChange, onQue
},
}}
>
-
- ) : (
- ({ color: t.colors.$colorMutedForeground })}
- />
- )
- }
+ leftIconElementDescriptor={descriptors.organizationProfileMembersSearchInputIcon}
onKeyUp={handleKeyUp}
onChange={handleChange}
+ onClear={handleClear}
elementDescriptor={descriptors.organizationProfileMembersSearchInput}
/>
diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts
index 69eca6184fe..0e8f3dc9d92 100644
--- a/packages/ui/src/customizables/elementDescriptors.ts
+++ b/packages/ui/src/customizables/elementDescriptors.ts
@@ -177,6 +177,8 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([
'formFieldInputShowPasswordIcon',
'formFieldInputCopyToClipboardButton',
'formFieldInputCopyToClipboardIcon',
+ 'searchInput',
+ 'searchInputClearButton',
'phoneInputBox',
'formInputGroup',
diff --git a/packages/ui/src/elements/InputWithIcon.tsx b/packages/ui/src/elements/InputWithIcon.tsx
index 09e7d5329f1..3d4c1f20199 100644
--- a/packages/ui/src/elements/InputWithIcon.tsx
+++ b/packages/ui/src/elements/InputWithIcon.tsx
@@ -1,12 +1,45 @@
+import { inertProps } from '@clerk/shared/inert';
+import { isVirtualClick } from '@floating-ui/react/utils';
import React from 'react';
import { Box, Flex, Input } from '../customizables';
-import type { PropsOfComponent } from '../styledSystem';
+import type { ElementDescriptor } from '../customizables/elementDescriptors';
+import { Close } from '../icons';
+import { common, type PropsOfComponent } from '../styledSystem';
+import { mergeRefs } from '../utils/mergeRefs';
+import { IconButton } from './IconButton';
-type InputWithIcon = PropsOfComponent & { leftIcon?: React.ReactElement };
+type InputWithIcon = PropsOfComponent & {
+ leftIcon?: React.ReactElement;
+ /**
+ * When provided, renders a clear button anchored to the trailing edge of the input. The button
+ * stays mounted (fading via opacity + `inert`) to avoid layout shift, and calls `onClear` on
+ * activation — the parent is responsible for resetting `value`. Keyboard activation returns focus
+ * to the input.
+ */
+ onClear?: () => void;
+ clearButtonLabel?: string;
+ clearButtonElementDescriptor?: ElementDescriptor;
+};
export const InputWithIcon = React.forwardRef((props, ref) => {
- const { leftIcon, sx, ...rest } = props;
+ const { leftIcon, sx, onClear, clearButtonLabel, clearButtonElementDescriptor, ...rest } = props;
+ const internalRef = React.useRef(null);
+
+ const hasClearButton = Boolean(onClear);
+ const hasValue = rest.value != null && rest.value !== '';
+
+ const handleClear = (event: React.MouseEvent) => {
+ onClear?.();
+ // The button is about to go inert and drop out of the tab order. On keyboard or assistive-tech
+ // activation, return focus to the input; a real pointer or touch click leaves focus alone so the
+ // on-screen keyboard is not forced open. `isVirtualClick` guards `detail === 0` with a pointer
+ // check plus Firefox/Android quirks, which a bare `detail === 0` check misses on touch devices.
+ if (isVirtualClick(event.nativeEvent)) {
+ internalRef.current?.focus({ preventScroll: true });
+ }
+ };
+
return (
((
width: '100%',
paddingInlineStart: theme.space.$10,
}),
+ hasClearButton
+ ? theme => ({
+ paddingInlineEnd: theme.space.$10,
+ '::-webkit-search-cancel-button': {
+ display: 'none',
+ },
+ })
+ : undefined,
sx,
]}
- ref={ref}
+ // @ts-expect-error Type mismatch between ForwardRef and RefObject due to null
+ ref={hasClearButton ? mergeRefs(ref, internalRef) : ref}
/>
+ {hasClearButton ? (
+ ({
+ ...common.inputTrailingButton(theme),
+ opacity: hasValue ? 1 : 0,
+ transition: `opacity ${theme.transitionDuration.$fast} ${theme.transitionTiming.$common}`,
+ })}
+ />
+ ) : null}
);
});
diff --git a/packages/ui/src/elements/PasswordInput.tsx b/packages/ui/src/elements/PasswordInput.tsx
index 719f0897563..93c1c2be5a1 100644
--- a/packages/ui/src/elements/PasswordInput.tsx
+++ b/packages/ui/src/elements/PasswordInput.tsx
@@ -7,7 +7,7 @@ import { useEnvironment } from '../contexts';
import { descriptors, Flex, Input, localizationKeys, useLocalizations } from '../customizables';
import { usePassword } from '../hooks/usePassword';
import { Eye, EyeSlash } from '../icons';
-import type { PropsOfComponent } from '../styledSystem';
+import { common, type PropsOfComponent } from '../styledSystem';
import { mergeRefs } from '../utils/mergeRefs';
import { IconButton } from './IconButton';
@@ -112,21 +112,7 @@ export const PasswordInput = forwardRef((p
size='xs'
tabIndex={tabIndex}
onClick={() => setHidden(s => !s)}
- sx={theme => ({
- position: 'absolute',
- insetInlineEnd: theme.space.$1,
- insetBlock: theme.space.$1,
- borderRadius: `calc(${theme.radii.$md} - ${theme.space.$1})`,
- color: theme.colors.$neutralAlpha400,
- paddingInline: theme.space.$2,
- '&::before': {
- content: '""',
- position: 'absolute',
- inset: `calc(${theme.space.$1} * -1)`,
- display: 'block',
- borderRadius: theme.radii.$md,
- },
- })}
+ sx={theme => common.inputTrailingButton(theme)}
icon={hidden ? Eye : EyeSlash}
/>
diff --git a/packages/ui/src/elements/SearchInput.tsx b/packages/ui/src/elements/SearchInput.tsx
new file mode 100644
index 00000000000..40ecdd8a8f9
--- /dev/null
+++ b/packages/ui/src/elements/SearchInput.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+
+import { descriptors, Icon, localizationKeys, useLocalizations } from '../customizables';
+import type { ElementDescriptor } from '../customizables/elementDescriptors';
+import { MagnifyingGlass } from '../icons';
+import { Spinner } from '../primitives';
+import type { PropsOfComponent } from '../styledSystem';
+import { InputWithIcon } from './InputWithIcon';
+
+type SearchInputProps = Omit<
+ PropsOfComponent,
+ 'leftIcon' | 'type' | 'clearButtonElementDescriptor'
+> & {
+ /**
+ * Called when the user clears the input. The parent is responsible for resetting `value`.
+ */
+ onClear: () => void;
+ /**
+ * Renders a spinner in place of the search icon, e.g. while results are loading.
+ */
+ isLoading?: boolean;
+ /**
+ * Descriptor for the leading search icon.
+ */
+ leftIconElementDescriptor?: ElementDescriptor;
+};
+
+/**
+ * A search input preset: a magnifying-glass icon (or spinner while loading) and a trailing clear
+ * button that all search inputs share. The clear button uses the single `searchInputClearButton`
+ * appearance element and a shared clear label, so new search inputs don't introduce their own.
+ */
+export const SearchInput = React.forwardRef(function SearchInput(
+ { isLoading, leftIconElementDescriptor, clearButtonLabel, elementDescriptor, ...rest },
+ ref,
+) {
+ const { t } = useLocalizations();
+
+ // Every search input shares the `searchInput` element, layered with any consumer-specific
+ // descriptor so both `cl-searchInput` and e.g. `cl-apiKeysSearchInput` are themeable.
+ const inputDescriptors = [
+ descriptors.searchInput,
+ ...(Array.isArray(elementDescriptor) ? elementDescriptor : [elementDescriptor]),
+ ];
+
+ return (
+
+ ) : (
+ ({ color: theme.colors.$colorMutedForeground })}
+ />
+ )
+ }
+ clearButtonLabel={clearButtonLabel ?? t(localizationKeys('searchInput.action__clear'))}
+ clearButtonElementDescriptor={descriptors.searchInputClearButton}
+ {...rest}
+ />
+ );
+});
diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts
index 71d20488744..bb64ee12425 100644
--- a/packages/ui/src/internal/appearance.ts
+++ b/packages/ui/src/internal/appearance.ts
@@ -306,6 +306,8 @@ export type ElementsConfig = {
formFieldInputShowPasswordIcon: WithOptions;
formFieldInputCopyToClipboardButton: WithOptions;
formFieldInputCopyToClipboardIcon: WithOptions;
+ searchInput: WithOptions;
+ searchInputClearButton: WithOptions;
phoneInputBox: WithOptions;
formInputGroup: WithOptions;
diff --git a/packages/ui/src/styledSystem/common.ts b/packages/ui/src/styledSystem/common.ts
index 9abc7b7173c..6ffa85d2f2a 100644
--- a/packages/ui/src/styledSystem/common.ts
+++ b/packages/ui/src/styledSystem/common.ts
@@ -188,6 +188,26 @@ const disabled = (t: InternalTheme) => {
} as const;
};
+// Shared chrome for a button anchored to the trailing edge of an input (e.g. password reveal,
+// search clear). The clerk theme tightens the geometry in `baseTheme.ts`; the simple theme uses
+// these values as-is. Callers layer on their own icon/visibility styles.
+const inputTrailingButton = (t: InternalTheme) =>
+ ({
+ position: 'absolute',
+ insetInlineEnd: t.space.$1,
+ insetBlock: t.space.$1,
+ borderRadius: `calc(${t.radii.$md} - ${t.space.$1})`,
+ color: t.colors.$neutralAlpha400,
+ paddingInline: t.space.$2,
+ '&::before': {
+ content: '""',
+ position: 'absolute',
+ inset: `calc(${t.space.$1} * -1)`,
+ display: 'block',
+ borderRadius: t.radii.$md,
+ },
+ }) as const;
+
const centeredFlex = (display: 'flex' | 'inline-flex' = 'flex') => ({
display: display,
justifyContent: 'center',
@@ -264,6 +284,7 @@ export const common = {
focusRing,
disabled,
borderColor,
+ inputTrailingButton,
centeredFlex,
maxHeightScroller,
mutedBackground,