⚠️ DEPRECATED⚠️ This project has been deprecated. We recommend using Robert Soriano's svelte-clerk project instead, which adds support for Svelte 5 and newer versions of the Clerk components.
Adapter for using Clerk authentication in SvelteKit.
The demo site is just this repository, hosted on Cloudflare Pages.
# npm
npm i clerk-sveltekit
# pnpm
pnpm i clerk-sveltekit
# yarn
yarn add clerk-sveltekit
# bun
bun i clerk-sveltekit
Add these values to your .env (get them from Clerk after creating an application there):
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_abcdefg123
CLERK_SECRET_KEY=sk_test_abcdefg123The easiest way to get these values is to click "API Keys" in the Clerk dashboard, and then copy the values for Next.js, and change NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY to PUBLIC_CLERK_PUBLISHABLE_KEY.
Note that for production sites using OAuth providers, you will have to do some more setup with Clerk and DNS.
Add this to src/hooks.server.ts (or integrate this code with your existing hooks.server.ts file):
import type { Handle } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'
import { handleClerk } from 'clerk-sveltekit/server'
import { CLERK_SECRET_KEY } from '$env/static/private'
export const handle: Handle = sequence(
handleClerk(CLERK_SECRET_KEY, {
debug: true,
protectedPaths: ['/admin'],
signInUrl: '/sign-in',
})
)Add this to src/hooks.client.ts:
import type { HandleClientError } from '@sveltejs/kit'
// To use Clerk components:
import { initializeClerkClient } from 'clerk-sveltekit/client'
// Or for headless mode:
// import { initializeClerkClient } from 'clerk-sveltekit/headless'
import { PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public'
initializeClerkClient(PUBLIC_CLERK_PUBLISHABLE_KEY, {
afterSignInUrl: '/admin/',
afterSignUpUrl: '/admin/',
signInUrl: '/sign-in',
signUpUrl: '/sign-up',
})
export const handleError: HandleClientError = async ({ error, event }) => {
console.error(error, event)
}Customize the protected paths, and the various URLs as you like.
Note
If you use clerk-sveltekit/headless instead of clerk-sveltekit/client, your bundle will be much smaller (by about 1MB), but you will not have access to <SignIn />, <SignUp />, <UserProfile />, <UserButton />, <OrganizationProfile />, <OrganizationSwitcher />, or <CreateOrganization />. Sign-ins will have to happen on your accounts.{TLD} subdomain.
Next, put the SignIn component on your sign in page:
<script lang="ts">
import SignIn from 'clerk-sveltekit/client/SignIn.svelte'
</script>
<div>
<SignIn redirectUrl="/admin" />
</div>And put the SignUp component on your sign up page:
<script lang="ts">
import SignUp from 'clerk-sveltekit/client/SignUp.svelte'
</script>
<div>
<SignUp redirectUrl="/admin" />
</div>Then, where you want to show the signed in user's photo and sign out button (probably in a +layout.svelte file in the header):
<script lang="ts">
import UserButton from 'clerk-sveltekit/client/UserButton.svelte'
import SignedIn from 'clerk-sveltekit/client/SignedIn.svelte'
import SignedOut from 'clerk-sveltekit/client/SignedOut.svelte'
</script>
<SignedIn>
<UserButton afterSignOutUrl="/" />
</SignedIn>
<SignedOut>
<a href="/sign-in">Sign in</a> <span>|</span> <a href="/sign-up">Sign up</a>
<!-- You could also use <SignInButton mode="modal" /> and <SignUpButton mode="modal" /> here -->
</SignedOut>All components can be imported from clerk-sveltekit/client/ComponentName.svelte
<ClerkLoading />— Wrapper that shows its contents when Clerk is still loading.<ClerkLoaded let:clerk />— Wrapper that shows its contents (and exposes theclerkobject) when Clerk is done loading.<SignIn />— Renders a sign-in form.<SignUp />— Renders a sign-up form.<SignedIn let:user />— Wrapper that shows its contents (and exposes the Clerkuserobject) when the user is signed in.<SignedOut />— Wrapper that shows its contents when the user is not signed in.<UserButton />— Button that shows the user’s profile photo with log out link when they are signed in.<UserProfile />— Renders the current user’s profile.<SignInButton />— Unstyled sign-in button (can domode="modal"too).<SignUpButton />— Unstyled sign-up button (can domode="modal"too).<SignOutButton />— Unstyled sign-out button.<OrganizationProfile />— Renders the organization profile component.<OrganizationSwitcher />— Renders an organization switcher component.<CreateOrganization />— Renders UI for creating an organization.
Note that components should be used for displaying UI, but are not sufficient for protecting routes. To protect a route, use the protectedPaths option passed to handleClerk() in your hooks.server.ts file.
The protectedPaths option will accept an array of either strings, or functions which accept a SvelteKit event object and return a boolean. When passed strings, any route that starts with that string will be protected. i.e. protecting '/admin' will protect /admin but also /admin/foo.
Server-side protected routes will automatically get a Clerk user object injected into locals.session which means you can use it in a load() function, a default action, or a form action.
Thanks to Cerbos for their https://github.com/cerbos/sveltekit-clerk-cerbos example repo which got this project started, and to Brian Bug for fixing bugs in that implementation.