Skip to content

Commit df2b381

Browse files
authored
fix(astro): Automatically use CSR control components for prerendered pages (#7708)
1 parent 559cd84 commit df2b381

File tree

8 files changed

+78
-4
lines changed

8 files changed

+78
-4
lines changed

.changeset/pink-dingos-exercise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clerk/astro": patch
3+
---
4+
5+
Fixed an error when using Control components (`<SignedIn />`, `<SignedOut />`) in prerendered pages

integration/templates/astro-node/src/pages/index.astro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ import { SignedIn, SignedOut, SignOutButton, OrganizationSwitcher } from '@clerk
6464
title='For members'
6565
body='Learn how Astro works and explore the official API docs.'
6666
/>
67+
<Card
68+
href='/prerendered'
69+
title='Prerendered Page'
70+
body='Test prerendered pages with Clerk control components'
71+
/>
6772
</SignedIn>
6873
</ul>
6974
</Layout>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
import { SignedIn, SignedOut } from '@clerk/astro/components';
3+
import Layout from '../layouts/Layout.astro';
4+
5+
// This page is prerendered at build time despite output: 'server' mode
6+
export const prerender = true;
7+
---
8+
9+
<Layout title='Prerendered Page Test'>
10+
<h1>Prerendered Page with Clerk Components</h1>
11+
12+
<p>This page is statically generated at build time (prerender = true) in server output mode.</p>
13+
14+
<SignedIn>
15+
<div id="signed-in-content">
16+
<p>✅ You are signed in! (This content should be hidden initially and shown after client-side auth check)</p>
17+
</div>
18+
</SignedIn>
19+
20+
<SignedOut>
21+
<div id="signed-out-content">
22+
<p>🔒 You are signed out. (This content should be visible initially for signed-out users)</p>
23+
</div>
24+
</SignedOut>
25+
</Layout>

integration/tests/astro/components.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,4 +511,34 @@ testAgainstRunningApps({ withPattern: ['astro.node.withCustomRoles'] })('basic f
511511
// await expect(u.page.getByText('Loading')).toBeHidden();
512512
await expect(u.page.getByText("I'm an admin")).toBeVisible();
513513
});
514+
515+
test('prerendered page with control components works correctly', async ({ page, context }) => {
516+
const u = createTestUtils({ app, page, context });
517+
518+
// Test while signed out
519+
await u.page.goToRelative('/prerendered');
520+
await u.page.waitForClerkJsLoaded();
521+
await u.po.expect.toBeSignedOut();
522+
523+
// Verify SignedOut content is visible and SignedIn is hidden
524+
await expect(u.page.locator('#signed-out-content')).toBeVisible();
525+
await expect(u.page.locator('#signed-in-content')).toBeHidden();
526+
527+
// Sign in
528+
await u.page.goToRelative('/sign-in');
529+
await u.po.signIn.waitForMounted();
530+
await u.po.signIn.signInWithEmailAndInstantPassword({
531+
email: fakeAdmin.email,
532+
password: fakeAdmin.password,
533+
});
534+
await u.po.expect.toBeSignedIn();
535+
536+
// Visit prerendered page again while signed in
537+
await u.page.goToRelative('/prerendered');
538+
await u.page.waitForClerkJsLoaded();
539+
540+
// Verify SignedIn content is visible and SignedOut is hidden
541+
await expect(u.page.locator('#signed-in-content')).toBeVisible();
542+
await expect(u.page.locator('#signed-out-content')).toBeHidden();
543+
});
514544
});

packages/astro/src/astro-components/control/Protect.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ type Props = ProtectProps & {
2626
2727
const { isStatic, ...props } = Astro.props;
2828
29-
const ProtectComponent = isStaticOutput(isStatic) ? ProtectCSR : ProtectSSR;
29+
// If user explicitly sets isStatic prop, honor it
30+
// Otherwise, detect based on runtime (whether auth function exists)
31+
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
32+
const ProtectComponent = shouldUseCSR ? ProtectCSR : ProtectSSR;
3033
3134
// Note: Astro server islands also use a "fallback" slot for loading states
3235
// See: https://docs.astro.build/en/guides/server-islands/#server-island-fallback-content

packages/astro/src/astro-components/control/ProtectCSR.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const {
4444
<script>
4545
import { $sessionStore } from '@clerk/astro/client';
4646
import { type AuthState, BaseClerkControlElement } from './BaseClerkControlElement';
47-
import type { CheckAuthorization } from '@clerk/types';
47+
import type { CheckAuthorization } from '@clerk/shared/types';
4848

4949
class ClerkProtect extends BaseClerkControlElement {
5050
private defaultSlot: HTMLDivElement | null = null;

packages/astro/src/astro-components/control/SignedIn.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ type Props = {
1515
1616
const { isStatic, class: className } = Astro.props;
1717
18-
const SignedInComponent = isStaticOutput(isStatic) ? SignedInCSR : SignedInSSR;
18+
// If user explicitly sets isStatic prop, honor it
19+
// Otherwise, detect based on runtime (whether auth function exists)
20+
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
21+
const SignedInComponent = shouldUseCSR ? SignedInCSR : SignedInSSR;
1922
---
2023

2124
<SignedInComponent class={className}>

packages/astro/src/astro-components/control/SignedOut.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ type Props = {
1515
1616
const { isStatic, class: className } = Astro.props;
1717
18-
const SignedOutComponent = isStaticOutput(isStatic) ? SignedOutCSR : SignedOutSSR;
18+
// If user explicitly sets isStatic prop, honor it
19+
// Otherwise, detect based on runtime (whether auth function exists)
20+
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
21+
const SignedOutComponent = shouldUseCSR ? SignedOutCSR : SignedOutSSR;
1922
---
2023

2124
<SignedOutComponent class={className}>

0 commit comments

Comments
 (0)