feat(react): Implement waitlist hook with signal primitives
Description
This PR introduces the useWaitlist() hook, a new signal-backed hook in @clerk/react that enables developers to build custom waitlist UIs with reactive state management. It follows the pattern of useSignIn() and useSignUp(), exposing waitlist (resource), errors, and fetchStatus.
The Waitlist resource in @clerk/clerk-js has been integrated with the signal system, providing a WaitlistFuture instance for reactive updates. The State class now manages a lazily initialized, singleton Waitlist instance, ensuring its independence from the Client resource and proper signal propagation.
An integration test has been added to validate the hook's functionality, including joining the waitlist, error handling, and loading states.
To test the changes, run the new integration test:
pnpm playwright test integration/tests/custom-flows/waitlist.test.ts
Checklist
- [x]
pnpm testruns as expected. - [x]
pnpm buildruns as expected. - [x] (If applicable) JSDoc comments have been added or updated for any package exports
- [ ] (If applicable) Documentation has been updated
Type of change
- [ ] 🐛 Bug fix
- [x] 🌟 New feature
- [ ] 🔨 Breaking change
- [ ] 📖 Refactoring / dependency upgrade / documentation
- [ ] other:
Summary by CodeRabbit
-
New Features
- Added Waitlist functionality with a new
useWaitlistReact hook for accessing waitlist state and join operations - Introduced Waitlist component for email signup with validation and loading states
- Added ability to join the waitlist with email address submission and error handling
- Added Waitlist functionality with a new
-
Bug Fixes
- Fixed environment configuration typo
-
Tests
- Added comprehensive integration tests for waitlist feature
-
Chores
- Removed experimental export entry from package exports
✏️ Tip: You can customize this high-level summary in your review settings.
Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents
⚠️ No Changeset found
Latest commit: 406f16a67ec2390c484cdf9d322e35bdfcd20542
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| clerk-js-sandbox | Preview | Comment | Dec 8, 2025 7:37pm |
Walkthrough
This pull request introduces a new waitlist feature across the Clerk SDK, including core resource and signal management, React hooks, state proxy integration, type definitions, and test utilities. Additionally, it fixes a typo in environment preset naming and removes an experimental package export.
Changes
| Cohort / File(s) | Summary |
|---|---|
Environment & Preset Configuration integration/presets/envs.ts, integration/tests/waitlist-mode.test.ts |
Fixed typo: renamed environment preset constant and ID from withWaitlistdMode to withWaitlistMode; updated test reference to use corrected preset name. |
React Template Routes & Components integration/templates/custom-flows-react-vite/src/main.tsx, integration/templates/custom-flows-react-vite/src/routes/Waitlist.tsx |
Added Waitlist route and component. Route imports and renders new Waitlist component at /waitlist path. Component uses useWaitlist hook, renders email input form with validation, displays success state after joining, and includes navigation links. |
Test Infrastructure integration/testUtils/index.ts, integration/testUtils/waitlistService.ts, integration/tests/custom-flows/waitlist.test.ts |
Added WaitlistService utility for clearing waitlist entries by email via Clerk client. Integrated service into test utils. Added Playwright test suite covering waitlist join flow with valid/invalid email and loading states. |
Core Waitlist Resource packages/clerk-js/src/core/resources/Waitlist.ts |
Extended Waitlist constructor to accept nullable data, added instance method join() that delegates to static method via runAsyncResourceTask, emits resource:update event on hydration. |
Signal Management packages/clerk-js/src/core/signals.ts |
Introduced waitlist signal set: waitlistResourceSignal, waitlistErrorSignal, waitlistFetchSignal, and computed waitlistComputedSignal. Added helper function errorsToWaitlistErrors() for error transformation. |
State Management packages/clerk-js/src/core/state.ts |
Added four public signal properties (waitlistResourceSignal, waitlistErrorSignal, waitlistFetchSignal, waitlistSignal), created internal Waitlist instance, exposed via __internal_waitlist getter, and wired resource event handlers for error/update/fetch flows. |
React Hooks packages/react/src/hooks/index.ts, packages/react/src/hooks/useClerkSignal.ts |
Added useWaitlist() hook exported from barrel. Extended useClerkSignal() overloads and implementation to support 'waitlist' signal type with WaitlistSignalValue. |
State Proxy packages/react/src/stateProxy.ts |
Added waitlistSignal() accessor and __internal_waitlist getter. Implemented buildWaitlistProxy() to construct waitlist sub-proxy with errors, fetchStatus, and join/reload operations. |
Type Definitions packages/shared/src/types/clerk.ts, packages/shared/src/types/state.ts, packages/shared/src/types/waitlist.ts |
Moved JoinWaitlistParams from clerk.ts to waitlist.ts. Added WaitlistFields, WaitlistErrors, WaitlistSignalValue, NullableWaitlistSignal, WaitlistSignal types. Extended WaitlistResource with readonly properties and join() method. Extended State interface with waitlistSignal and __internal_waitlist. |
Documentation & Configuration packages/localizations/README.md, packages/tanstack-react-start/package.json |
Added extra blank line in localization README. Removed "./experimental" export entry from package.json exports map. |
Sequence Diagram
sequenceDiagram
participant User as User
participant WaitlistUI as Waitlist Component
participant Hook as useWaitlist Hook
participant ClerkState as Clerk State
participant WaitlistResource as Waitlist Resource
participant API as Clerk Backend
User->>WaitlistUI: Submit email
WaitlistUI->>Hook: Call useWaitlist()
activate Hook
Hook->>ClerkState: Subscribe to waitlistSignal
deactivate Hook
WaitlistUI->>WaitlistResource: Call waitlist.join({ emailAddress })
activate WaitlistResource
WaitlistResource->>ClerkState: Emit waitlistFetchSignal ('fetching')
activate ClerkState
deactivate WaitlistResource
WaitlistResource->>API: POST /waitlist/join
activate API
API-->>WaitlistResource: WaitlistResource response
deactivate API
WaitlistResource->>ClerkState: Emit waitlistResourceSignal (updated)
WaitlistResource->>ClerkState: Emit waitlistFetchSignal ('idle')
deactivate ClerkState
ClerkState-->>Hook: Signal updated
Hook-->>WaitlistUI: Re-render with success state
WaitlistUI-->>User: Show "Successfully joined!"
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
-
Signal and state management wiring: Ensure
waitlistResourceSignal,waitlistErrorSignal, andwaitlistFetchSignalare correctly composed in the computed signal and properly exposed through State class public members. -
Type definitions coordination: Verify
JoinWaitlistParams,WaitlistErrors,WaitlistSignalValue, andWaitlistSignalare consistently used across clerk.ts, state.ts, and waitlist.ts type files. -
StateProxy implementation: Review
buildWaitlistProxy()logic for consistency with existing signIn/signUp proxy patterns; ensure error handling and fetch status management align with established patterns. -
React hook overloads: Confirm
useClerkSignal()overload ordering and switch-case logic handle the new 'waitlist' signal correctly without breaking existing signIn/signUp cases. -
Error transformation pipeline: Verify
errorsToWaitlistErrors()correctly maps ClerkError to WaitlistErrors structure matching WaitlistFields. -
Resource lifecycle: Check Waitlist constructor nullable data handling, join method delegation via
runAsyncResourceTask, and event emissions don't introduce race conditions or memory leaks.
Poem
🐰 A waitlist arrives in the Clerk today,
With signals and hooks along the way!
New types, new states, new flows so clean,
The hopping begins—join now, we've seen!
From bunny-approved types to React's embrace,
This feature hops into place with grace! 🎉
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title accurately describes the main change—implementing a waitlist hook using signal primitives in the React package—which is the primary focus across the changeset. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
cursor/implement-waitlist-hook-with-signal-primitives-3ad1
Comment @coderabbitai help to get the list of available commands and usage tips.
@clerk/agent-toolkit
npm i https://pkg.pr.new/@clerk/agent-toolkit@7097
@clerk/astro
npm i https://pkg.pr.new/@clerk/astro@7097
@clerk/backend
npm i https://pkg.pr.new/@clerk/backend@7097
@clerk/chrome-extension
npm i https://pkg.pr.new/@clerk/chrome-extension@7097
@clerk/clerk-js
npm i https://pkg.pr.new/@clerk/clerk-js@7097
@clerk/dev-cli
npm i https://pkg.pr.new/@clerk/dev-cli@7097
@clerk/expo
npm i https://pkg.pr.new/@clerk/expo@7097
@clerk/expo-passkeys
npm i https://pkg.pr.new/@clerk/expo-passkeys@7097
@clerk/express
npm i https://pkg.pr.new/@clerk/express@7097
@clerk/fastify
npm i https://pkg.pr.new/@clerk/fastify@7097
@clerk/localizations
npm i https://pkg.pr.new/@clerk/localizations@7097
@clerk/nextjs
npm i https://pkg.pr.new/@clerk/nextjs@7097
@clerk/nuxt
npm i https://pkg.pr.new/@clerk/nuxt@7097
@clerk/react
npm i https://pkg.pr.new/@clerk/react@7097
@clerk/react-router
npm i https://pkg.pr.new/@clerk/react-router@7097
@clerk/shared
npm i https://pkg.pr.new/@clerk/shared@7097
@clerk/tanstack-react-start
npm i https://pkg.pr.new/@clerk/tanstack-react-start@7097
@clerk/testing
npm i https://pkg.pr.new/@clerk/testing@7097
@clerk/ui
npm i https://pkg.pr.new/@clerk/ui@7097
@clerk/upgrade
npm i https://pkg.pr.new/@clerk/upgrade@7097
@clerk/vue
npm i https://pkg.pr.new/@clerk/vue@7097
commit: 406f16a