Skip to content

feat(ui): spotlight interactive captcha during sign-in/sign-up#8907

Merged
anagstef merged 13 commits into
mainfrom
carp/captcha-inline-spotlight
Jun 19, 2026
Merged

feat(ui): spotlight interactive captcha during sign-in/sign-up#8907
anagstef merged 13 commits into
mainfrom
carp/captcha-inline-spotlight

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 17, 2026

Copy link
Copy Markdown
Member

What

Spotlight the interactive bot-protection challenge during sign-in/sign-up.

When Turnstile escalates to an interactive "Verify you are human" challenge, the start card now brings it to the foreground — collapses + inerts the rest (social/form), keeps header/footer/passkey reachable — until solved. Invisible challenges unaffected.

How

  • CaptchaElement: new onInteractiveChange (MutationObserver on #clerk-captcha maxHeight surfaces interactive ↔ resolved) + gapless (drop out of flow while collapsed, no flex gap gutter).
  • Sign-in/up start: hoist captcha to single slot; collapse main column (display:none) + inert while interactive; passkey action relocated outside the collapsed subtree.
  • @clerk/ui patch changeset.

Test

  • pnpm vitest run in packages/ui (+ turnstile unit test in clerk-js).
  • Sandbox: cd packages/clerk-js && pnpm dev:sandbox, open http://localhost:4000/sign-up?captcha=interactive (or /sign-in). ?captcha=interactive forces the CF test sitekey + inline render + disables OAuth bypass. Click the checkbox → form snaps back on resolve, no gap during challenge. Drop the param for normal invisible captcha.

Summary by CodeRabbit

  • New Features
    • Interactive bot-protection (Turnstile) challenges now foreground during sign-in and sign-up by collapsing/inerting other fields and actions until the challenge is solved, while keeping passkey access available.
  • Bug Fixes
    • Improved inline “captcha spotlight” behavior across older/newer runtimes, and ensured invisible challenges don’t trigger spotlight expansion.
  • Tests
    • Added regression and component tests for interactive vs. invisible Turnstile flows, spotlight/inert-state behavior in sign-in/sign-up, and interactive-change callback handling in the captcha element.

Add an optional onInteractiveChange prop to CaptchaElement that fires when
Cloudflare Turnstile escalates to / resolves an interactive challenge (driven
off the existing #clerk-captcha maxHeight MutationObserver). Behavior-preserving:
no caller passes the prop yet. Also seeds reusable test-only captcha helpers and
Cloudflare test-sitekey constants, plus a turnstile.ts characterization test
pinning that invisible challenges never touch #clerk-captcha.
Consolidate the scattered <CaptchaElement/> render sites in the SignIn/SignUp
Start flows into one slot rendered as a sibling of the descriptors.main column
(direct child of Card.Content), so the widget can stay mounted while sitting
outside the soon-to-be-inert subtree. Add an opt-in `gapless` prop that drops the
collapsed element out of flow (position:absolute) so it adds no gap gutter to the
flex parent; only the start-flow slot opts in, leaving the other render sites
unchanged. Behavior-preserving (no spotlight wired yet).
When an interactive bot-protection challenge appears, collapse and inert the
descriptors.main column (social buttons, divider, form) so only the header and
the captcha widget remain — bringing the 'Verify you are human' check to the
foreground. The card restores once the challenge resolves. The passkey action is
moved out of the main column so it stays reachable during the spotlight. No focus
is moved; the captcha is a non-inert sibling reachable by keyboard. Invisible
challenges (~99%) are unaffected.
…line spotlight

Sandbox-only: when the query param is present, override the loaded environment to use
Cloudflare's test sitekey (3x…FF), render inline, and disable the OAuth bypass so the
interactive captcha spotlight can be exercised without changing instance settings or
editing src/. No effect without the param.
…xHeight 0 as 0px

The interactive check compared maxHeight against the literal '0', but browsers
serialize a reset maxHeight of '0' to '0px' when read back off style, so the
collapse read as still-interactive and the spotlight never lifted (blank card
after the challenge resolved). Compare the parsed length instead.

The test helper now collapses with '0px' to mirror the real browser value
(jsdom keeps a literal '0', which hid this bug).
…ex gap

The collapsed start-flow column used visibility:hidden;height:0, which keeps it
a flex child of Card.Content and so still contributes a gap gutter, leaving a
large empty space above the spotlighted captcha. display:none removes it from
flow entirely; the subtree stays mounted so form state is preserved.
@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d4cfe1d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@clerk/ui Minor
@clerk/clerk-js Minor
@clerk/chrome-extension Patch
@clerk/swingset Patch
@clerk/expo Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 19, 2026 2:54pm
swingset Ready Ready Preview, Comment Jun 19, 2026 2:54pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: fb42f7c3-f7bf-4005-8270-988fd660fbef

📥 Commits

Reviewing files that changed from the base of the PR and between ca463ef and d4cfe1d.

📒 Files selected for processing (2)
  • .changeset/captcha-inline-spotlight.md
  • .changeset/captcha-spotlight-compat.md
✅ Files skipped from review due to trivial changes (1)
  • .changeset/captcha-spotlight-compat.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/captcha-inline-spotlight.md

📝 Walkthrough

Walkthrough

Adds a "captcha spotlight" behavior to sign-in and sign-up flows: when Turnstile escalates to an interactive challenge, the main card column is made inert and hidden (display: none) while CaptchaElement stays visible. CaptchaElement gains onInteractiveChange and gapless props, backed by MutationObserver-driven state detecting data-cl-interactive attribute set by clerk-js. New test helpers, component tests, sandbox override, and turnstile regression test accompany the change.

Changes

Captcha Spotlight Feature

Layer / File(s) Summary
CaptchaElement props, interactive state, and gapless layout
packages/ui/src/elements/CaptchaElement.tsx
CaptchaElement accepts onInteractiveChange and gapless props; useState tracks the interactive state; MutationObserver logic detects interactivity from data-cl-interactive or maxHeight heuristic and fires the callback via ref; gapless toggles position between absolute (collapsed) and static (expanded).
Test helpers: simulate interactive/resolved transitions and test keys
packages/ui/src/test/captcha.ts
New test-only module exports simulateCaptchaInteractive/simulateCaptchaResolved (toggle dataset.clInteractive), legacy simulateCaptchaInteractiveLegacy/simulateCaptchaResolvedLegacy (use style.maxHeight), plus TEST_SITEKEYS and TEST_SECRET_KEYS constants for Turnstile test scenarios.
CaptchaElement tests: callback lifecycle and legacy compatibility
packages/ui/src/elements/__tests__/CaptchaElement.test.tsx
Comprehensive suite verifies collapsed default state, callback not firing on mount, callback firing during interactive/resolved transitions, gapless position toggling, maxHeight heuristic fallback, and legacy simulation helpers for backward compatibility.
Turnstile interactive state marking
packages/clerk-js/src/utils/captcha/turnstile.ts
When Turnstile transitions to before-interactive phase, the visible widget element is marked with dataset.clInteractive="true"; during cleanup, that flag is removed, enabling CaptchaElement's MutationObserver to detect and propagate interactive escalation.
SignInStart spotlight: inert state and placement
packages/ui/src/components/SignIn/SignInStart.tsx
SignInStart adds captchaIsInteractive state; the main column receives inert and display: none when interactive; CaptchaElement is placed outside the inert region with gapless and onInteractiveChange callback wired; passkey action kept outside inert region for accessibility during spotlight.
SignInStart spotlight tests
packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx
Tests verify CAPTCHA widget renders in email and social-only configs, form/social subtree becomes inert during interactive CAPTCHA while widget and header remain interactive, interactivity restored after resolution, and passkey action remains accessible outside inert subtree.
SignUpStart spotlight and SignUpForm cleanup
packages/ui/src/components/SignUp/SignUpStart.tsx, packages/ui/src/components/SignUp/SignUpForm.tsx
SignUpStart adds captchaIsInteractive state and makes main Flex inert/hidden when interactive; CaptchaElement embedded with gapless and callback wired. SignUpForm removes local CaptchaElement import and render, consolidating captcha into SignUpStart.
SignUpStart spotlight tests
packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx
Tests verify CAPTCHA widget renders in email and social-only configs, form/social subtree becomes inert during interactive CAPTCHA, and interactivity restored after resolution.
Sandbox override, turnstile regression, and release notes
packages/clerk-js/sandbox/app.ts, packages/clerk-js/src/utils/captcha/__tests__/turnstile.test.ts, .changeset/captcha-inline-spotlight.md, .changeset/captcha-spotlight-compat.md
Sandbox gains applyCaptchaSandboxOverrides activated by ?captcha=interactive to force interactive Turnstile config. Regression test verifies invisible Turnstile flow does not mutate #clerk-captcha (spotlight guard) and cleans up temporary containers. Changesets document @clerk/ui spotlight feature patch and @clerk/clerk-js compatibility fallback to maxHeight heuristic.

Sequence Diagram(s)

sequenceDiagram
  participant Turnstile as Cloudflare Turnstile
  participant CaptchaElement
  participant SignStart as SignInStart / SignUpStart
  participant MainColumn as Main Card Column

  Note over Turnstile,MainColumn: Invisible challenge – no spotlight
  Turnstile->>CaptchaElement: maxHeight stays '0'
  CaptchaElement->>SignStart: onInteractiveChange not called
  MainColumn-->>MainColumn: remains visible and interactive

  Note over Turnstile,MainColumn: Interactive challenge – spotlight activated
  Turnstile->>CaptchaElement: dataset.clInteractive="true"
  CaptchaElement->>CaptchaElement: MutationObserver → isInteractive=true
  CaptchaElement->>SignStart: onInteractiveChange(true)
  SignStart->>MainColumn: inert + display:none

  Note over Turnstile,MainColumn: Challenge resolved – spotlight lifted
  Turnstile->>CaptchaElement: dataset.clInteractive removed
  CaptchaElement->>CaptchaElement: MutationObserver → isInteractive=false
  CaptchaElement->>SignStart: onInteractiveChange(false)
  SignStart->>MainColumn: remove inert + display
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wobsoriano

Poem

🐇 When the bot-check pops to test your intent,
The card grows dim, all focus is sent,
To the shimmering widget, bold and bright,
While the form takes a nap — out of sight!
One challenge passed, the fields return right. 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(ui): spotlight interactive captcha during sign-in/sign-up' clearly and concisely describes the main feature being implemented—bringing interactive bot-protection challenges to the foreground during sign-in and sign-up flows by spotlighting the captcha.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alexcarpenter alexcarpenter marked this pull request as draft June 18, 2026 00:00
@alexcarpenter alexcarpenter changed the title fix(ui): inline captcha spotlight — blank card + gap feat(ui): spotlight interactive captcha during sign-in/sign-up Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-19T14:55:26.137Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 0
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d4cfe1d.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ui/src/elements/CaptchaElement.tsx (1)

16-27: ⚡ Quick win

Add an explicit return type to exported CaptchaElement.

Line 16 exports a public component without an explicit return type. Please annotate it explicitly for API clarity and consistency.

Proposed change
 export const CaptchaElement = ({
   onInteractiveChange,
   gapless,
 }: {
   onInteractiveChange?: (interactive: boolean) => void;
   /**
    * When true, the element is removed from flow (`position:absolute`) while collapsed so it adds no
    * gap gutter to a flex parent, switching to `position:static` while interactive. Opt-in so the
    * other (non-spotlight) render sites keep their current positioning.
    */
   gapless?: boolean;
-}) => {
+}): JSX.Element => {

As per coding guidelines: **/*.{ts,tsx} requires explicit return types for functions, especially public APIs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/elements/CaptchaElement.tsx` around lines 16 - 27, The
exported CaptchaElement component function lacks an explicit return type
annotation, which violates the coding guidelines requiring explicit return types
for public APIs. Add an explicit return type annotation after the closing
parenthesis of the parameter destructuring in the CaptchaElement function
signature, before the arrow function body. Use an appropriate React component
return type (such as React.ReactElement, JSX.Element, or React.FC with the props
type) to clearly specify what the component returns.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/elements/__tests__/CaptchaElement.test.tsx`:
- Line 21: The maxHeight assertions on lines 21 and 61 are comparing string
values that may be serialized differently (e.g., '0' vs '0px'), causing flaky
tests. Normalize both the maxHeight value and the expected value to extract only
the numeric portion before comparison. You can do this by parsing the
style.maxHeight to remove the 'px' suffix or converting both to numeric values,
ensuring consistent assertions regardless of CSS serialization format.

---

Nitpick comments:
In `@packages/ui/src/elements/CaptchaElement.tsx`:
- Around line 16-27: The exported CaptchaElement component function lacks an
explicit return type annotation, which violates the coding guidelines requiring
explicit return types for public APIs. Add an explicit return type annotation
after the closing parenthesis of the parameter destructuring in the
CaptchaElement function signature, before the arrow function body. Use an
appropriate React component return type (such as React.ReactElement,
JSX.Element, or React.FC with the props type) to clearly specify what the
component returns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5ebb7487-8af5-47ac-803c-a308ef76bd17

📥 Commits

Reviewing files that changed from the base of the PR and between f4488f5 and fb7c867.

📒 Files selected for processing (11)
  • .changeset/captcha-inline-spotlight.md
  • packages/clerk-js/sandbox/app.ts
  • packages/clerk-js/src/utils/captcha/__tests__/turnstile.test.ts
  • packages/ui/src/components/SignIn/SignInStart.tsx
  • packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/ui/src/components/SignUp/SignUpForm.tsx
  • packages/ui/src/components/SignUp/SignUpStart.tsx
  • packages/ui/src/components/SignUp/__tests__/SignUpStart.test.tsx
  • packages/ui/src/elements/CaptchaElement.tsx
  • packages/ui/src/elements/__tests__/CaptchaElement.test.tsx
  • packages/ui/src/test/captcha.ts
💤 Files with no reviewable changes (1)
  • packages/ui/src/components/SignUp/SignUpForm.tsx


const el = getCaptcha();
expect(el).not.toBeNull();
expect(el.style.maxHeight || '0').toBe('0');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize maxHeight assertions to avoid '0' vs '0px' false results.

Line 21 and Line 61 rely on a literal '0' comparison. Collapsed values can serialize as '0px', and Line 61 can pass even if the captcha never expanded.

Suggested fix
-    expect(el.style.maxHeight || '0').toBe('0');
+    expect(parseFloat(el.style.maxHeight || '0')).toBe(0);
...
-    await waitFor(() => expect((getCaptcha().style.maxHeight || '0') !== '0').toBe(true));
+    await waitFor(() => expect(getCaptcha().style.maxHeight).toBe('unset'));

Also applies to: 61-61

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/elements/__tests__/CaptchaElement.test.tsx` at line 21, The
maxHeight assertions on lines 21 and 61 are comparing string values that may be
serialized differently (e.g., '0' vs '0px'), causing flaky tests. Normalize both
the maxHeight value and the expected value to extract only the numeric portion
before comparison. You can do this by parsing the style.maxHeight to remove the
'px' suffix or converting both to numeric values, ensuring consistent assertions
regardless of CSS serialization format.

@anagstef anagstef left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good in general. But I have a main concern:

The implementation on the CaptchaElement is hooked on a CSS implementation detail that the before-interactive-callback is triggering (max-height value). This is fragile and may silently break in the future.

I would suggest we go with another signal, like toggling data-cl-interactive="true" on #clerk-captcha when before-interactive-callback is called and then reset its value on the finally block.

What do you think?

Comment on lines +478 to +481
<CaptchaElement
gapless
onInteractiveChange={setCaptchaIsInteractive}
/>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be breaking customers that have applied layout CSS changes on the Card.Content component, because it adds one more child to it.

The impact may be really small here, but let's make sure first.

Same applies for SignInStart.tsx

@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8907

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8907

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8907

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8907

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@8907

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8907

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8907

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8907

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8907

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8907

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8907

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8907

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8907

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8907

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8907

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8907

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8907

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8907

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8907

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8907

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8907

commit: d4cfe1d

…ta-cl-interactive

When the attribute is absent (old clerk-js in the wild), infer interactive
state from maxHeight so the spotlight still fires. New clerk-js sets the
attribute alongside the style; because MutationObserver delivers all
mutations from the same microtask before the callback runs, the dataset
check is always current and new+new uses the clean attribute contract.
@anagstef

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @anagstef - the snapshot version command generated the following package versions:

Package Version
@clerk/astro 3.4.5-snapshot.v20260619083211
@clerk/backend 3.8.1-snapshot.v20260619083211
@clerk/chrome-extension 3.1.39-snapshot.v20260619083211
@clerk/clerk-js 6.18.1-snapshot.v20260619083211
@clerk/eslint-plugin 0.1.1-snapshot.v20260619083211
@clerk/expo 3.4.7-snapshot.v20260619083211
@clerk/expo-passkeys 1.1.7-snapshot.v20260619083211
@clerk/express 2.1.29-snapshot.v20260619083211
@clerk/fastify 3.1.39-snapshot.v20260619083211
@clerk/headless 0.0.1-snapshot.v20260619083211
@clerk/hono 0.1.39-snapshot.v20260619083211
@clerk/localizations 4.9.1-snapshot.v20260619083211
@clerk/msw 0.0.37-snapshot.v20260619083211
@clerk/nextjs 7.5.6-snapshot.v20260619083211
@clerk/nuxt 2.6.5-snapshot.v20260619083211
@clerk/react 6.10.3-snapshot.v20260619083211
@clerk/react-router 3.4.6-snapshot.v20260619083211
@clerk/shared 4.19.1-snapshot.v20260619083211
@clerk/swingset 0.0.5-snapshot.v20260619083211
@clerk/tanstack-react-start 1.4.6-snapshot.v20260619083211
@clerk/testing 2.1.4-snapshot.v20260619083211
@clerk/ui 1.18.1-snapshot.v20260619083211
@clerk/upgrade 2.0.5-snapshot.v20260619083211
@clerk/vue 2.4.5-snapshot.v20260619083211

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.4.5-snapshot.v20260619083211 --save-exact

@clerk/backend

npm i @clerk/backend@3.8.1-snapshot.v20260619083211 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.39-snapshot.v20260619083211 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.18.1-snapshot.v20260619083211 --save-exact

@clerk/eslint-plugin

npm i @clerk/eslint-plugin@0.1.1-snapshot.v20260619083211 --save-exact

@clerk/expo

npm i @clerk/expo@3.4.7-snapshot.v20260619083211 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.7-snapshot.v20260619083211 --save-exact

@clerk/express

npm i @clerk/express@2.1.29-snapshot.v20260619083211 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.1.39-snapshot.v20260619083211 --save-exact

@clerk/headless

npm i @clerk/headless@0.0.1-snapshot.v20260619083211 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.39-snapshot.v20260619083211 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.9.1-snapshot.v20260619083211 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.37-snapshot.v20260619083211 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.5.6-snapshot.v20260619083211 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.6.5-snapshot.v20260619083211 --save-exact

@clerk/react

npm i @clerk/react@6.10.3-snapshot.v20260619083211 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.4.6-snapshot.v20260619083211 --save-exact

@clerk/shared

npm i @clerk/shared@4.19.1-snapshot.v20260619083211 --save-exact

@clerk/swingset

npm i @clerk/swingset@0.0.5-snapshot.v20260619083211 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.4.6-snapshot.v20260619083211 --save-exact

@clerk/testing

npm i @clerk/testing@2.1.4-snapshot.v20260619083211 --save-exact

@clerk/ui

npm i @clerk/ui@1.18.1-snapshot.v20260619083211 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.5-snapshot.v20260619083211 --save-exact

@clerk/vue

npm i @clerk/vue@2.4.5-snapshot.v20260619083211 --save-exact

@anagstef anagstef marked this pull request as ready for review June 19, 2026 14:04
The spotlight is a new feature (new CaptchaElement props and clerk-js interactive signal), so both packages warrant a minor release.
@anagstef anagstef merged commit 9e1d849 into main Jun 19, 2026
49 checks passed
@anagstef anagstef deleted the carp/captcha-inline-spotlight branch June 19, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants