Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pink-dingos-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/astro": patch
---

Fixed an error when using Control components (`<SignedIn />`, `<SignedOut />`) in prerendered pages
5 changes: 5 additions & 0 deletions integration/templates/astro-node/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ import { SignedIn, SignedOut, SignOutButton, OrganizationSwitcher } from '@clerk
title='For members'
body='Learn how Astro works and explore the official API docs.'
/>
<Card
href='/prerendered'
title='Prerendered Page'
body='Test prerendered pages with Clerk control components'
/>
</SignedIn>
</ul>
</Layout>
Expand Down
25 changes: 25 additions & 0 deletions integration/templates/astro-node/src/pages/prerendered.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
import { SignedIn, SignedOut } from '@clerk/astro/components';
import Layout from '../layouts/Layout.astro';

// This page is prerendered at build time despite output: 'server' mode
export const prerender = true;
---

<Layout title='Prerendered Page Test'>
<h1>Prerendered Page with Clerk Components</h1>

<p>This page is statically generated at build time (prerender = true) in server output mode.</p>

<SignedIn>
<div id="signed-in-content">
<p>✅ You are signed in! (This content should be hidden initially and shown after client-side auth check)</p>
</div>
</SignedIn>

<SignedOut>
<div id="signed-out-content">
<p>🔒 You are signed out. (This content should be visible initially for signed-out users)</p>
</div>
</SignedOut>
</Layout>
30 changes: 30 additions & 0 deletions integration/tests/astro/components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,4 +511,34 @@ testAgainstRunningApps({ withPattern: ['astro.node.withCustomRoles'] })('basic f
// await expect(u.page.getByText('Loading')).toBeHidden();
await expect(u.page.getByText("I'm an admin")).toBeVisible();
});

test('prerendered page with control components works correctly', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

// Test while signed out
await u.page.goToRelative('/prerendered');
await u.page.waitForClerkJsLoaded();
await u.po.expect.toBeSignedOut();

// Verify SignedOut content is visible and SignedIn is hidden
await expect(u.page.locator('#signed-out-content')).toBeVisible();
await expect(u.page.locator('#signed-in-content')).toBeHidden();

// Sign in
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({
email: fakeAdmin.email,
password: fakeAdmin.password,
});
await u.po.expect.toBeSignedIn();

// Visit prerendered page again while signed in
await u.page.goToRelative('/prerendered');
await u.page.waitForClerkJsLoaded();

// Verify SignedIn content is visible and SignedOut is hidden
await expect(u.page.locator('#signed-in-content')).toBeVisible();
await expect(u.page.locator('#signed-out-content')).toBeHidden();
});
});
5 changes: 4 additions & 1 deletion packages/astro/src/astro-components/control/Protect.astro
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ type Props = ProtectProps & {

const { isStatic, ...props } = Astro.props;

const ProtectComponent = isStaticOutput(isStatic) ? ProtectCSR : ProtectSSR;
// If user explicitly sets isStatic prop, honor it
// Otherwise, detect based on runtime (whether auth function exists)
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
const ProtectComponent = shouldUseCSR ? ProtectCSR : ProtectSSR;

// Note: Astro server islands also use a "fallback" slot for loading states
// See: https://docs.astro.build/en/guides/server-islands/#server-island-fallback-content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const {
<script>
import { $sessionStore } from '@clerk/astro/client';
import { type AuthState, BaseClerkControlElement } from './BaseClerkControlElement';
import type { CheckAuthorization } from '@clerk/types';
import type { CheckAuthorization } from '@clerk/shared/types';

class ClerkProtect extends BaseClerkControlElement {
private defaultSlot: HTMLDivElement | null = null;
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/astro-components/control/SignedIn.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ type Props = {

const { isStatic, class: className } = Astro.props;

const SignedInComponent = isStaticOutput(isStatic) ? SignedInCSR : SignedInSSR;
// If user explicitly sets isStatic prop, honor it
// Otherwise, detect based on runtime (whether auth function exists)
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
const SignedInComponent = shouldUseCSR ? SignedInCSR : SignedInSSR;
---

<SignedInComponent class={className}>
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/astro-components/control/SignedOut.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ type Props = {

const { isStatic, class: className } = Astro.props;

const SignedOutComponent = isStaticOutput(isStatic) ? SignedOutCSR : SignedOutSSR;
// If user explicitly sets isStatic prop, honor it
// Otherwise, detect based on runtime (whether auth function exists)
const shouldUseCSR = isStatic !== undefined ? isStaticOutput(isStatic) : !Astro.locals.auth;
const SignedOutComponent = shouldUseCSR ? SignedOutCSR : SignedOutSSR;
---

<SignedOutComponent class={className}>
Expand Down