diff --git a/.changeset/five-cougars-throw.md b/.changeset/five-cougars-throw.md
new file mode 100644
index 00000000000..4c4f2403ce4
--- /dev/null
+++ b/.changeset/five-cougars-throw.md
@@ -0,0 +1,29 @@
+---
+'@clerk/react': major
+'@clerk/nextjs': major
+'@clerk/react-router': major
+'@clerk/tanstack-react-start': minor
+'@clerk/shared': patch
+---
+
+`useAuth().getToken` is no longer `undefined` during server-side rendering, it is a function and calling it will throw.
+
+* If you are only using `getToken` in `useEffect`, event handlers or with non-suspenseful data fetching libraries, no change is necessary as these only trigger on the client.
+* If you are using suspenseful data fetching libraries that do trigger during SSR, you likely have strategies in place to avoid calling `getToken` already, since this has never been possible.
+* If you are using `getToken === undefined` checks to avoid calling it, know that it will now throw instead and you should catch and handle the error.
+
+```tsx
+async function doThingWithToken(getToken: GetToken) {
+ try {
+ const token = await getToken();
+
+ // Use token
+ } catch (error) {
+ if (isClerkRuntimeError(error) && error.code === 'clerk_runtime_not_browser') {
+ // Handle error
+ }
+ }
+}
+```
+
+To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object).
diff --git a/packages/react/src/hooks/__tests__/useAuth.test.tsx b/packages/react/src/hooks/__tests__/useAuth.test.tsx
index 70f86cac5b7..b395627fc15 100644
--- a/packages/react/src/hooks/__tests__/useAuth.test.tsx
+++ b/packages/react/src/hooks/__tests__/useAuth.test.tsx
@@ -78,6 +78,31 @@ describe('useAuth', () => {
});
});
+describe('useAuth.getToken', () => {
+ test('throws an error if getToken is called in a non-browser environment', async () => {
+ const originalWindow = global.window;
+
+ const { result } = renderHook(() => useAuth(), {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
+
+ // Set window to undefined to simulate non-browser environment
+ global.window = undefined as any;
+
+ try {
+ await expect(result.current.getToken()).rejects.toThrow(
+ 'useAuth().getToken() can only be used in browser environments',
+ );
+ } finally {
+ global.window = originalWindow;
+ }
+ });
+});
+
describe('useDerivedAuth', () => {
beforeEach(() => {
vi.clearAllMocks();
diff --git a/packages/react/src/hooks/utils.ts b/packages/react/src/hooks/utils.ts
index 9ac4cfbfe97..b895b755a70 100644
--- a/packages/react/src/hooks/utils.ts
+++ b/packages/react/src/hooks/utils.ts
@@ -1,3 +1,6 @@
+import { inBrowser } from '@clerk/shared/browser';
+import { ClerkRuntimeError } from '@clerk/shared/error';
+
import type { IsomorphicClerk } from '../isomorphicClerk';
/**
@@ -22,6 +25,15 @@ const clerkLoaded = (isomorphicClerk: IsomorphicClerk) => {
*/
export const createGetToken = (isomorphicClerk: IsomorphicClerk) => {
return async (options: any) => {
+ if (!inBrowser()) {
+ throw new ClerkRuntimeError(
+ 'useAuth().getToken() can only be used in browser environments. To access auth data server-side, see the Auth object reference doc: https://clerk.com/docs/reference/backend/types/auth-object',
+ {
+ code: 'clerk_runtime_not_browser',
+ },
+ );
+ }
+
await clerkLoaded(isomorphicClerk);
if (!isomorphicClerk.session) {
return null;
diff --git a/packages/shared/src/getToken.ts b/packages/shared/src/getToken.ts
index 14c98f834cd..a9b77255990 100644
--- a/packages/shared/src/getToken.ts
+++ b/packages/shared/src/getToken.ts
@@ -37,9 +37,12 @@ function getWindowClerk(): LoadedClerk | undefined {
async function waitForClerk(): Promise {
if (!inBrowser()) {
- throw new ClerkRuntimeError('getToken can only be used in browser environments.', {
- code: 'clerk_runtime_not_browser',
- });
+ throw new ClerkRuntimeError(
+ 'getToken can only be used in browser environments. To access auth data server-side, see the Auth object reference doc: https://clerk.com/docs/reference/backend/types/auth-object',
+ {
+ code: 'clerk_runtime_not_browser',
+ },
+ );
}
const clerk = getWindowClerk();
diff --git a/packages/shared/src/types/hooks.ts b/packages/shared/src/types/hooks.ts
index 622d588478b..812a0c29445 100644
--- a/packages/shared/src/types/hooks.ts
+++ b/packages/shared/src/types/hooks.ts
@@ -71,6 +71,11 @@ export type UseAuthReturn =
signOut: SignOut;
/**
* A function that retrieves the current user's session token or a custom JWT template. Returns a promise that resolves to the token. See the [reference doc](https://clerk.com/docs/reference/javascript/session#get-token).
+ *
+ * > [!NOTE]
+ * > To access auth data server-side, see the [`Auth` object reference doc](https://clerk.com/docs/reference/backend/types/auth-object).
+ *
+ * @throws {ClerkRuntimeError} When called in a non-browser environment (code: `clerk_runtime_not_browser`)
*/
getToken: GetToken;
}