Clerk
Drop-in authentication with prebuilt sign-in, MFA and user management.
The rule
This is the whole text, exactly as your agent receives it. Nothing is held back for the paid tier.
Reading auth state
const { isLoaded, isSignedIn, userId } = useAuth();
if (!isLoaded) return <Splash />;Never treat "not loaded" as "signed out". Doing so flashes the sign-in screen at every launch for users who are already authenticated, and can redirect them out of a deep link.
Tokens
tokenCachemust be backed byexpo-secure-store. A session token is a credential;AsyncStorageis plaintext.- Send
await getToken()as a bearer token to your backend. - The backend verifies the token. Never trust a user id from a request body or a query parameter.
- The Clerk secret key is server-side only, never in the app.
User data
- Store Clerk's
userIdon your own rows to join user data. - Handle the
user.deletedwebhook so removing a user removes their data. Orphaned personal data is a compliance problem, not just untidiness.
Environments
- Separate Clerk applications for development and production. Sharing one puts test accounts in your production user pool.
Never
- Never log a session token or JWT.
- Never gate access purely on client state — the backend re-verifies.
- Never render user-specific UI before
isLoaded.
6 formats, one per tool
Each tab is the file that tool actually reads, at the path it actually looks in. Knowing where each one looks is most of the work of supporting it.
---
description: Clerk authentication conventions
globs: ["src/features/auth/**", "src/services/**", "app/**"]
alwaysApply: false
---
# Clerk
## Reading auth state
```tsx
const { isLoaded, isSignedIn, userId } = useAuth();
if (!isLoaded) return <Splash />;
```
Never treat "not loaded" as "signed out". Doing so flashes the sign-in screen at
every launch for users who are already authenticated, and can redirect them out
of a deep link.
## Tokens
- `tokenCache` must be backed by `expo-secure-store`. A session token is a
credential; `AsyncStorage` is plaintext.
- Send `await getToken()` as a bearer token to your backend.
- The backend **verifies** the token. Never trust a user id from a request body
or a query parameter.
- The Clerk secret key is server-side only, never in the app.
## User data
- Store Clerk's `userId` on your own rows to join user data.
- Handle the `user.deleted` webhook so removing a user removes their data.
Orphaned personal data is a compliance problem, not just untidiness.
## Environments
- Separate Clerk applications for development and production. Sharing one puts
test accounts in your production user pool.
## Never
- Never log a session token or JWT.
- Never gate access purely on client state — the backend re-verifies.
- Never render user-specific UI before `isLoaded`.
Hand-written by the module author, frontmatter and all. It is the source the four derived formats are rendered from, so a correction lands here first.
What else this module writes
The rule is one file of several. Selecting Clerk contributes all of this too — merged with every other module you pick, with conflicts resolved rather than duplicated.
{{envPrefix}}CLERK_PUBLISHABLE_KEYrequiredPublishable key for this environment's Clerk instance.CLERK_SECRET_KEYoptionalServer-side key for backend verification and the admin API. Never ship in the app.CLERK_WEBHOOK_SECREToptionalVerifies incoming webhooks, including `user.deleted`. Server-side only.Advisory history
Every time this rule turned out to be wrong, and what we did about it.
This rule has been accurate since it was published. That is a fact about the rule, not a promise about the future — which is the whole reason this section exists.
Pro tells you the day a correction lands that affects a repo you actually have.
See what Pro adds →Rules people add alongside this one
The wizard picks the rest of the stack with you, writes all 6 formats, and leaves a manifest so check can tell you when any of it drifts.