create-aron-app 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/package.json +5 -2
  2. package/templates/_base/.cursor/agents/skills/clerk/SKILL.md +89 -0
  3. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/SKILL.md +142 -0
  4. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +30 -0
  5. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +88 -0
  6. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
  7. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
  8. package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +14 -0
  9. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/SKILL.md +157 -0
  10. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +224 -0
  11. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +190 -0
  12. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +314 -0
  13. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +259 -0
  14. package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +125 -0
  15. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +94 -0
  16. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +50 -0
  17. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +56 -0
  18. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +68 -0
  19. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +56 -0
  20. package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +104 -0
  21. package/templates/_base/.cursor/agents/skills/clerk/clerk-webhooks/SKILL.md +131 -0
  22. package/templates/_base/.cursor/agents/skills/shadcn/SKILL.md +241 -0
  23. package/templates/_base/.cursor/agents/skills/shadcn/agents/openai.yml +5 -0
  24. package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn-small.png +0 -0
  25. package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn.png +0 -0
  26. package/templates/_base/.cursor/agents/skills/shadcn/cli.md +257 -0
  27. package/templates/_base/.cursor/agents/skills/shadcn/customization.md +202 -0
  28. package/templates/_base/.cursor/agents/skills/shadcn/evals/evals.json +47 -0
  29. package/templates/_base/.cursor/agents/skills/shadcn/mcp.md +94 -0
  30. package/templates/_base/.cursor/agents/skills/shadcn/rules/base-vs-radix.md +306 -0
  31. package/templates/_base/.cursor/agents/skills/shadcn/rules/composition.md +195 -0
  32. package/templates/_base/.cursor/agents/skills/shadcn/rules/forms.md +192 -0
  33. package/templates/_base/.cursor/agents/skills/shadcn/rules/icons.md +101 -0
  34. package/templates/_base/.cursor/agents/skills/shadcn/rules/styling.md +162 -0
  35. package/templates/_base/.cursor/commands/builder.md +0 -0
  36. package/templates/_base/.cursor/commands/pr.md +7 -0
  37. package/templates/_base/.cursor/rules/api_architecture.mdc +268 -0
  38. package/templates/_base/.cursor/rules/coding_standards.mdc +64 -0
  39. package/templates/_base/.cursor/rules/convex_rules.mdc +675 -0
  40. package/templates/_base/.cursor/rules/frontend_rules.mdc +268 -0
  41. package/templates/_base/.env.convex.example +3 -0
  42. package/templates/_base/.github/workflows/ci.yml +29 -0
  43. package/templates/_base/.nvmrc +1 -0
  44. package/templates/_base/.vscode/settings.json +9 -0
  45. package/templates/_base/apps/api/auth.config.ts +18 -0
  46. package/templates/_base/apps/api/functions.ts +99 -0
  47. package/templates/_base/apps/api/project.json +22 -0
  48. package/templates/_base/apps/api/schema.ts +11 -0
  49. package/templates/_base/apps/api/todos/crud.ts +81 -0
  50. package/templates/_base/apps/api/todos/schema.ts +11 -0
  51. package/templates/_base/apps/api/todos/types.ts +22 -0
  52. package/templates/_base/apps/api/tsconfig.json +23 -0
  53. package/templates/_base/apps/api/types.ts +16 -0
  54. package/templates/_base/biome.json +114 -0
  55. package/templates/_base/convex.json +4 -0
  56. package/templates/_base/emails/project.json +16 -0
  57. package/templates/_base/emails/tsconfig.json +5 -0
  58. package/templates/_base/emails/welcome_email.tsx +53 -0
  59. package/templates/_base/nx.json +29 -0
  60. package/templates/_base/package.json +73 -0
  61. package/templates/_base/scripts/sync_convex_env.ts +63 -0
  62. package/templates/_base/shared/assets/image.d.ts +4 -0
  63. package/templates/_base/shared/assets/src/styles/global.css +73 -0
  64. package/templates/_base/shared/assets/tsconfig.json +5 -0
  65. package/templates/_base/shared/ui/src/base/alert_dialog.tsx +139 -0
  66. package/templates/_base/shared/ui/src/base/badge.tsx +33 -0
  67. package/templates/_base/shared/ui/src/base/basic_data_table.tsx +61 -0
  68. package/templates/_base/shared/ui/src/base/button.tsx +69 -0
  69. package/templates/_base/shared/ui/src/base/button_group.tsx +82 -0
  70. package/templates/_base/shared/ui/src/base/card.tsx +79 -0
  71. package/templates/_base/shared/ui/src/base/checkbox.tsx +26 -0
  72. package/templates/_base/shared/ui/src/base/command.tsx +165 -0
  73. package/templates/_base/shared/ui/src/base/dialog.tsx +129 -0
  74. package/templates/_base/shared/ui/src/base/dropdown_menu.tsx +232 -0
  75. package/templates/_base/shared/ui/src/base/form.tsx +161 -0
  76. package/templates/_base/shared/ui/src/base/input.tsx +129 -0
  77. package/templates/_base/shared/ui/src/base/label.tsx +19 -0
  78. package/templates/_base/shared/ui/src/base/popover.tsx +46 -0
  79. package/templates/_base/shared/ui/src/base/radio_group.tsx +49 -0
  80. package/templates/_base/shared/ui/src/base/resizable.tsx +55 -0
  81. package/templates/_base/shared/ui/src/base/scroll_area.tsx +44 -0
  82. package/templates/_base/shared/ui/src/base/select.tsx +151 -0
  83. package/templates/_base/shared/ui/src/base/separator.tsx +32 -0
  84. package/templates/_base/shared/ui/src/base/sheet.tsx +130 -0
  85. package/templates/_base/shared/ui/src/base/side_bar.tsx +688 -0
  86. package/templates/_base/shared/ui/src/base/skeleton.tsx +7 -0
  87. package/templates/_base/shared/ui/src/base/spinner.tsx +20 -0
  88. package/templates/_base/shared/ui/src/base/switch.tsx +27 -0
  89. package/templates/_base/shared/ui/src/base/table.tsx +91 -0
  90. package/templates/_base/shared/ui/src/base/text_area.tsx +21 -0
  91. package/templates/_base/shared/ui/src/base/tooltip.tsx +31 -0
  92. package/templates/_base/shared/ui/src/base/utils.ts +17 -0
  93. package/templates/_base/shared/ui/src/hooks/use_keyboard_press.tsx +48 -0
  94. package/templates/_base/shared/ui/src/hooks/use_keyboard_release.tsx +48 -0
  95. package/templates/_base/shared/ui/src/hooks/use_mobile.tsx +25 -0
  96. package/templates/_base/shared/ui/src/hooks/use_mouse_click.tsx +44 -0
  97. package/templates/_base/shared/ui/src/hooks/use_mouse_location.tsx +55 -0
  98. package/templates/_base/shared/ui/src/hooks/use_outside_click.tsx +29 -0
  99. package/templates/_base/shared/ui/src/hooks/use_query_params.tsx +33 -0
  100. package/templates/_base/shared/ui/tsconfig.json +8 -0
  101. package/templates/_base/shared/utils/src/convex.ts +3 -0
  102. package/templates/_base/shared/utils/src/time.ts +12 -0
  103. package/templates/_base/shared/utils/tsconfig.json +5 -0
  104. package/templates/_base/skills-lock.json +35 -0
  105. package/templates/_base/tsconfig.base.json +34 -0
  106. package/templates/nextjs/.env.example +8 -0
  107. package/templates/nextjs/index.d.ts +6 -0
  108. package/templates/nextjs/next-env.d.ts +5 -0
  109. package/templates/nextjs/next.config.js +22 -0
  110. package/templates/nextjs/postcss.config.js +17 -0
  111. package/templates/nextjs/project.json +22 -0
  112. package/templates/nextjs/src/app/(auth)/layout.tsx +21 -0
  113. package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +22 -0
  114. package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +15 -0
  115. package/templates/nextjs/src/app/(dashboard)/layout.tsx +27 -0
  116. package/templates/nextjs/src/app/(dashboard)/page.tsx +5 -0
  117. package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +23 -0
  118. package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +16 -0
  119. package/templates/nextjs/src/app/app.css +3 -0
  120. package/templates/nextjs/src/app/layout.tsx +26 -0
  121. package/templates/nextjs/src/convex.ts +11 -0
  122. package/templates/nextjs/src/middleware.ts +18 -0
  123. package/templates/nextjs/src/providers/convex_provider.tsx +44 -0
  124. package/templates/nextjs/src/surfaces/home_surface.tsx +22 -0
  125. package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +97 -0
  126. package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +107 -0
  127. package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +90 -0
  128. package/templates/nextjs/src/ui/sidebar/nav_link.tsx +36 -0
  129. package/templates/nextjs/src/ui/sidebar/sidebar.tsx +125 -0
  130. package/templates/nextjs/src/utils/font.ts +9 -0
  131. package/templates/nextjs/tsconfig.json +42 -0
  132. package/templates/react-router/.env.example +8 -0
  133. package/templates/react-router/postcss.config.js +15 -0
  134. package/templates/react-router/project.json +23 -0
  135. package/templates/react-router/public/favicon.ico +0 -0
  136. package/templates/react-router/react-router.config.ts +9 -0
  137. package/templates/react-router/src/app.css +3 -0
  138. package/templates/react-router/src/components/error_boundary.tsx +33 -0
  139. package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +76 -0
  140. package/templates/react-router/src/layouts/sidebar/sidebar_aside/user_menu.tsx +36 -0
  141. package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +22 -0
  142. package/templates/react-router/src/providers/api_auth_provider.tsx +38 -0
  143. package/templates/react-router/src/root.tsx +37 -0
  144. package/templates/react-router/src/routes/auth/layout.tsx +13 -0
  145. package/templates/react-router/src/routes/auth/sign-in.tsx +13 -0
  146. package/templates/react-router/src/routes/index.tsx +9 -0
  147. package/templates/react-router/src/routes/layout.tsx +26 -0
  148. package/templates/react-router/src/routes/todos/[id].tsx +22 -0
  149. package/templates/react-router/src/routes/todos/index.tsx +13 -0
  150. package/templates/react-router/src/routes.ts +12 -0
  151. package/templates/react-router/src/surfaces/home_surface.tsx +20 -0
  152. package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +87 -0
  153. package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +102 -0
  154. package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +81 -0
  155. package/templates/react-router/tsconfig.json +20 -0
  156. package/templates/react-router/vite.config.ts +40 -0
