Generated banner for Clerk Astro Patterns
Generated logo for Clerk Astro Patterns

Clerk Astro Patterns

ClerkOfficialCreated by clerk

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk
  • Has scripts

From the author

Astro patterns with Clerk — middleware, SSR pages, island components, API routes, static vs SSR rendering. Triggers on: astro clerk, clerk astro middleware, astro protected page, clerk island component, astro API route auth, clerk astro SSR.
— the description by clerk

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides essential patterns for integrating Clerk authentication into Astro projects. It covers the configuration of middleware, handling authentication in server-side rendered (SSR) pages, managing Clerk components within React islands, and securing API routes. It clarifies the distinction between static and server-rendered pages, ensuring developers correctly implement auth checks and state management using the @clerk/astro SDK.

What it does

  • Configures clerkMiddleware for request-level authentication handling.
  • Demonstrates how to protect SSR pages using Astro.locals.auth().
  • Provides patterns for using Clerk components in React islands with client:load.
  • Explains how to secure API routes with proper status codes and auth checks.
  • Outlines the correct import sources for components, stores, and server utilities.
  • Addresses common pitfalls regarding rendering modes and middleware configuration.

When to use it

  • When setting up Clerk authentication in a new or existing Astro project.
  • When deciding between static and SSR rendering for protected routes.
  • When implementing authentication checks within Astro API routes.
  • When integrating Clerk React components into Astro islands.
  • When troubleshooting authentication issues in production versus development environments.

Advantages

  • Provides clear separation between SSR and static authentication patterns.
  • Standardizes the use of Astro.locals.auth() for consistent auth checks.
  • Includes specific guidance for React integration within Astro islands.
  • Helps avoid common configuration errors related to Astro rendering modes.

Usage examples

“How do I protect an SSR page in Astro using Clerk?”
The agent will provide a code snippet showing how to use Astro.locals.auth() to check for a userId and redirect unauthenticated users to a sign-in page.
“How do I secure an Astro API route with Clerk?”
The agent will provide an example of an API route using context.locals.auth() to verify the user and return appropriate 401 or 403 HTTP status codes.
“Why is my Clerk component not working in my Astro island?”
The agent will explain the requirement for the client:load directive and verify that the component is imported from the correct @clerk/astro/react package.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/clerk/clerk-astro-patterns/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip clerk-astro-patterns.zip -d .agents/skills

Documentation by clerk

Astro Patterns

SDK: @clerk/astro v4+. Requires Astro 5+.

What Do You Need?

TaskReference
Configure middleware (bare clerkMiddleware(), checks per page)references/middleware.md
Protect SSR pagesreferences/ssr-pages.md
Use Clerk in island componentsreferences/island-components.md
Auth in API routesreferences/api-routes.md
Use Clerk with React in Astroreferences/astro-react.md

Mental Model

Astro has two rendering modes per page: SSR and static prerender. Clerk works differently in each:

  • SSR pages — use Astro.locals.auth() which is populated by the middleware

  • Static pages (export const prerender = true) — Clerk middleware skips them; use client-side hooks in islands

  • Islands — React/Vue/Svelte components; use useAuth() and other hooks from @clerk/astro/react

Request → clerkMiddleware() → SSR page → Astro.locals.auth()
                                ↓
                         Island (.client) → useAuth() hook

Setup

astro.config.mjs
import { defineConfig } from 'astro/config'
import clerk from '@clerk/astro'

export default defineConfig({
  integrations: [clerk()],
  output: 'server',
})
src/middleware.ts
import { clerkMiddleware } from '@clerk/astro/server'

export const onRequest = clerkMiddleware()

The middleware populates Astro.locals.auth(). Each page and API route checks for itself. createRouteMatcher was removed in @clerk/astro 4.0 (deprecated in 3.x). Don't add it. Existing uses must move to page and API-route checks before upgrading. See the Astro clerkMiddleware() reference.

SSR Page Auth

---
const { userId, orgId } = Astro.locals.auth()
if (!userId) return Astro.redirect('/sign-in')
---

<h1>Dashboard</h1>

API Route Auth

// src/pages/api/data.ts
import type { APIRoute } from 'astro'

export const GET: APIRoute = ({ locals }) => {
  const { userId } = locals.auth()
  if (!userId) return new Response('Unauthorized', { status: 401 })

  return Response.json({ userId })
}

Common Pitfalls

SymptomCauseFix
Astro.locals.auth is undefinedMissing middlewareAdd clerkMiddleware to src/middleware.ts
Auth works in dev but not productionoutput: 'static' globallySet output: 'server' or hybrid for protected pages
Static page has no authPrerendered pages skip middlewareUse export const prerender = false or move to island
Island not reactive to sign-inMissing client:load directiveAdd client:load to the island component

Import Map

WhatImport From
clerkMiddleware, clerkClient@clerk/astro/server
useAuth, UserButton (no useUser)@clerk/astro/react
$userStore, $authStore@clerk/astro/client
Astro components (<SignIn>, etc.)@clerk/astro/components

Env Variables

# .env
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...

Astro uses PUBLIC_ prefix for client-exposed variables (not NEXT_PUBLIC_).

See Also

  • clerk-setup - Initial Clerk install

  • clerk-custom-ui - Custom flows & appearance

  • clerk-orgs - B2B organizations

