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://mp-staging.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

references/api-routes.md

API Routes

Basic Auth Check

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

export const GET: APIRoute = async (context) => {
  const { userId } = context.locals.auth()

  if (!userId) {
    return new Response('Unauthorized', { status: 401 })
  }

  const data = await fetchData(userId)
  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  })
}

POST with Org Check

export const POST: APIRoute = async (context) => {
  const { userId, orgId } = context.locals.auth()

  if (!userId) return new Response('Unauthorized', { status: 401 })
  if (!orgId) return new Response('No active org', { status: 403 })

  const body = await context.request.json()
  await saveOrgData(orgId, body)

  return new Response(JSON.stringify({ ok: true }), {
    headers: { 'Content-Type': 'application/json' },
  })
}

Permission Check

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

  const canDelete = auth.has({ permission: 'org:items:delete' })
  if (!canDelete) return new Response('Forbidden', { status: 403 })

  await deleteItem(context.params.id!)
  return new Response(null, { status: 204 })
}

Using clerkClient in API Routes

import { clerkClient } from '@clerk/astro/server'

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

  const client = clerkClient(context)
  const user = await client.users.getUser(userId)

  return new Response(JSON.stringify({ name: user.fullName }), {
    headers: { 'Content-Type': 'application/json' },
  })
}

CRITICAL

  • API routes are always SSR — prerender does not apply

  • Use context.locals.auth() (not Astro.locals.auth()) in API routes

  • Return proper HTTP status codes: 401 = not authenticated, 403 = not authorized

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.

Download ZIP

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