diff --git a/.changeset/calm-wasps-accept.md b/.changeset/calm-wasps-accept.md
new file mode 100644
index 00000000000..2a3ea3b0bcc
--- /dev/null
+++ b/.changeset/calm-wasps-accept.md
@@ -0,0 +1,36 @@
+---
+'@clerk/clerk-expo': minor
+---
+
+Introduce `getClerkInstance()` to avoid importing the Clerk class from clerk-js manually.
+
+This enables developers to create and access a Clerk instance in their application outside of React.
+```tsx
+import { ClerkProvider, getClerkInstance } from "@clerk/expo"
+
+const clerkInstance = getClerkInstance({ publishableKey: 'xxxx' })
+
+// Always pass the `publishableKey` to `ClerkProvider`
+
+ ...
+
+
+// Somewhere in your code, outside of React you can do
+const token = await clerkInstance.session?.getToken();
+fetch('http://example.com/', {headers: {Authorization: token })
+```
+```tsx
+import { ClerkProvider, getClerkInstance } from "@clerk/expo"
+
+// Always pass the `publishableKey` to `ClerkProvider`
+
+ ...
+
+
+// If you sure that this code will run after the ClerkProvider has rendered then you can use `getClerkIntance` without options
+const token = await getClerkInstance().session?.getToken();
+fetch('http://example.com/', {headers: {Authorization: token })
+
+```
+Attention: If `getClerkInstance` is called without a publishable key, and ClerkProvider has not rendered yet, an error will be thrown
+
diff --git a/.changeset/dirty-panthers-perform.md b/.changeset/dirty-panthers-perform.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/dirty-panthers-perform.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/empty-deers-notice.md b/.changeset/empty-deers-notice.md
new file mode 100644
index 00000000000..0c65810650f
--- /dev/null
+++ b/.changeset/empty-deers-notice.md
@@ -0,0 +1,5 @@
+---
+"@clerk/shared": patch
+---
+
+Update `js-cookie` from `3.0.1` to `3.0.5`. Update `swr` from `2.2.0` to `2.2.5`.
diff --git a/.changeset/gentle-brooms-worry.md b/.changeset/gentle-brooms-worry.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/gentle-brooms-worry.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/giant-cougars-grab.md b/.changeset/giant-cougars-grab.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/giant-cougars-grab.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/metal-foxes-raise.md b/.changeset/metal-foxes-raise.md
new file mode 100644
index 00000000000..b960dae3869
--- /dev/null
+++ b/.changeset/metal-foxes-raise.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-react': patch
+---
+
+Update `SignUpButton` and `SignInButton` to respect `forceRedirect` and `fallbackRedirect` props. Previously, these were getting ignored and successful completions of the flows would fallback to the default redirect URL.
diff --git a/.changeset/pretty-hounds-drive.md b/.changeset/pretty-hounds-drive.md
new file mode 100644
index 00000000000..ec380ec43f2
--- /dev/null
+++ b/.changeset/pretty-hounds-drive.md
@@ -0,0 +1,3 @@
+---
+---
+
diff --git a/.changeset/rotten-eyes-tickle.md b/.changeset/rotten-eyes-tickle.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/rotten-eyes-tickle.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/.changeset/sixty-ears-rest.md b/.changeset/sixty-ears-rest.md
new file mode 100644
index 00000000000..c59bdb3a1e9
--- /dev/null
+++ b/.changeset/sixty-ears-rest.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-js': patch
+---
+
+Fixed a bug where Clerk components rendered in modals were wrapped with `aria-hidden`.
diff --git a/.changeset/yellow-deers-dress.md b/.changeset/yellow-deers-dress.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/yellow-deers-dress.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/integration/templates/express-vite/package.json b/integration/templates/express-vite/package.json
index 9aa07214537..f2fbca4ae5f 100644
--- a/integration/templates/express-vite/package.json
+++ b/integration/templates/express-vite/package.json
@@ -14,11 +14,11 @@
"ejs": "^3.1.6",
"express": "^4.18.2",
"ts-node": "^10.9.1",
- "typescript": "^4.9.3",
+ "typescript": "^5.4.5",
"vite-express": "^0.11.0"
},
"devDependencies": {
- "@types/express": "^4.17.15",
+ "@types/express": "^4.17.21",
"@types/node": "^18.19.33",
"nodemon": "^2.0.20",
"vite": "^4.0.4"
diff --git a/integration/templates/next-app-router-quickstart/package.json b/integration/templates/next-app-router-quickstart/package.json
index a8a2ca029b4..6d39af16fcd 100644
--- a/integration/templates/next-app-router-quickstart/package.json
+++ b/integration/templates/next-app-router-quickstart/package.json
@@ -15,7 +15,7 @@
"next": "14.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "typescript": "^5"
+ "typescript": "^5.4.5"
},
"engines": {
"node": ">=18.17.0"
diff --git a/integration/templates/next-app-router/package.json b/integration/templates/next-app-router/package.json
index 76a46c7549b..056a0a8e25f 100644
--- a/integration/templates/next-app-router/package.json
+++ b/integration/templates/next-app-router/package.json
@@ -15,7 +15,7 @@
"next": "^13.5",
"react": "18.3.1",
"react-dom": "18.3.1",
- "typescript": "5.1.6"
+ "typescript": "^5.4.5"
},
"engines": {
"node": ">=18.17.0"
diff --git a/integration/templates/next-app-router/src/app/buttons/page.tsx b/integration/templates/next-app-router/src/app/buttons/page.tsx
new file mode 100644
index 00000000000..4e4a500e721
--- /dev/null
+++ b/integration/templates/next-app-router/src/app/buttons/page.tsx
@@ -0,0 +1,35 @@
+import { SignInButton, SignUpButton } from '@clerk/nextjs';
+
+export default function Home() {
+ return (
+
+
+ Sign in button (force)
+
+
+
+ Sign in button (fallback)
+
+
+
+ Sign up button (force)
+
+
+
+ Sign up button (fallback)
+
+
+ );
+}
diff --git a/integration/templates/react-vite/package.json b/integration/templates/react-vite/package.json
index 73cf36d9dd7..2d5fc3e4e31 100644
--- a/integration/templates/react-vite/package.json
+++ b/integration/templates/react-vite/package.json
@@ -24,7 +24,7 @@
"eslint": "^8.38.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
- "typescript": "^5.0.2",
+ "typescript": "^5.4.5",
"vite": "^4.3.9"
},
"engines": {
diff --git a/integration/templates/remix-node/package.json b/integration/templates/remix-node/package.json
index 346561d2152..46dadb7297a 100644
--- a/integration/templates/remix-node/package.json
+++ b/integration/templates/remix-node/package.json
@@ -25,7 +25,7 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"eslint": "^8.38.0",
- "typescript": "^5.0.4"
+ "typescript": "^5.4.5"
},
"engines": {
"node": ">=18.17.0"
diff --git a/integration/tests/redirects.test.ts b/integration/tests/redirects.test.ts
new file mode 100644
index 00000000000..ce8d1434e0f
--- /dev/null
+++ b/integration/tests/redirects.test.ts
@@ -0,0 +1,130 @@
+import { test } from '@playwright/test';
+
+import { appConfigs } from '../presets';
+import type { FakeUser } from '../testUtils';
+import { createTestUtils, testAgainstRunningApps } from '../testUtils';
+
+testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('redirect props @nextjs', ({ app }) => {
+ test.describe.configure({ mode: 'serial' });
+
+ let fakeUser: FakeUser;
+
+ test.beforeAll(async () => {
+ const u = createTestUtils({ app });
+ fakeUser = u.services.users.createFakeUser({
+ fictionalEmail: true,
+ withPhoneNumber: true,
+ withUsername: true,
+ });
+ await u.services.users.createBapiUser(fakeUser);
+ });
+
+ test.afterAll(async () => {
+ await fakeUser.deleteIfExists();
+ await app.teardown();
+ });
+
+ test.afterEach(async ({ page, context }) => {
+ const u = createTestUtils({ app, page, context });
+ await u.page.signOut();
+ await u.page.context().clearCookies();
+ });
+
+ test.describe('SignInButton', () => {
+ test('sign in button respects forceRedirectUrl', async ({ page, context }) => {
+ const u = createTestUtils({ app, page, context });
+
+ await u.page.goToRelative('/buttons');
+ await u.page.waitForClerkJsLoaded();
+ await u.po.expect.toBeSignedOut();
+
+ await u.page.getByText('Sign in button (force)').click();
+
+ await u.po.signIn.waitForMounted();
+ await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
+
+ await u.page.waitForAppUrl('/protected');
+
+ await u.po.expect.toBeSignedIn();
+ });
+
+ test('sign in button respects fallbackRedirectUrl', async ({ page, context }) => {
+ const u = createTestUtils({ app, page, context });
+
+ await u.page.goToRelative('/buttons');
+ await u.page.waitForClerkJsLoaded();
+ await u.po.expect.toBeSignedOut();
+
+ await u.page.getByText('Sign in button (fallback)').click();
+
+ await u.po.signIn.waitForMounted();
+ await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
+
+ await u.page.waitForAppUrl('/protected');
+
+ await u.po.expect.toBeSignedIn();
+ });
+ });
+
+ test.describe('SignUpButton', () => {
+ test('sign up button respects forceRedirectUrl', async ({ page, context }) => {
+ const u = createTestUtils({ app, page, context });
+ const fakeUser = u.services.users.createFakeUser({
+ fictionalEmail: true,
+ withPhoneNumber: true,
+ withUsername: true,
+ });
+
+ await u.page.goToRelative('/buttons');
+ await u.page.waitForClerkJsLoaded();
+
+ await u.page.getByText('Sign up button (force)').click();
+
+ // Fill in sign up form
+ await u.po.signUp.signUpWithEmailAndPassword({
+ email: fakeUser.email,
+ password: fakeUser.password,
+ });
+
+ // Verify email
+ await u.po.signUp.enterTestOtpCode();
+
+ await u.page.waitForAppUrl('/protected');
+
+ // Check if user is signed in
+ await u.po.expect.toBeSignedIn();
+
+ await fakeUser.deleteIfExists();
+ });
+
+ test('sign up button respects fallbackRedirectUrl', async ({ page, context }) => {
+ const u = createTestUtils({ app, page, context });
+ const fakeUser = u.services.users.createFakeUser({
+ fictionalEmail: true,
+ withPhoneNumber: true,
+ withUsername: true,
+ });
+
+ await u.page.goToRelative('/buttons');
+ await u.page.waitForClerkJsLoaded();
+
+ await u.page.getByText('Sign up button (fallback)').click();
+
+ // Fill in sign up form
+ await u.po.signUp.signUpWithEmailAndPassword({
+ email: fakeUser.email,
+ password: fakeUser.password,
+ });
+
+ // Verify email
+ await u.po.signUp.enterTestOtpCode();
+
+ await u.page.waitForAppUrl('/protected');
+
+ // Check if user is signed in
+ await u.po.expect.toBeSignedIn();
+
+ await fakeUser.deleteIfExists();
+ });
+ });
+});
diff --git a/package-lock.json b/package-lock.json
index a119d489205..1e914696a37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"license": "MIT",
"devDependencies": {
"@actions/core": "^1.10.1",
- "@arethetypeswrong/cli": "^0.12.1",
+ "@arethetypeswrong/cli": "^0.15.3",
"@changesets/cli": "^2.26.2",
"@changesets/get-github-info": "^0.5.2",
"@commitlint/cli": "^19.3.0",
@@ -58,7 +58,7 @@
"ts-jest": "^29.0.3",
"tsup": "^8.0.1",
"turbo": "^1.10.16",
- "typescript": "^5.2.2",
+ "typescript": "^5.4.5",
"verdaccio": "^5.26.3",
"zx": "^7.2.3"
},
@@ -127,7 +127,9 @@
}
},
"node_modules/@andrewbranch/untar.js": {
- "version": "1.0.2",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz",
+ "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==",
"dev": true
},
"node_modules/@ardatan/relay-compiler": {
@@ -327,21 +329,24 @@
}
},
"node_modules/@arethetypeswrong/cli": {
- "version": "0.12.1",
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.15.3.tgz",
+ "integrity": "sha512-sIMA9ZJBWDEg1+xt5RkAEflZuf8+PO8SdKj17x6PtETuUho+qlZJg4DgmKc3q+QwQ9zOB5VLK6jVRbFdNLdUIA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@arethetypeswrong/core": "0.12.1",
+ "@arethetypeswrong/core": "0.15.1",
"chalk": "^4.1.2",
"cli-table3": "^0.6.3",
"commander": "^10.0.1",
- "marked": "^5.1.0",
- "marked-terminal": "^5.2.0",
- "node-fetch": "^2.6.4",
+ "marked": "^9.1.2",
+ "marked-terminal": "^6.0.0",
"semver": "^7.5.4"
},
"bin": {
"attw": "dist/index.js"
+ },
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@arethetypeswrong/cli/node_modules/ansi-styles": {
@@ -417,16 +422,33 @@
}
},
"node_modules/@arethetypeswrong/core": {
- "version": "0.12.1",
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-FYp6GBAgsNz81BkfItRz8RLZO03w5+BaeiPma1uCfmxTnxbtuMrI/dbzGiOk8VghO108uFI0oJo0OkewdSHw7g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@andrewbranch/untar.js": "^1.0.0",
- "fetch-ponyfill": "^7.1.0",
- "fflate": "^0.7.4",
+ "@andrewbranch/untar.js": "^1.0.3",
+ "fflate": "^0.8.2",
"semver": "^7.5.4",
- "typescript": "^5.2.2",
+ "ts-expose-internals-conditionally": "1.0.0-empty.0",
+ "typescript": "5.3.3",
"validate-npm-package-name": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@arethetypeswrong/core/node_modules/typescript": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
}
},
"node_modules/@babel/code-frame": {
@@ -11066,12 +11088,13 @@
"license": "MIT"
},
"node_modules/@types/express": {
- "version": "4.17.14",
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
+ "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.18",
+ "@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
@@ -11411,8 +11434,9 @@
}
},
"node_modules/@types/semver": {
- "version": "7.5.4",
- "license": "MIT"
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="
},
"node_modules/@types/send": {
"version": "0.17.1",
@@ -12830,8 +12854,9 @@
},
"node_modules/ansicolors": {
"version": "0.3.2",
- "dev": true,
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+ "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
+ "dev": true
},
"node_modules/any-eslint-parser": {
"version": "1.0.1",
@@ -14745,8 +14770,9 @@
},
"node_modules/cardinal": {
"version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz",
+ "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"ansicolors": "~0.3.2",
"redeyed": "~2.1.0"
@@ -18175,6 +18201,12 @@
"version": "9.2.2",
"license": "MIT"
},
+ "node_modules/emojilib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
+ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
+ "dev": true
+ },
"node_modules/emojis-list": {
"version": "3.0.0",
"dev": true,
@@ -20530,52 +20562,6 @@
"node": "^12.20 || >= 14.13"
}
},
- "node_modules/fetch-ponyfill": {
- "version": "7.1.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "node-fetch": "~2.6.1"
- }
- },
- "node_modules/fetch-ponyfill/node_modules/node-fetch": {
- "version": "2.6.13",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/fetch-ponyfill/node_modules/tr46": {
- "version": "0.0.3",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fetch-ponyfill/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/fetch-ponyfill/node_modules/whatwg-url": {
- "version": "5.0.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
"node_modules/fetch-retry": {
"version": "4.1.1",
"dev": true,
@@ -20583,9 +20569,10 @@
"peer": true
},
"node_modules/fflate": {
- "version": "0.7.4",
- "dev": true,
- "license": "MIT"
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "dev": true
},
"node_modules/figures": {
"version": "3.2.0",
@@ -26858,10 +26845,11 @@
}
},
"node_modules/js-cookie": {
- "version": "3.0.1",
- "license": "MIT",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
- "node": ">=12"
+ "node": ">=14"
}
},
"node_modules/js-tokens": {
@@ -28310,9 +28298,10 @@
"dev": true
},
"node_modules/marked": {
- "version": "5.1.2",
+ "version": "9.1.6",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
+ "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==",
"dev": true,
- "license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
@@ -28321,31 +28310,30 @@
}
},
"node_modules/marked-terminal": {
- "version": "5.2.0",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
+ "integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"ansi-escapes": "^6.2.0",
"cardinal": "^2.1.1",
- "chalk": "^5.2.0",
+ "chalk": "^5.3.0",
"cli-table3": "^0.6.3",
- "node-emoji": "^1.11.0",
- "supports-hyperlinks": "^2.3.0"
+ "node-emoji": "^2.1.3",
+ "supports-hyperlinks": "^3.0.0"
},
"engines": {
- "node": ">=14.13.1 || >=16.0.0"
+ "node": ">=16.0.0"
},
"peerDependencies": {
- "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
+ "marked": ">=1 <12"
}
},
"node_modules/marked-terminal/node_modules/ansi-escapes": {
- "version": "6.2.0",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
+ "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "type-fest": "^3.0.0"
- },
"engines": {
"node": ">=14.16"
},
@@ -28355,8 +28343,9 @@
},
"node_modules/marked-terminal/node_modules/chalk": {
"version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"dev": true,
- "license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
@@ -28364,15 +28353,38 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/marked-terminal/node_modules/type-fest": {
- "version": "3.13.1",
+ "node_modules/marked-terminal/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
- "license": "(MIT OR CC0-1.0)",
"engines": {
- "node": ">=14.16"
+ "node": ">=8"
+ }
+ },
+ "node_modules/marked-terminal/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/marked-terminal/node_modules/supports-hyperlinks": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz",
+ "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=14.18"
}
},
"node_modules/marky": {
@@ -29181,11 +29193,18 @@
}
},
"node_modules/node-emoji": {
- "version": "1.11.0",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz",
+ "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "lodash": "^4.17.21"
+ "@sindresorhus/is": "^4.6.0",
+ "char-regex": "^1.0.2",
+ "emojilib": "^2.4.0",
+ "skin-tone": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/node-fetch": {
@@ -32682,8 +32701,9 @@
},
"node_modules/redeyed": {
"version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz",
+ "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"esprima": "~4.0.0"
}
@@ -33958,6 +33978,18 @@
"devOptional": true,
"license": "MIT"
},
+ "node_modules/skin-tone": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
+ "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
+ "dev": true,
+ "dependencies": {
+ "unicode-emoji-modifier-base": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/slash": {
"version": "3.0.0",
"license": "MIT",
@@ -35193,6 +35225,7 @@
"version": "2.3.0",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"has-flag": "^4.0.0",
"supports-color": "^7.0.0"
@@ -35205,6 +35238,7 @@
"version": "4.0.0",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=8"
}
@@ -35213,6 +35247,7 @@
"version": "7.2.0",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -35272,9 +35307,11 @@
}
},
"node_modules/swr": {
- "version": "2.2.0",
- "license": "MIT",
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz",
+ "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==",
"dependencies": {
+ "client-only": "^0.0.1",
"use-sync-external-store": "^1.2.0"
},
"peerDependencies": {
@@ -36052,6 +36089,12 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/ts-expose-internals-conditionally": {
+ "version": "1.0.0-empty.0",
+ "resolved": "https://registry.npmjs.org/ts-expose-internals-conditionally/-/ts-expose-internals-conditionally-1.0.0-empty.0.tgz",
+ "integrity": "sha512-F8m9NOF6ZhdOClDVdlM8gj3fDCav4ZIFSs/EI3ksQbAAXVSCN/Jh5OCJDDZWBuBy9psFc6jULGDlPwjMYMhJDw==",
+ "dev": true
+ },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"license": "Apache-2.0"
@@ -36718,8 +36761,9 @@
}
},
"node_modules/typescript": {
- "version": "5.2.2",
- "license": "Apache-2.0",
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -36803,6 +36847,15 @@
"node": ">=4"
}
},
+ "node_modules/unicode-emoji-modifier-base": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
+ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/unicode-match-property-ecmascript": {
"version": "2.0.0",
"dev": true,
@@ -37141,12 +37194,10 @@
}
},
"node_modules/validate-npm-package-name": {
- "version": "5.0.0",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz",
+ "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==",
"dev": true,
- "license": "ISC",
- "dependencies": {
- "builtins": "^5.0.0"
- },
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
@@ -39618,7 +39669,7 @@
"tslib": "2.4.1",
"tsup": "*",
"type-fest": "^4.9.0",
- "typescript": "^5.3.3"
+ "typescript": "*"
},
"engines": {
"node": ">=18.17.0"
@@ -40137,7 +40188,7 @@
},
"devDependencies": {
"@clerk/eslint-config-custom": "*",
- "@types/express": "^4.17",
+ "@types/express": "^4.17.21",
"@types/node": "^18.19.33",
"@types/supertest": "^6.0.2",
"express": "^4.19.2",
@@ -40564,7 +40615,7 @@
},
"devDependencies": {
"@clerk/eslint-config-custom": "*",
- "@types/express": "4.17.14",
+ "@types/express": "^4.17.21",
"@types/node": "^18.19.33",
"nock": "^13.0.7",
"npm-run-all": "^4.1.5",
@@ -40588,7 +40639,7 @@
"dependencies": {
"@clerk/types": "4.5.1",
"glob-to-regexp": "0.4.1",
- "js-cookie": "3.0.1",
+ "js-cookie": "3.0.5",
"std-env": "^3.7.0",
"swr": "^2.2.0"
},
diff --git a/package.json b/package.json
index cf9aa8ac759..204680c5ac9 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
},
"devDependencies": {
"@actions/core": "^1.10.1",
- "@arethetypeswrong/cli": "^0.12.1",
+ "@arethetypeswrong/cli": "^0.15.3",
"@changesets/cli": "^2.26.2",
"@changesets/get-github-info": "^0.5.2",
"@commitlint/cli": "^19.3.0",
@@ -97,7 +97,7 @@
"ts-jest": "^29.0.3",
"tsup": "^8.0.1",
"turbo": "^1.10.16",
- "typescript": "^5.2.2",
+ "typescript": "^5.4.5",
"verdaccio": "^5.26.3",
"zx": "^7.2.3"
},
diff --git a/packages/clerk-js/src/ui/elements/Modal.tsx b/packages/clerk-js/src/ui/elements/Modal.tsx
index 5815040fcc6..a5cd6a734f3 100644
--- a/packages/clerk-js/src/ui/elements/Modal.tsx
+++ b/packages/clerk-js/src/ui/elements/Modal.tsx
@@ -50,7 +50,6 @@ export const Modal = withFloatingTree((props: ModalProps) => {
>
{
animation: `${animations.fadeIn} 150ms ${t.transitionTiming.$common}`,
zIndex: t.zIndices.$modal,
backgroundColor: t.colors.$modalBackdrop,
- // ...common.centeredFlex(),
alignItems: 'flex-start',
justifyContent: 'center',
overflow: 'auto',
diff --git a/packages/elements/package.json b/packages/elements/package.json
index 02b95868c56..267b5b049c5 100644
--- a/packages/elements/package.json
+++ b/packages/elements/package.json
@@ -90,7 +90,7 @@
"tslib": "2.4.1",
"tsup": "*",
"type-fest": "^4.9.0",
- "typescript": "^5.3.3"
+ "typescript": "*"
},
"peerDependencies": {
"@clerk/clerk-react": "^5.0.0",
diff --git a/packages/expo/src/ClerkProvider.tsx b/packages/expo/src/ClerkProvider.tsx
index fc4f804c306..c25b7280663 100644
--- a/packages/expo/src/ClerkProvider.tsx
+++ b/packages/expo/src/ClerkProvider.tsx
@@ -5,27 +5,25 @@ import { ClerkProvider as ClerkReactProvider } from '@clerk/clerk-react';
import React from 'react';
import type { TokenCache } from './cache';
-import { MemoryTokenCache } from './cache';
import { isReactNative } from './runtime';
-import { buildClerk } from './singleton';
+import { getClerkInstance } from './singleton';
export type ClerkProviderProps = ClerkReactProviderProps & {
tokenCache?: TokenCache;
};
export function ClerkProvider(props: ClerkProviderProps): JSX.Element {
- const { children, tokenCache = MemoryTokenCache, publishableKey, ...rest } = props;
- const key =
- publishableKey || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || '';
+ const { children, tokenCache, publishableKey, ...rest } = props;
+ const pk = publishableKey || process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY || process.env.CLERK_PUBLISHABLE_KEY || '';
return (
{children}
diff --git a/packages/expo/src/errorThrower.ts b/packages/expo/src/errorThrower.ts
new file mode 100644
index 00000000000..339b52dc6e3
--- /dev/null
+++ b/packages/expo/src/errorThrower.ts
@@ -0,0 +1,5 @@
+import { buildErrorThrower } from '@clerk/shared/error';
+
+const errorThrower = buildErrorThrower({ packageName: PACKAGE_NAME });
+
+export { errorThrower };
diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts
index 3e3eeb04815..4fa4c6b6a0e 100644
--- a/packages/expo/src/index.ts
+++ b/packages/expo/src/index.ts
@@ -17,7 +17,11 @@ export {
export { isClerkAPIResponseError, isEmailLinkError, isKnownError, isMetamaskError } from '@clerk/clerk-react/errors';
+/**
+ * @deprecated Use `getClerkInstance()` instead.
+ */
export { clerk as Clerk } from './singleton';
+export { getClerkInstance } from './singleton';
export * from './ClerkProvider';
export * from './useOAuth';
diff --git a/packages/expo/src/singleton.ts b/packages/expo/src/singleton.ts
index 996e2fee415..1929b00ecae 100644
--- a/packages/expo/src/singleton.ts
+++ b/packages/expo/src/singleton.ts
@@ -3,6 +3,8 @@ import { Clerk } from '@clerk/clerk-js/headless';
import type { HeadlessBrowserClerk } from '@clerk/clerk-react';
import type { TokenCache } from './cache';
+import { MemoryTokenCache } from './cache';
+import { errorThrower } from './errorThrower';
Clerk.sdkMetadata = {
name: PACKAGE_NAME,
@@ -11,29 +13,56 @@ Clerk.sdkMetadata = {
const KEY = '__clerk_client_jwt';
+/**
+ * @deprecated Use `getClerkInstance` instead. `Clerk` will be removed in the next major version.
+ */
export let clerk: HeadlessBrowserClerk;
+let __internal_clerk: HeadlessBrowserClerk | undefined;
type BuildClerkOptions = {
- key: string;
- tokenCache: TokenCache;
+ publishableKey?: string;
+ tokenCache?: TokenCache;
};
-export function buildClerk({ key, tokenCache }: BuildClerkOptions): HeadlessBrowserClerk {
+/**
+ * Access or create a Clerk instance outside of React.
+ * @example
+ * import { ClerkProvider, getClerkInstance } from "@clerk/expo"
+ *
+ * const clerkInstance = getClerkInstance({ publishableKey: 'xxxx' })
+ *
+ * // Always pass the `publishableKey` to `ClerkProvider`
+ *
+ * ...
+ *
+ *
+ * // Somewhere in your code, outside of React you can do
+ * const token = await clerkInstance.session?.getToken();
+ * fetch('http://example.com/', {headers: {Authorization: token })
+ * @throws MissingPublishableKeyError publishableKey is missing and Clerk has not been initialized yet
+ * @returns HeadlessBrowserClerk
+ */
+export function getClerkInstance(options?: BuildClerkOptions): HeadlessBrowserClerk {
+ const { publishableKey = process.env.CLERK_PUBLISHABLE_KEY || '', tokenCache = MemoryTokenCache } = options || {};
+
+ if (!__internal_clerk && !publishableKey) {
+ errorThrower.throwMissingPublishableKeyError();
+ }
+
// Support "hot-swapping" the Clerk instance at runtime. See JS-598 for additional details.
- const hasKeyChanged = clerk && key !== clerk.publishableKey;
+ const hasKeyChanged = __internal_clerk && !!publishableKey && publishableKey !== __internal_clerk.publishableKey;
- if (!clerk || hasKeyChanged) {
+ if (!__internal_clerk || hasKeyChanged) {
if (hasKeyChanged) {
tokenCache.clearToken?.(KEY);
}
const getToken = tokenCache.getToken;
const saveToken = tokenCache.saveToken;
- // TODO: DO NOT ACCEPT THIS
- clerk = new Clerk(key);
+ __internal_clerk = clerk = new Clerk(publishableKey);
// @ts-expect-error
- clerk.__unstable__onBeforeRequest(async (requestInit: FapiRequestInit) => {
+ __internal_clerk.__unstable__onBeforeRequest(async (requestInit: FapiRequestInit) => {
// https://reactnative.dev/docs/0.61/network#known-issues-with-fetch-and-cookie-based-authentication
requestInit.credentials = 'omit';
@@ -44,13 +73,12 @@ export function buildClerk({ key, tokenCache }: BuildClerkOptions): HeadlessBrow
});
// @ts-expect-error
- clerk.__unstable__onAfterResponse(async (_: FapiRequestInit, response: FapiResponse) => {
+ __internal_clerk.__unstable__onAfterResponse(async (_: FapiRequestInit, response: FapiResponse) => {
const authHeader = response.headers.get('authorization');
if (authHeader) {
await saveToken(KEY, authHeader);
}
});
}
-
- return clerk;
+ return __internal_clerk;
}
diff --git a/packages/express/package.json b/packages/express/package.json
index e9339be4651..a91c5383146 100644
--- a/packages/express/package.json
+++ b/packages/express/package.json
@@ -60,7 +60,7 @@
},
"devDependencies": {
"@clerk/eslint-config-custom": "*",
- "@types/express": "^4.17",
+ "@types/express": "^4.17.21",
"@types/node": "^18.19.33",
"@types/supertest": "^6.0.2",
"express": "^4.19.2",
diff --git a/packages/nextjs/examples/next/.eslintrc.json b/packages/nextjs/examples/next/.eslintrc.json
deleted file mode 100644
index da874f7928c..00000000000
--- a/packages/nextjs/examples/next/.eslintrc.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": "next/core-web-vitals",
- "parserOptions": {
- "project": ["./tsconfig.json"]
- }
-}
diff --git a/packages/nextjs/examples/next/.gitignore b/packages/nextjs/examples/next/.gitignore
deleted file mode 100644
index 737d8721092..00000000000
--- a/packages/nextjs/examples/next/.gitignore
+++ /dev/null
@@ -1,35 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-.pnpm-debug.log*
-
-# local env files
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
diff --git a/packages/nextjs/examples/next/README.md b/packages/nextjs/examples/next/README.md
deleted file mode 100644
index c87e0421d22..00000000000
--- a/packages/nextjs/examples/next/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
-
-[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
-
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/packages/nextjs/examples/next/next-env.d.ts b/packages/nextjs/examples/next/next-env.d.ts
deleted file mode 100644
index 4f11a03dc6c..00000000000
--- a/packages/nextjs/examples/next/next-env.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-///
-///
-
-// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/packages/nextjs/examples/next/next.config.js b/packages/nextjs/examples/next/next.config.js
deleted file mode 100644
index 3d3bc9990d8..00000000000
--- a/packages/nextjs/examples/next/next.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/** @type {import('next').NextConfig} */
-const nextConfig = {
- reactStrictMode: true,
- swcMinify: true,
-};
-
-module.exports = nextConfig;
diff --git a/packages/nextjs/examples/next/package.json b/packages/nextjs/examples/next/package.json
deleted file mode 100644
index 0504fb78e8c..00000000000
--- a/packages/nextjs/examples/next/package.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "my-app",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "next dev",
- "build": "next build",
- "start": "next start",
- "lint": "next lint"
- },
- "dependencies": {
- "@clerk/nextjs": "latest",
- "next": "12.3.4",
- "react": "18.3.1",
- "react-dom": "18.3.1"
- },
- "devDependencies": {
- "@types/node": "^18.19.33",
- "@types/react": "18.3.3",
- "@types/react-dom": "18.3.0",
- "eslint": "8.21.0",
- "eslint-config-next": "12.2.3",
- "typescript": "4.7.4"
- }
-}
\ No newline at end of file
diff --git a/packages/nextjs/examples/next/pages/_app.tsx b/packages/nextjs/examples/next/pages/_app.tsx
deleted file mode 100644
index caacba57ca8..00000000000
--- a/packages/nextjs/examples/next/pages/_app.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import '../styles/globals.css';
-
-import { ClerkProvider, RedirectToSignIn, SignedIn, SignedOut } from '@clerk/nextjs';
-import type { AppProps } from 'next/app';
-import { useRouter } from 'next/router';
-import React from 'react';
-
-const publicPages: Array = ['/'];
-
-function MyApp({ Component, pageProps }: AppProps) {
- // Get the pathname
- const { pathname } = useRouter();
-
- // Check if the current route matches a public page
- const isPublicPage = publicPages.includes(pathname);
-
- // If the current route is listed as public, render it directly
- // Otherwise, use Clerk to require authentication
- return (
-
- {isPublicPage ? (
-
- ) : (
- <>
-
-
-
-
-
-
- >
- )}
-
- );
-}
-
-export default MyApp;
diff --git a/packages/nextjs/examples/next/pages/api/hello.ts b/packages/nextjs/examples/next/pages/api/hello.ts
deleted file mode 100644
index eb4cc6657b3..00000000000
--- a/packages/nextjs/examples/next/pages/api/hello.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
-import type { NextApiRequest, NextApiResponse } from 'next';
-
-type Data = {
- name: string;
-};
-
-export default function handler(req: NextApiRequest, res: NextApiResponse) {
- res.status(200).json({ name: 'John Doe' });
-}
diff --git a/packages/nextjs/examples/next/pages/api/require-auth.ts b/packages/nextjs/examples/next/pages/api/require-auth.ts
deleted file mode 100644
index 087b30e48c4..00000000000
--- a/packages/nextjs/examples/next/pages/api/require-auth.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { requireAuth, RequireAuthProp } from '@clerk/nextjs/api';
-import type { NextApiRequest, NextApiResponse } from 'next';
-
-function handler(req: RequireAuthProp, res: NextApiResponse) {
- console.log('Session required');
- res.statusCode = 200;
- res.json(req.auth);
-}
-
-export default requireAuth(handler);
diff --git a/packages/nextjs/examples/next/pages/api/with-auth.ts b/packages/nextjs/examples/next/pages/api/with-auth.ts
deleted file mode 100644
index 461bf6e09c0..00000000000
--- a/packages/nextjs/examples/next/pages/api/with-auth.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { withAuth, WithAuthProp } from '@clerk/nextjs/api';
-import type { NextApiRequest, NextApiResponse } from 'next';
-
-function handler(req: WithAuthProp, res: NextApiResponse) {
- console.log('Session optional');
- res.statusCode = 200;
- res.json(req.auth);
-}
-
-export default withAuth(handler);
diff --git a/packages/nextjs/examples/next/pages/index.tsx b/packages/nextjs/examples/next/pages/index.tsx
deleted file mode 100644
index 3ffeb025699..00000000000
--- a/packages/nextjs/examples/next/pages/index.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import type { NextPage } from 'next';
-import Head from 'next/head';
-import Image from 'next/image';
-import React from 'react';
-
-import styles from '../styles/Home.module.css';
-
-const Home: NextPage = () => {
- const [responses, setResponse] = React.useState>({});
-
- const makeRequest = async (path: string) => {
- setResponse({
- ...responses,
- [path]: '// Loading...',
- });
-
- try {
- const res = await fetch(path);
- const body = await res.json();
- setResponse({
- ...responses,
- [path]: JSON.stringify(body, null, ' '),
- });
- } catch (e) {
- setResponse({ ...responses, [path]: '// There was an error with the request. Please contact support@clerk.com' });
- }
- };
-
- const getResponse = (path: string): string => responses[path] || '// Click above to run the request';
-
- return (
-
-
-
Create Next App
-
-
-
-
-
-
-
-
- Get started by editing pages/index.tsx
-
-
-
-
-
withAuth →
-
-
-
-
- {getResponse('/api/with-auth')}
-
-
-
-
-
requireAuth →
-
-
- {getResponse('/api/require-auth')}
-
-
-
-
-
-
-
- );
-};
-
-export default Home;
diff --git a/packages/nextjs/examples/next/public/favicon.ico b/packages/nextjs/examples/next/public/favicon.ico
deleted file mode 100644
index 718d6fea483..00000000000
Binary files a/packages/nextjs/examples/next/public/favicon.ico and /dev/null differ
diff --git a/packages/nextjs/examples/next/public/vercel.svg b/packages/nextjs/examples/next/public/vercel.svg
deleted file mode 100644
index fbf0e25a651..00000000000
--- a/packages/nextjs/examples/next/public/vercel.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/nextjs/examples/next/styles/Home.module.css b/packages/nextjs/examples/next/styles/Home.module.css
deleted file mode 100644
index f7a8472d22d..00000000000
--- a/packages/nextjs/examples/next/styles/Home.module.css
+++ /dev/null
@@ -1,130 +0,0 @@
-.container {
- padding: 0 2rem;
-}
-
-.main {
- min-height: 100vh;
- padding: 4rem 0;
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
-}
-
-.footer {
- display: flex;
- flex: 1;
- padding: 2rem 0;
- border-top: 1px solid #eaeaea;
- justify-content: center;
- align-items: center;
-}
-
-.footer a {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-grow: 1;
-}
-
-.title a {
- color: #0070f3;
- text-decoration: none;
-}
-
-.title a:hover,
-.title a:focus,
-.title a:active {
- text-decoration: underline;
-}
-
-.title {
- margin: 0;
- line-height: 1.15;
- font-size: 4rem;
-}
-
-.title,
-.description {
- text-align: center;
-}
-
-.description {
- margin: 4rem 0;
- line-height: 1.5;
- font-size: 1.5rem;
-}
-
-.code {
- background: #fafafa;
- border-radius: 5px;
- padding: 0.75rem;
- font-size: 1.1rem;
- font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New,
- monospace;
-}
-
-.grid {
- display: flex;
- align-items: center;
- justify-content: center;
- flex-wrap: wrap;
- max-width: 800px;
-}
-
-.card {
- margin: 1rem;
- padding: 1.5rem;
- text-align: left;
- color: inherit;
- text-decoration: none;
- border: 1px solid #eaeaea;
- border-radius: 10px;
- transition: color 0.15s ease, border-color 0.15s ease;
- max-width: 300px;
- min-height: 500px;
-}
-
-.card:hover,
-.card:focus,
-.card:active {
- color: #0070f3;
- border-color: #0070f3;
-}
-
-.card h2 {
- margin: 0 0 1rem 0;
- font-size: 1.5rem;
-}
-
-.card p {
- margin: 0;
- font-size: 1.25rem;
- line-height: 1.5;
-}
-
-.logo {
- height: 1em;
- margin-left: 0.5rem;
-}
-
-@media (max-width: 600px) {
- .grid {
- width: 100%;
- flex-direction: column;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .card,
- .footer {
- border-color: #222;
- }
- .code {
- background: #111;
- }
- .logo img {
- filter: invert(1);
- }
-}
diff --git a/packages/nextjs/examples/next/styles/globals.css b/packages/nextjs/examples/next/styles/globals.css
deleted file mode 100644
index aa3b7f2a5b3..00000000000
--- a/packages/nextjs/examples/next/styles/globals.css
+++ /dev/null
@@ -1,26 +0,0 @@
-html,
-body {
- padding: 0;
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans,
- Helvetica Neue, sans-serif;
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-* {
- box-sizing: border-box;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
- body {
- color: white;
- background: black;
- }
-}
diff --git a/packages/nextjs/examples/next/tsconfig.json b/packages/nextjs/examples/next/tsconfig.json
deleted file mode 100644
index 99710e85787..00000000000
--- a/packages/nextjs/examples/next/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "compilerOptions": {
- "target": "es5",
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true
- },
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
- "exclude": ["node_modules"]
-}
diff --git a/packages/react/src/components/SignInButton.tsx b/packages/react/src/components/SignInButton.tsx
index 50cec831de3..6dd8453a028 100644
--- a/packages/react/src/components/SignInButton.tsx
+++ b/packages/react/src/components/SignInButton.tsx
@@ -1,3 +1,4 @@
+import type { SignInProps } from '@clerk/types';
import React from 'react';
import type { SignInButtonProps, WithClerkProp } from '../types';
@@ -11,21 +12,27 @@ export const SignInButton = withClerk(({ clerk, children, ...props }: WithClerkP
const child = assertSingleChild(children)('SignInButton');
const clickHandler = () => {
- const opts = {
+ const opts: SignInProps = {
+ forceRedirectUrl,
+ fallbackRedirectUrl,
signUpFallbackRedirectUrl,
signUpForceRedirectUrl,
- signInForceRedirectUrl: forceRedirectUrl,
- signInFallbackRedirectUrl: fallbackRedirectUrl,
};
if (mode === 'modal') {
return clerk.openSignIn(opts);
}
- return clerk.redirectToSignIn(opts);
+ return clerk.redirectToSignIn({
+ ...opts,
+ signInFallbackRedirectUrl: fallbackRedirectUrl,
+ signInForceRedirectUrl: forceRedirectUrl,
+ });
};
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
- await safeExecute((child as any).props.onClick)(e);
+ if (child && typeof child === 'object' && 'props' in child) {
+ await safeExecute(child.props.onClick)(e);
+ }
return clickHandler();
};
diff --git a/packages/react/src/components/SignUpButton.tsx b/packages/react/src/components/SignUpButton.tsx
index 1db1a5f30ea..729494550a8 100644
--- a/packages/react/src/components/SignUpButton.tsx
+++ b/packages/react/src/components/SignUpButton.tsx
@@ -1,3 +1,4 @@
+import type { SignUpProps } from '@clerk/types';
import React from 'react';
import type { SignUpButtonProps, WithClerkProp } from '../types';
@@ -19,9 +20,9 @@ export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkP
const child = assertSingleChild(children)('SignUpButton');
const clickHandler = () => {
- const opts = {
- signUpFallbackRedirectUrl: fallbackRedirectUrl,
- signUpForceRedirectUrl: forceRedirectUrl,
+ const opts: SignUpProps = {
+ fallbackRedirectUrl,
+ forceRedirectUrl,
signInFallbackRedirectUrl,
signInForceRedirectUrl,
unsafeMetadata,
@@ -31,11 +32,17 @@ export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkP
return clerk.openSignUp(opts);
}
- return clerk.redirectToSignUp(opts);
+ return clerk.redirectToSignUp({
+ ...opts,
+ signUpFallbackRedirectUrl: fallbackRedirectUrl,
+ signUpForceRedirectUrl: forceRedirectUrl,
+ });
};
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
- await safeExecute((child as any).props.onClick)(e);
+ if (child && typeof child === 'object' && 'props' in child) {
+ await safeExecute(child.props.onClick)(e);
+ }
return clickHandler();
};
diff --git a/packages/react/src/components/__tests__/SignInButton.test.tsx b/packages/react/src/components/__tests__/SignInButton.test.tsx
index 22050952ef6..a1b14241b9e 100644
--- a/packages/react/src/components/__tests__/SignInButton.test.tsx
+++ b/packages/react/src/components/__tests__/SignInButton.test.tsx
@@ -52,7 +52,7 @@ describe('', () => {
const btn = screen.getByText('Sign in');
await userEvent.click(btn);
- expect(mockRedirectToSignIn).toHaveBeenCalledWith({ signInForceRedirectUrl: url });
+ expect(mockRedirectToSignIn).toHaveBeenCalledWith({ forceRedirectUrl: url, signInForceRedirectUrl: url });
});
it('handles fallbackRedirectUrl prop', async () => {
@@ -62,6 +62,7 @@ describe('', () => {
await userEvent.click(btn);
expect(mockRedirectToSignIn).toHaveBeenCalledWith({
+ fallbackRedirectUrl: url,
signInFallbackRedirectUrl: url,
});
});
diff --git a/packages/react/src/components/__tests__/SignUpButton.test.tsx b/packages/react/src/components/__tests__/SignUpButton.test.tsx
index 918ecf8baa6..b1f9f4e755c 100644
--- a/packages/react/src/components/__tests__/SignUpButton.test.tsx
+++ b/packages/react/src/components/__tests__/SignUpButton.test.tsx
@@ -53,7 +53,7 @@ describe('', () => {
const btn = screen.getByText('Sign up');
userEvent.click(btn);
await waitFor(() => {
- expect(mockRedirectToSignUp).toHaveBeenCalledWith({ signUpForceRedirectUrl: url });
+ expect(mockRedirectToSignUp).toHaveBeenCalledWith({ forceRedirectUrl: url, signUpForceRedirectUrl: url });
});
});
@@ -63,6 +63,7 @@ describe('', () => {
userEvent.click(btn);
await waitFor(() => {
expect(mockRedirectToSignUp).toHaveBeenCalledWith({
+ fallbackRedirectUrl: url,
signUpFallbackRedirectUrl: url,
});
});
diff --git a/packages/sdk-node/package.json b/packages/sdk-node/package.json
index 142155eb18f..d0431b5f4b6 100644
--- a/packages/sdk-node/package.json
+++ b/packages/sdk-node/package.json
@@ -60,7 +60,7 @@
},
"devDependencies": {
"@clerk/eslint-config-custom": "*",
- "@types/express": "4.17.14",
+ "@types/express": "^4.17.21",
"@types/node": "^18.19.33",
"nock": "^13.0.7",
"npm-run-all": "^4.1.5",
diff --git a/packages/sdk-node/src/__tests__/authenticateRequest.test.ts b/packages/sdk-node/src/__tests__/authenticateRequest.test.ts
index 27753a593cb..60444dc0edb 100644
--- a/packages/sdk-node/src/__tests__/authenticateRequest.test.ts
+++ b/packages/sdk-node/src/__tests__/authenticateRequest.test.ts
@@ -1,5 +1,6 @@
import { constants } from '@clerk/backend/internal';
-import { Request } from 'express';
+// eslint-disable-next-line @typescript-eslint/consistent-type-imports
+import type { Request } from 'express';
import { authenticateRequest } from '../authenticateRequest';
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 57c3f614e90..4746ef3d212 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -90,7 +90,7 @@
"dependencies": {
"@clerk/types": "4.5.1",
"glob-to-regexp": "0.4.1",
- "js-cookie": "3.0.1",
+ "js-cookie": "3.0.5",
"std-env": "^3.7.0",
"swr": "^2.2.0"
},
diff --git a/packages/ui/src/global.css b/packages/ui/src/global.css
index 0e4d7c008d2..b3e4dd54fe4 100644
--- a/packages/ui/src/global.css
+++ b/packages/ui/src/global.css
@@ -12,6 +12,7 @@
--cl-color-destructive: 0 84% 60%;
--cl-radius: 0.375rem;
--cl-spacing-unit: 1rem;
+ --cl-font-size: 0.8125rem;
}
*,
diff --git a/packages/ui/src/primitives/button.tsx b/packages/ui/src/primitives/button.tsx
index 90e90098567..0eff921a05c 100644
--- a/packages/ui/src/primitives/button.tsx
+++ b/packages/ui/src/primitives/button.tsx
@@ -20,7 +20,7 @@ export const Button = React.forwardRef(function Button(
ref={forwardedRef}
{...props}
className={cn(
- 'text-accent-contrast bg-accent-9 border-accent-9 focus:ring-gray-a3 relative isolate inline-flex w-full select-none appearance-none items-center justify-center rounded-md border px-2 py-1.5 text-[0.8125rem]/[1.125rem] font-medium shadow-[0_1px_1px_0_theme(colors.white/.07)_inset] outline-none ring-[0.1875rem] ring-transparent before:absolute before:inset-0 before:rounded-[calc(theme(borderRadius.md)-1px)] before:shadow-[0_1px_1px_0_theme(colors.white/.07)_inset] after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:rounded-[calc(theme(borderRadius.md)-1px)] after:bg-gradient-to-b after:from-white/10 after:to-transparent disabled:pointer-events-none disabled:cursor-not-allowed',
+ 'text-accent-contrast bg-accent-9 border-accent-9 focus:ring-gray-a3 relative isolate inline-flex w-full select-none appearance-none items-center justify-center rounded-md border px-2 py-1.5 text-base font-medium shadow-[0_1px_1px_0_theme(colors.white/.07)_inset] outline-none ring-[0.1875rem] ring-transparent before:absolute before:inset-0 before:rounded-[calc(theme(borderRadius.md)-1px)] before:shadow-[0_1px_1px_0_theme(colors.white/.07)_inset] after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:rounded-[calc(theme(borderRadius.md)-1px)] after:bg-gradient-to-b after:from-white/10 after:to-transparent disabled:pointer-events-none disabled:cursor-not-allowed',
// note: only reduce opacity of `disabled` so `busy` is more prominent
disabled && 'disabled:opacity-50',
!busy && !disabled && 'hover:bg-accent-10 hover:after:opacity-0',
@@ -33,7 +33,7 @@ export const Button = React.forwardRef(function Button(
) : (
<>
{children}
- {icon && {icon}}
+ {icon && {icon}}
>
)}
diff --git a/packages/ui/src/primitives/card.tsx b/packages/ui/src/primitives/card.tsx
index b70dfa4476c..0e05c8d9411 100644
--- a/packages/ui/src/primitives/card.tsx
+++ b/packages/ui/src/primitives/card.tsx
@@ -47,7 +47,7 @@ export const Header = React.forwardRef
{children}
@@ -62,7 +62,7 @@ export const Title = React.forwardRef
{children}
@@ -75,7 +75,7 @@ export const Description = React.forwardRef
{children}
@@ -110,7 +110,7 @@ export const Footer = React.forwardRef
{children}
-
+
Secured by{' '}
{children}
@@ -160,7 +160,7 @@ export const FooterActionLink = React.forwardRef
{children}
diff --git a/packages/ui/src/primitives/connection.tsx b/packages/ui/src/primitives/connection.tsx
index 04067b8e7c8..1442a87bb98 100644
--- a/packages/ui/src/primitives/connection.tsx
+++ b/packages/ui/src/primitives/connection.tsx
@@ -35,7 +35,7 @@ export const Button = React.forwardRef(function Button(
ref={forwardedRef}
{...props}
className={cn(
- 'flex items-center justify-center gap-2 w-full bg-transparent text-gray-12 font-medium rounded-md bg-clip-padding border border-gray-a6 shadow-sm shadow-gray-a3 py-1.5 px-2.5 outline-none focus-visible:ring-[0.1875rem] focus-visible:ring-gray-a3 focus-visible:border-gray-a8 disabled:cursor-not-allowed text-[0.8125rem]/[1.125rem]',
+ 'flex items-center justify-center gap-2 w-full bg-transparent text-gray-12 font-medium rounded-md bg-clip-padding border border-gray-a6 shadow-sm shadow-gray-a3 py-1.5 px-2.5 outline-none focus-visible:ring-[0.1875rem] focus-visible:ring-gray-a3 focus-visible:border-gray-a8 disabled:cursor-not-allowed text-base',
// note: only reduce opacity of `disabled` so `busy` is more prominent
disabled && 'disabled:opacity-40',
!busy && !disabled && 'hover:bg-gray-a2',
diff --git a/packages/ui/src/primitives/field.tsx b/packages/ui/src/primitives/field.tsx
index da17fd956d5..f5f01a41e96 100644
--- a/packages/ui/src/primitives/field.tsx
+++ b/packages/ui/src/primitives/field.tsx
@@ -25,7 +25,7 @@ export const Label = React.forwardRef
@@ -44,7 +44,7 @@ export const Input = React.forwardRef
@@ -68,7 +68,7 @@ export const Message = React.forwardRef<
ref={forwardedRef}
{...props}
className={cn(
- 'text-[0.8125rem]/[1.125rem] flex gap-x-1',
+ 'text-base flex gap-x-1',
{
// TODO: Use the color tokens here
'text-[#ef4444]': intent === 'error',
diff --git a/packages/ui/src/primitives/icon.tsx b/packages/ui/src/primitives/icon.tsx
index 621a1df4acc..b5b21c9eb4d 100644
--- a/packages/ui/src/primitives/icon.tsx
+++ b/packages/ui/src/primitives/icon.tsx
@@ -4,6 +4,9 @@ import * as React from 'react';
type IconRef = SVGSVGElement;
type IconProps = Omit, 'viewBox'>;
+// Icons that need to be rotated 180deg in RTL mode
+const rtlIcons = ['IconCaretRight'];
+
function createIcon({ displayName, viewBox, path }: { displayName: string; viewBox: string; path: React.ReactNode }) {
const Icon = React.forwardRef(function ({ className, ...props }: IconProps, ref: React.ForwardedRef) {
return (
@@ -12,7 +15,7 @@ function createIcon({ displayName, viewBox, path }: { displayName: string; viewB
viewBox={viewBox}
fill='none'
xmlns='http://www.w3.org/2000/svg'
- className={cn('size-[1em]', className)}
+ className={cn('size-[1em]', rtlIcons.includes(displayName) && 'rtl:rotate-180', className)}
{...props}
>
{path}
diff --git a/packages/ui/src/primitives/seperator.tsx b/packages/ui/src/primitives/seperator.tsx
index 70314a671a9..5b10574e61e 100644
--- a/packages/ui/src/primitives/seperator.tsx
+++ b/packages/ui/src/primitives/seperator.tsx
@@ -8,7 +8,7 @@ export const Seperator = React.forwardRef
diff --git a/packages/ui/src/tailwind.config.ts b/packages/ui/src/tailwind.config.ts
index 2119b38ef00..7551fb5ef75 100644
--- a/packages/ui/src/tailwind.config.ts
+++ b/packages/ui/src/tailwind.config.ts
@@ -43,6 +43,15 @@ const config = {
fontFamily: {
sans: ['var(--cl-font-family)'],
},
+ fontSize: {
+ DEFAULT: ['var(--cl-font-size)', '1.38462'],
+ base: ['var(--cl-font-size)', '1.38462'],
+ xs: ['calc(var(--cl-font-size) * 0.8)', '1.38462'],
+ sm: ['calc(var(--cl-font-size) * 0.9)', '1.38462'],
+ md: ['var(--cl-font-size)', '1.38462'],
+ lg: ['calc(var(--cl-font-size) * 1.3)', '1.38462'],
+ xl: ['calc(var(--cl-font-size) * 1.85)', '1.38462'],
+ },
borderRadius: {
DEFAULT: 'var(--cl-radius)',
sm: 'calc(var(--cl-radius) * 0.66)',
@@ -50,6 +59,13 @@ const config = {
lg: 'calc(var(--cl-radius) * 1.33)',
xl: 'calc(var(--cl-radius) * 2)',
},
+ lineHeight: {
+ normal: 'normal',
+ extraSmall: '1.33333',
+ small: '1.38462',
+ medium: '1.41176',
+ large: '1.45455',
+ },
spacing: {
...generateSpaceScale(),
},
diff --git a/packages/ui/theme-builder/.gitignore b/packages/ui/theme-builder/.gitignore
index fd3dbb571a1..e32376ee409 100644
--- a/packages/ui/theme-builder/.gitignore
+++ b/packages/ui/theme-builder/.gitignore
@@ -34,3 +34,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+package-lock.json
diff --git a/packages/ui/theme-builder/app/color-picker.tsx b/packages/ui/theme-builder/app/color-picker.tsx
index c11eee9103c..57e2599eb0b 100644
--- a/packages/ui/theme-builder/app/color-picker.tsx
+++ b/packages/ui/theme-builder/app/color-picker.tsx
@@ -58,7 +58,7 @@ export function ColorPicker({
-
+
{
+export const getPreviewStyles = ({
+ lightColors,
+ darkColors,
+ radius,
+ spacingUnit,
+ fontSize,
+}: GetNewPreviewStylesParams) => {
const lightAccentColorsCss = getColorScaleCss({
isDarkMode: false,
name: 'accent',
@@ -711,6 +718,7 @@ ${darkGrayColorsCss}
:where(:root) {
--cl-radius: ${radius};
--cl-spacing-unit: ${spacingUnit};
+ --cl-font-size: ${fontSize};
}
`.trim();
};
diff --git a/packages/ui/theme-builder/app/theme-builder.tsx b/packages/ui/theme-builder/app/theme-builder.tsx
index 378b0ababff..95fa0e869a1 100644
--- a/packages/ui/theme-builder/app/theme-builder.tsx
+++ b/packages/ui/theme-builder/app/theme-builder.tsx
@@ -2,12 +2,12 @@
import { SignIn } from '@clerk/ui/sign-in';
import { SignUp } from '@clerk/ui/sign-up';
import cn from 'clsx';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
-import { AppearanceToggle } from './appearance-toggle';
import { ColorPicker } from './color-picker';
import { generateColors, getPreviewStyles } from './generate-colors';
import { ThemeDialog } from './theme-dialog';
+import { ToggleGroup } from './toggle-group';
const lightAccentDefault = '#2F3037';
const lightGrayDefault = '#2f3037';
@@ -19,6 +19,7 @@ const darkBackgroundDefault = '#111';
const radiusDefault = '0.375rem';
const spacingUnitDefault = '1rem';
+const fontSizeDefault = '0.8125rem';
const componnents = {
SignIn: ,
@@ -27,6 +28,7 @@ const componnents = {
type Component = keyof typeof componnents;
export function ThemeBuilder() {
+ const [dir, setDir] = useState('ltr');
const [appearance, setAppearance] = useState('light');
const [lightAccent, setLightAccent] = useState(lightAccentDefault);
const [lightGray, setLightGray] = useState(lightGrayDefault);
@@ -36,6 +38,7 @@ export function ThemeBuilder() {
const [darkBackground, setDarkBackground] = useState(darkBackgroundDefault);
const [radius, setRadius] = useState(radiusDefault);
const [spacingUnit, setSpacingUnit] = useState(spacingUnitDefault);
+ const [fontSize, setFontSize] = useState(fontSizeDefault);
const [selectedComponent, setSelectedComponent] = useState('SignIn');
const handleReset = () => {
setLightAccent(lightAccentDefault);
@@ -46,6 +49,7 @@ export function ThemeBuilder() {
setDarkBackground(darkBackgroundDefault);
setRadius(radiusDefault);
setSpacingUnit(spacingUnitDefault);
+ setFontSize(fontSizeDefault);
};
const lightResult = generateColors({
appearance: 'light',
@@ -64,7 +68,11 @@ export function ThemeBuilder() {
darkColors: darkResult,
radius,
spacingUnit,
+ fontSize,
});
+ useEffect(() => {
+ document.documentElement.dir = dir;
+ }, [dir]);
return (
<>