@@ -0,0 +1,259 @@
1
+ # Custom Sign-Up Flow
2
+
3
+ Build a custom sign-up experience using the `useSignUp()` hook.
4
+
5
+ ## Hook API
6
+
7
+ ```typescript
8
+ import { useSignUp } from '@clerk/nextjs' // or @clerk/react, @clerk/expo
9
+
10
+ const { signUp, errors, fetchStatus } = useSignUp()
11
+ ```
12
+
13
+ | Property | Type | Description |
14
+ |----------|------|-------------|
15
+ | `signUp` | `SignUpFuture` | Sign-up object with namespaced methods |
16
+ | `errors` | `Errors<SignUpFields>` | Structured error object |
17
+ | `fetchStatus` | `'idle' \| 'fetching'` | Network request status |
18
+
19
+ ## Sign-Up Methods
20
+
21
+ ### Password (Email/Password)
22
+
23
+ ```typescript
24
+ const { error } = await signUp.password({
25
+ emailAddress: 'user@example.com',
26
+ password: 'securePassword123',
27
+ firstName: 'Jane', // optional
28
+ lastName: 'Doe', // optional
29
+ })
30
+ ```
31
+
32
+ ### SSO (OAuth)
33
+
34
+ ```typescript
35
+ const { error } = await signUp.sso({
36
+ strategy: 'oauth_google', // or 'oauth_github', etc.
37
+ redirectUrl: '/dashboard', // where to go after SSO completes
38
+ redirectCallbackUrl: '/sso-callback', // intermediate callback route
39
+ })
40
+ ```
41
+
42
+ ### Web3
43
+
44
+ ```typescript
45
+ const { error } = await signUp.web3({ strategy: 'web3_solana_signature' })
46
+ ```
47
+
48
+ ### Update (add fields to existing sign-up)
49
+
50
+ Use `update()` to add optional fields (name, metadata, legal acceptance, locale) to an existing sign-up before finalization.
51
+
52
+ ```typescript
53
+ const { error } = await signUp.update({
54
+ firstName: 'Jane',
55
+ lastName: 'Doe',
56
+ unsafeMetadata: { referralSource: 'twitter' },
57
+ legalAccepted: true,
58
+ })
59
+ ```
60
+
61
+ ## Email / Phone Verification
62
+
63
+ After creating a sign-up, verify the user's email or phone:
64
+
65
+ ### Email Code
66
+
67
+ ```typescript
68
+ // Send verification code
69
+ const { error } = await signUp.verifications.sendEmailCode()
70
+
71
+ // Verify the code
72
+ const { error } = await signUp.verifications.verifyEmailCode({ code: '123456' })
73
+ ```
74
+
75
+ ### Phone Code
76
+
77
+ ```typescript
78
+ // Send verification code
79
+ const { error } = await signUp.verifications.sendPhoneCode()
80
+
81
+ // Verify the code
82
+ const { error } = await signUp.verifications.verifyPhoneCode({ code: '123456' })
83
+ ```
84
+
85
+ ### Email Link
86
+
87
+ ```typescript
88
+ // verificationUrl: where the user lands after clicking the email link (relative or absolute)
89
+ const { error } = await signUp.verifications.sendEmailLink({ verificationUrl: '/verify' })
90
+ // User clicks the link in their email to verify
91
+ ```
92
+
93
+ ## Finalizing Sign-Up
94
+
95
+ After successful sign-up and verification, call `finalize()` to activate the session:
96
+
97
+ ```typescript
98
+ await signUp.finalize({
99
+ navigate: async ({ session, decorateUrl }) => {
100
+ const destination = session.currentTask
101
+ ? `/sign-up/tasks/${session.currentTask.key}`
102
+ : '/'
103
+ const url = decorateUrl(destination)
104
+ // decorateUrl may return an absolute URL for Safari ITP
105
+ if (url.startsWith('http')) {
106
+ window.location.href = url
107
+ } else {
108
+ router.push(url)
109
+ }
110
+ },
111
+ })
112
+ ```
113
+
114
+ ### Transferable Sign-Ups
115
+
116
+ If `signUp.isTransferable` is `true`, the identifier matches an existing user and the sign-up should be transferred to a sign-in flow. This involves coordinating between sign-up and sign-in resources. See the [transferable sign-up docs](https://clerk.com/docs/custom-flows/overview) for the full implementation.
117
+
118
+ ### Reset State
119
+
120
+ Clear local sign-up state and start over:
121
+
122
+ ```typescript
123
+ signUp.reset()
124
+ ```
125
+
126
+ ## Error Handling
127
+
128
+ All methods return `Promise<{ error: ClerkError | null }>`. Errors are also available reactively on the hook:
129
+
130
+ ```typescript
131
+ const { signUp, errors } = useSignUp()
132
+
133
+ // Field-level errors
134
+ errors?.fields?.emailAddress // { code, message, longMessage? }
135
+ errors?.fields?.password // { code, message, longMessage? }
136
+ errors?.fields?.firstName // { code, message, longMessage? }
137
+ errors?.fields?.lastName // { code, message, longMessage? }
138
+ errors?.fields?.phoneNumber // { code, message, longMessage? }
139
+ errors?.fields?.username // { code, message, longMessage? }
140
+ errors?.fields?.code // { code, message, longMessage? }
141
+
142
+ // Global errors
143
+ errors?.global // ClerkGlobalHookError[] | null
144
+
145
+ // Raw error array
146
+ errors?.raw // ClerkError[] | null
147
+ ```
148
+
149
+ ## Complete Example: Phone OTP Sign-Up
150
+
151
+ From [the docs](https://clerk.com/docs/guides/development/custom-flows/authentication/email-sms-otp). Uses phone OTP with inline comments for adapting to email OTP.
152
+
153
+ ```tsx
154
+ 'use client'
155
+
156
+ import * as React from 'react'
157
+ import { useAuth, useSignUp } from '@clerk/nextjs'
158
+ import { useRouter } from 'next/navigation'
159
+
160
+ export default function SignUpPage() {
161
+ const { signUp, errors, fetchStatus } = useSignUp()
162
+ const { isSignedIn } = useAuth()
163
+ const router = useRouter()
164
+
165
+ const handleSubmit = async (formData: FormData) => {
166
+ // For email OTP: collect the email address instead of the phone number
167
+ const phoneNumber = formData.get('phoneNumber') as string
168
+
169
+ // For email OTP: change create({ phoneNumber }) to create({ emailAddress })
170
+ const error = await signUp.create({ phoneNumber })
171
+
172
+ // For email OTP: change sendPhoneCode() to sendEmailCode()
173
+ if (!error) await signUp.verifications.sendPhoneCode()
174
+ }
175
+
176
+ const handleVerify = async (formData: FormData) => {
177
+ const code = formData.get('code') as string
178
+
179
+ // For email OTP: change verifyPhoneCode() to verifyEmailCode()
180
+ await signUp.verifications.verifyPhoneCode({ code })
181
+
182
+ if (signUp.status === 'complete') {
183
+ await signUp.finalize({
184
+ navigate: ({ session, decorateUrl }) => {
185
+ if (session?.currentTask) {
186
+ // Handle pending session tasks
187
+ // See https://clerk.com/docs/guides/development/custom-flows/authentication/session-tasks
188
+ console.log(session?.currentTask)
189
+ return
190
+ }
191
+
192
+ const url = decorateUrl('/')
193
+ if (url.startsWith('http')) {
194
+ window.location.href = url
195
+ } else {
196
+ router.push(url)
197
+ }
198
+ },
199
+ })
200
+ }
201
+ }
202
+
203
+ if (signUp.status === 'complete' || isSignedIn) {
204
+ return null
205
+ }
206
+
207
+ if (
208
+ signUp.status === 'missing_requirements' &&
209
+ // For email OTP: check for phone_number instead of email_address
210
+ signUp.unverifiedFields.includes('phone_number') &&
211
+ signUp.missingFields.length === 0
212
+ ) {
213
+ return (
214
+ <>
215
+ <h1>Verify your account</h1>
216
+ <form action={handleVerify}>
217
+ <div>
218
+ <label htmlFor="code">Code</label>
219
+ <input id="code" name="code" type="text" />
220
+ </div>
221
+ {errors.fields.code && <p>{errors.fields.code.message}</p>}
222
+ <button type="submit" disabled={fetchStatus === 'fetching'}>
223
+ Verify
224
+ </button>
225
+ </form>
226
+ {/* For email OTP: change sendPhoneCode() to sendEmailCode() */}
227
+ <button onClick={() => signUp.verifications.sendPhoneCode()}>I need a new code</button>
228
+ </>
229
+ )
230
+ }
231
+
232
+ return (
233
+ <>
234
+ <h1>Sign up</h1>
235
+ <form action={handleSubmit}>
236
+ {/* For email OTP: collect the emailAddress instead */}
237
+ <div>
238
+ <label htmlFor="phoneNumber">Phone number</label>
239
+ <input id="phoneNumber" name="phoneNumber" type="tel" />
240
+ {errors.fields.phoneNumber && <p>{errors.fields.phoneNumber.message}</p>}
241
+ </div>
242
+ <button type="submit" disabled={fetchStatus === 'fetching'}>
243
+ Continue
244
+ </button>
245
+ </form>
246
+ {errors && <p>{JSON.stringify(errors, null, 2)}</p>}
247
+
248
+ {/* Required for sign-up flows. Clerk's bot sign-up protection is enabled by default */}
249
+ <div id="clerk-captcha" />
250
+ </>
251
+ )
252
+ }
253
+ ```
254
+
255
+ ## Docs
256
+
257
+ - [Custom sign-up flow](https://clerk.com/docs/custom-flows/overview)
258
+ - [Email/phone OTP custom flow](https://clerk.com/docs/guides/development/custom-flows/authentication/email-sms-otp)
259
+ - [useSignUp() reference](https://clerk.com/docs/references/react/use-sign-up)
@@ -0,0 +1,125 @@
1
+ # `<Show>` Component
2
+
3
+ The `<Show>` component conditionally renders content based on authentication state, roles, permissions, billing plans, and features.
4
+
5
+ > **Core 2 ONLY (skip if current SDK):** The `<Show>` component does not exist in Core 2. Use `<SignedIn>`, `<SignedOut>`, and `<Protect>` instead. See migration table below.
6
+
7
+ ## Import
8
+
9
+ ```typescript
10
+ import { Show } from '@clerk/nextjs' // Next.js
11
+ import { Show } from '@clerk/react' // React
12
+ import { Show } from '@clerk/react-router' // React Router
13
+ import { Show } from '@clerk/expo' // Expo
14
+ ```
15
+
16
+ ## Props
17
+
18
+ | Prop | Type | Description |
19
+ |------|------|-------------|
20
+ | `when` | `string \| object \| function` | Condition for rendering children |
21
+ | `fallback?` | `ReactNode` | Content shown when condition fails |
22
+ | `treatPendingAsSignedOut?` | `boolean` | Treat pending sessions as signed-out (default: `true`) |
23
+
24
+ ## `when` Prop Variants
25
+
26
+ ### Authentication State
27
+
28
+ ```tsx
29
+ // Show content only when signed in
30
+ <Show when="signed-in">
31
+ <p>Welcome back!</p>
32
+ </Show>
33
+
34
+ // Show content only when signed out
35
+ <Show when="signed-out">
36
+ <p>Please sign in.</p>
37
+ </Show>
38
+ ```
39
+
40
+ ### Role Check
41
+
42
+ ```tsx
43
+ <Show when={{ role: 'org:admin' }}>
44
+ <AdminPanel />
45
+ </Show>
46
+ ```
47
+
48
+ ### Permission Check
49
+
50
+ ```tsx
51
+ <Show when={{ permission: 'org:billing:manage' }}>
52
+ <BillingSettings />
53
+ </Show>
54
+ ```
55
+
56
+ ### Billing Feature Check
57
+
58
+ ```tsx
59
+ <Show when={{ feature: 'widgets' }}>
60
+ <WidgetBuilder />
61
+ </Show>
62
+ ```
63
+
64
+ ### Billing Plan Check
65
+
66
+ ```tsx
67
+ <Show when={{ plan: 'gold' }}>
68
+ <PremiumContent />
69
+ </Show>
70
+ ```
71
+
72
+ ### Custom Condition (Function)
73
+
74
+ ```tsx
75
+ <Show when={(has) => has({ role: 'org:admin' }) || has({ permission: 'org:billing:manage' })}>
76
+ <SettingsPanel />
77
+ </Show>
78
+ ```
79
+
80
+ ## Fallback Content
81
+
82
+ Show alternative content when the condition fails:
83
+
84
+ ```tsx
85
+ <Show when="signed-in" fallback={<p>Please sign in to continue.</p>}>
86
+ <Dashboard />
87
+ </Show>
88
+ ```
89
+
90
+ ## Session Tasks and Pending State
91
+
92
+ The `treatPendingAsSignedOut` prop controls how pending sessions (sessions with incomplete tasks) are handled:
93
+
94
+ ```tsx
95
+ // Default: pending sessions are treated as signed-out
96
+ <Show when="signed-in" treatPendingAsSignedOut>
97
+ <Dashboard />
98
+ </Show>
99
+
100
+ // Treat pending sessions as signed-in (e.g., to show task completion UI)
101
+ <Show when="signed-in" treatPendingAsSignedOut={false}>
102
+ <TaskCompletionFlow />
103
+ </Show>
104
+ ```
105
+
106
+ ## Security Caveat
107
+
108
+ **`<Show>` only visually hides content** — it remains in browser source. It is not a security boundary. For protecting sensitive data, always verify authentication server-side with `auth()` or use `auth.protect()` in middleware.
109
+
110
+ ## Migration from Core 2
111
+
112
+ | Core 2 | Current |
113
+ |--------|---------|
114
+ | `<SignedIn>` | `<Show when="signed-in">` |
115
+ | `<SignedOut>` | `<Show when="signed-out">` |
116
+ | `<Protect role="org:admin">` | `<Show when={{ role: 'org:admin' }}>` |
117
+ | `<Protect permission="org:billing:manage">` | `<Show when={{ permission: 'org:billing:manage' }}>` |
118
+ | `<Protect condition={(has) => expr}>` | `<Show when={(has) => expr}>` |
119
+ | `<Protect fallback={...}>` | `<Show when={...} fallback={...}>` |
120
+ | *(no equivalent)* | `<Show when={{ feature: 'widgets' }}>` |
121
+ | *(no equivalent)* | `<Show when={{ plan: 'gold' }}>` |
122
+
123
+ ## Docs
124
+
125
+ - [Show component reference](https://clerk.com/docs/components/control/show)
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: clerk-nextjs-patterns
3
+ description: Advanced Next.js patterns - middleware, Server Actions, caching with Clerk.
4
+ license: MIT
5
+ allowed-tools: WebFetch
6
+ metadata:
7
+ author: clerk
8
+ version: "2.1.0"
9
+ ---
10
+
11
+ # Next.js Patterns
12
+
13
+ > **Version**: Check `package.json` for the SDK version — see `clerk` skill for the version table. Core 2 differences are noted inline with `> **Core 2 ONLY (skip if current SDK):**` callouts.
14
+
15
+ For basic setup, see `setup/`.
16
+
17
+ ## Impact Levels
18
+
19
+ - **CRITICAL** - Breaking bugs, security holes
20
+ - **HIGH** - Common mistakes
21
+ - **MEDIUM** - Optimization
22
+
23
+ ## References
24
+
25
+ | Reference | Impact |
26
+ |-----------|--------|
27
+ | `references/server-vs-client.md` | CRITICAL - `await auth()` vs hooks |
28
+ | `references/middleware-strategies.md` | HIGH - Public-first vs protected-first, `proxy.ts` (Next.js <=15: `middleware.ts`) |
29
+ | `references/server-actions.md` | HIGH - Protect mutations |
30
+ | `references/api-routes.md` | HIGH - 401 vs 403 |
31
+ | `references/caching-auth.md` | MEDIUM - User-scoped caching |
32
+
33
+ ## Mental Model
34
+
35
+ Server vs Client = different auth APIs:
36
+ - **Server**: `await auth()` from `@clerk/nextjs/server` (async!)
37
+ - **Client**: `useAuth()` hook from `@clerk/nextjs` (sync)
38
+
39
+ Never mix them. Server Components use server imports, Client Components use hooks.
40
+
41
+ Key properties from `auth()`:
42
+ - `isAuthenticated` — boolean, replaces the `!!userId` pattern
43
+ - `sessionStatus` — `'active'` | `'pending'`, for detecting incomplete session tasks
44
+ - `userId`, `orgId`, `orgSlug`, `has()`, `protect()` — unchanged
45
+
46
+ > **Core 2 ONLY (skip if current SDK):** `isAuthenticated` and `sessionStatus` are not available. Check `!!userId` instead.
47
+
48
+ ## Minimal Pattern
49
+
50
+ ```typescript
51
+ // Server Component
52
+ import { auth } from '@clerk/nextjs/server'
53
+
54
+ export default async function Page() {
55
+ const { isAuthenticated, userId } = await auth() // MUST await!
56
+ if (!isAuthenticated) return <p>Not signed in</p>
57
+ return <p>Hello {userId}</p>
58
+ }
59
+ ```
60
+
61
+ > **Core 2 ONLY (skip if current SDK):** `isAuthenticated` is not available. Use `if (!userId)` instead.
62
+
63
+ ### Conditional Rendering with `<Show>`
64
+
65
+ For client-side conditional rendering based on auth state:
66
+
67
+ ```tsx
68
+ import { Show } from '@clerk/nextjs'
69
+
70
+ <Show when="signed-in" fallback={<p>Please sign in</p>}>
71
+ <Dashboard />
72
+ </Show>
73
+ ```
74
+
75
+ > **Core 2 ONLY (skip if current SDK):** Use `<SignedIn>` and `<SignedOut>` components instead of `<Show>`. See `custom-ui/core-3/show-component.md` for the full migration table.
76
+
77
+ ## Common Pitfalls
78
+
79
+ | Symptom | Cause | Fix |
80
+ |---------|-------|-----|
81
+ | `undefined` userId in Server Component | Missing `await` | `await auth()` not `auth()` |
82
+ | Auth not working on API routes | Missing matcher | Add `'/(api|trpc)(.*)'` to `proxy.ts` (Next.js <=15: `middleware.ts`) |
83
+ | Cache returns wrong user's data | Missing userId in key | Include `userId` in `unstable_cache` key |
84
+ | Mutations bypass auth | Unprotected Server Action | Check `auth()` at start of action |
85
+ | Wrong HTTP error code | Confused 401/403 | 401 = not signed in, 403 = no permission |
86
+
87
+ ## See Also
88
+
89
+ - `setup/`
90
+ - `orgs/`
91
+
92
+ ## Docs
93
+
94
+ [Next.js SDK](https://clerk.com/docs/reference/nextjs/overview)
@@ -0,0 +1,50 @@
1
+ # API Routes (HIGH)
2
+
3
+ ## Auth Check Pattern
4
+
5
+ ```typescript
6
+ import { auth } from '@clerk/nextjs/server';
7
+
8
+ export async function GET() {
9
+ const { isAuthenticated, userId } = await auth();
10
+ if (!isAuthenticated) {
11
+ return Response.json({ error: 'Unauthorized' }, { status: 401 });
12
+ }
13
+ const data = await db.data.findMany({ where: { userId } });
14
+ return Response.json(data);
15
+ }
16
+ ```
17
+
18
+ > **Core 2 ONLY (skip if current SDK):** `isAuthenticated` is not available. Use `if (!userId)` instead.
19
+
20
+ ## 401 vs 403
21
+
22
+ - **401** - Not authenticated
23
+ - **403** - Authenticated but lacks permission
24
+
25
+ ```typescript
26
+ export async function DELETE(req: Request) {
27
+ const { isAuthenticated, has } = await auth();
28
+ if (!isAuthenticated) return Response.json({ error: 'Unauthorized' }, { status: 401 });
29
+
30
+ const isAdmin = await has({ role: 'org:admin' });
31
+ if (!isAdmin) return Response.json({ error: 'Forbidden' }, { status: 403 });
32
+
33
+ return Response.json({ success: true });
34
+ }
35
+ ```
36
+
37
+ ## Org Route Protection
38
+
39
+ ```typescript
40
+ export async function GET(req: Request, { params }: { params: { orgId: string } }) {
41
+ const { userId, orgId } = await auth();
42
+ if (!userId) return Response.json({ error: 'Unauthorized' }, { status: 401 });
43
+ if (orgId !== params.orgId) return Response.json({ error: 'Forbidden' }, { status: 403 });
44
+
45
+ const orgData = await db.orgs.findUnique({ where: { id: orgId } });
46
+ return Response.json(orgData);
47
+ }
48
+ ```
49
+
50
+ [Docs](https://clerk.com/docs/reference/nextjs/auth)
@@ -0,0 +1,56 @@
1
+ # Caching with Auth (CRITICAL)
2
+
3
+ **CRITICAL**: Cache keys MUST include userId/orgId to prevent data leaking between users.
4
+
5
+ ## User-Scoped Cache
6
+
7
+ ```typescript
8
+ import { auth } from '@clerk/nextjs/server';
9
+ import { unstable_cache } from 'next/cache';
10
+
11
+ export default async function ProfilePage() {
12
+ const { userId } = await auth();
13
+ if (!userId) return <div>Not signed in</div>;
14
+
15
+ const cachedGetUserData = unstable_cache(
16
+ () => getUserData(userId),
17
+ [`user-${userId}`],
18
+ { revalidate: 60, tags: [`user-${userId}`] }
19
+ );
20
+
21
+ const userData = await cachedGetUserData();
22
+ return <div>{userData.name}</div>;
23
+ }
24
+ ```
25
+
26
+ ## Revalidate After Updates
27
+
28
+ ```typescript
29
+ 'use server';
30
+ import { revalidateTag } from 'next/cache';
31
+ import { auth } from '@clerk/nextjs/server';
32
+
33
+ export async function updateProfile(formData: FormData) {
34
+ const { userId } = await auth();
35
+ if (!userId) throw new Error('Unauthorized');
36
+
37
+ await db.users.update({
38
+ where: { id: userId },
39
+ data: { name: formData.get('name') as string },
40
+ });
41
+ revalidateTag(`user-${userId}`);
42
+ }
43
+ ```
44
+
45
+ ## Org-Scoped Cache
46
+
47
+ ```typescript
48
+ const { orgId } = await auth();
49
+ const getOrgData = unstable_cache(
50
+ () => db.orgData.findMany({ where: { organizationId: orgId } }),
51
+ [`org-${orgId}-data`],
52
+ { revalidate: 300, tags: [`org-${orgId}`] }
53
+ );
54
+ ```
55
+
56
+ [Docs](https://nextjs.org/docs/app/building-your-application/caching)
@@ -0,0 +1,68 @@
1
+ # Middleware Strategies (HIGH)
2
+
3
+ > **Filename:** `proxy.ts` (Next.js <=15: `middleware.ts`). The code is identical; only the filename changes.
4
+
5
+ ## Public-First (marketing sites, blogs)
6
+
7
+ Protect specific routes, allow everything else:
8
+
9
+ ```typescript
10
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
11
+
12
+ const isProtectedRoute = createRouteMatcher([
13
+ '/dashboard(.*)',
14
+ '/settings(.*)',
15
+ '/api/private(.*)',
16
+ ]);
17
+
18
+ export default clerkMiddleware(async (auth, req) => {
19
+ if (isProtectedRoute(req)) await auth.protect();
20
+ });
21
+
22
+ export const config = {
23
+ matcher: [
24
+ '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
25
+ '/(api|trpc)(.*)',
26
+ ],
27
+ };
28
+ ```
29
+
30
+ ## Protected-First (internal tools, dashboards)
31
+
32
+ Block everything, allow specific public routes:
33
+
34
+ ```typescript
35
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
36
+
37
+ const isPublicRoute = createRouteMatcher([
38
+ '/',
39
+ '/sign-in(.*)',
40
+ '/sign-up(.*)',
41
+ '/api/public(.*)',
42
+ ]);
43
+
44
+ export default clerkMiddleware(async (auth, req) => {
45
+ if (!isPublicRoute(req)) await auth.protect();
46
+ });
47
+ ```
48
+
49
+ ## Session Tasks
50
+
51
+ When session tasks are enabled (e.g., forced password reset, MFA setup), users may have a `pending` session status. You can handle this in middleware:
52
+
53
+ ```typescript
54
+ export default clerkMiddleware(async (auth, req) => {
55
+ const { sessionStatus } = await auth();
56
+
57
+ // Redirect pending sessions to task completion page
58
+ if (sessionStatus === 'pending') {
59
+ return NextResponse.redirect(new URL('/sign-in/tasks', req.url));
60
+ }
61
+
62
+ if (isProtectedRoute(req)) await auth.protect();
63
+ });
64
+ ```
65
+
66
+ > **Core 2 ONLY (skip if current SDK):** `sessionStatus` is not available. Session tasks do not exist in Core 2.
67
+
68
+ [Docs](https://clerk.com/docs/reference/nextjs/clerk-middleware)