Docs

Astro SDK

Files

evals/evals.json

{
  "skill_name": "clerk-astro-patterns",
  "evals": [
    {
      "id": 1,
      "prompt": "i'm adding clerk to my astro app. protect the /dashboard and /settings pages so signed-out users are redirected to /sign-in.",
      "expected_output": "Keeps src/middleware.ts as a bare clerkMiddleware(). Each of src/pages/dashboard.astro and src/pages/settings.astro calls Astro.locals.auth() in its frontmatter and returns Astro.redirect('/sign-in') when userId is falsy.",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Does NOT import createRouteMatcher",
        "src/middleware.ts still exports onRequest = clerkMiddleware() from @clerk/astro/server",
        "src/pages/dashboard.astro calls Astro.locals.auth() and returns Astro.redirect('/sign-in') when userId is falsy",
        "src/pages/settings.astro calls Astro.locals.auth() and returns Astro.redirect('/sign-in') when userId is falsy",
        "/ remains public"
      ]
    },
    {
      "id": 2,
      "prompt": "i have an astro SSR page at /dashboard. i need to get the current user's id and redirect to /sign-in if they're not authenticated.",
      "expected_output": "Uses Astro.locals.auth() to get userId, calls Astro.redirect('/sign-in') when userId is falsy",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Calls Astro.locals.auth() to get auth data in the frontmatter",
        "Destructures userId from the auth result",
        "Calls return Astro.redirect('/sign-in') when userId is falsy",
        "Page has output: server or is not marked as prerender",
        "Does NOT import auth from @clerk/nextjs/server"
      ]
    },
    {
      "id": 3,
      "prompt": "i need to protect an Astro API route at /api/data.ts so only authenticated users can call it. return 401 if not authenticated.",
      "expected_output": "API route reads Astro.locals.auth() to get userId, returns 401 Response when not authenticated",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Reads Astro.locals.auth() in the GET or POST handler",
        "Returns new Response('Unauthorized', { status: 401 }) when userId is falsy",
        "Uses userId in the data fetching logic",
        "Does NOT require auth in the static prerender path"
      ]
    },
    {
      "id": 4,
      "prompt": "i want to show a sign-in button in my astro site's header when the user is not signed in, and a user menu when they are. this needs to work client-side.",
      "expected_output": "Creates a React island component with client:load using useAuth and SignInButton/UserButton from @clerk/astro/react",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Creates a React island component (Header.tsx or similar)",
        "Imports useAuth from @clerk/astro/react",
        "Shows UserButton when isSignedIn is true",
        "Shows SignInButton when isSignedIn is false",
        "Mounts the component with client:load directive in the .astro file"
      ]
    },
    {
      "id": 5,
      "prompt": "i have a mix of static and dynamic pages in my astro app. how do i handle auth on pages that are statically prerendered?",
      "expected_output": "Explains that static prerendered pages skip middleware, suggests using client-side auth in islands or converting the page to SSR with export const prerender = false",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Notes that clerkMiddleware is skipped for prerendered pages",
        "Suggests removing export const prerender = true for pages that need auth",
        "OR suggests using client-side useAuth in island components for partially static pages",
        "Does NOT suggest that Astro.locals.auth() works on prerendered pages",
        "Explains the hybrid rendering approach (output: hybrid in astro.config.mjs)"
      ]
    },
    {
      "id": 6,
      "prompt": "i need to show org-aware content in an astro SSR page. load the current org name from clerk and show it, or redirect to /select-org if no org is active.",
      "expected_output": "Uses Astro.locals.auth() to get orgId, redirects to /select-org if orgId is missing, shows org data",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Calls Astro.locals.auth() and destructures both userId and orgId",
        "Returns Astro.redirect('/sign-in') when userId is falsy",
        "Returns Astro.redirect('/select-org') when orgId is falsy",
        "Uses orgId to fetch or display org-specific data",
        "Handles both unauthenticated and no-org-selected states"
      ]
    },
    {
      "id": 7,
      "prompt": "i want to use React components with Clerk in my Astro app. set up the React integration and create a header component that shows sign-in/sign-up for signed-out users and a user button for signed-in users. use it in an Astro layout.",
      "expected_output": "Adds @astrojs/react, updates astro.config with react(), creates React header using Show/UserButton/SignInButton from @clerk/astro/react, mounts with client:load in Astro layout",
      "scaffold": "astro-basic-auth",
      "expectations": [
        "Updates astro.config to include react() in integrations array alongside clerk()",
        "Creates a React component (.tsx) importing from @clerk/astro/react (NOT @clerk/astro/components)",
        "Uses Show when='signed-in' and Show when='signed-out' for conditional rendering (NOT SignedIn/SignedOut, which @clerk/astro 3 removed)",
        "Includes UserButton and SignInButton from @clerk/astro/react",
        "Mounts the React component in an .astro file with client:load directive",
        "Does NOT forget client:load on the React component (would render as static HTML without it)"
      ]
    }
  ]
}

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Needs Node.js

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
clerk
Publisher
Clerk (Official)
Source
clerk/skills · skills/clerk-astro-patterns · commit c5d2c0d
Licence
MIT License (redistribution allowed)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by clerk or Clerk.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download clerk-astro-patterns.zip