Skip to content

Your backend signs users in. Session kit handles everything after.

Hand it the tokens your sign-in endpoint returns. It saves, restores and refreshes the session, keeps tabs in step, and guards your routes.

npm install @monarcode/session-kit
Get startedGitHubv0.1.0-alpha.8
export const auth = createAuth({
name: "my-app",
user: userSchema,
storage: webStorage(),
});

Before 09:00, your app signs in with your backend, then hands the tokens to Session kit. With the refresh option, the user stays signed in all hour: Session kit refreshes a minute before each expiry, saves the session at sign-in and on every refresh, reads it back on a reload at 09:20, where the route guard lets the page render, and keeps a second tab opened at 09:31 in step. Later, when the refresh token has expired too, the refresh fails: the session ends in every tab and the route guard redirects to the sign-in page. Without the refresh option, the session ends when access expires at 09:15, and the guard redirects at 09:16.

One illustrative hour, as Session kit runs it
pausedrefresh oncycle 1

Illustrative: your app signs in with your backend, then from 09:00 Session kit runs the session, until it ends. Access tokens last 15 minutes. With refresh, the page reloads at 09:20, a second tab opens at 09:31, and later the refresh token expires too.

The whole idea in three files

A client that knows your backend, a route only signed-in users reach, and a page that reads the session. One TanStack Router app, type-checked against the published package. Each setup's guide has its own version.

Create the client

import { createAuth, webStorage } from "@monarcode/session-kit";

import { login, refresh, userSchema } from "./backend.js";

export const auth = createAuth({
  // Keeps this app's saved session apart from any other on the same origin.
  name: "my-app",
  // Every user is checked against this schema, and typed by it.
  user: userSchema,
  // Where the session is saved: `localStorage`.
  storage: webStorage(),
  // How expired access is replaced.
  refresh: ({ refreshToken, signal }) => refresh(refreshToken, signal),
});

/** Signs in with the backend, then hands the result to Session kit. */
export async function signInWithPassword(username: string, password: string) {
  await auth.signIn(await login(username, password));
}

export type AppAuth = typeof auth;
Read the walkthrough in Getting started

What it doesn't do

Session kit never signs users in. It has no sign-in form, OAuth flow or user database: it starts where your backend's sign-in response ends. If you need those too, use an auth service or framework such as Auth.js, Better Auth or Clerk, which manage their own sessions. Read the introduction

One client, wherever your app runs

Each setup has its own entry point, a guide and a runnable example app.

  • TanStack Router"@monarcode/session-kit/tanstack-router"Guards in beforeLoad, with the session in route context.example
  • TanStack Start"@monarcode/session-kit/tanstack-start"Server rendering, with the session in an encrypted cookie.example
  • React Router"@monarcode/session-kit/react-router"Declarative and data mode, and framework mode as a SPA.example
  • React Router SSR"@monarcode/session-kit/react-router/server"Framework mode with server rendering, the session on the server.example
  • Next.js"@monarcode/session-kit/next"The App Router, with a Proxy that refreshes ahead of pages.example
  • Expo Router"@monarcode/session-kit/expo"Secure storage, a splash screen held while restoring, and deep links.example
  • Effect"@monarcode/session-kit/effect"The session as a service, and an effetch plugin.example

What you get

Schema-driven users
Any Standard Schema library, such as Zod, Valibot or ArkType, checks the user at sign-in, refresh, restore and in other tabs, and types it.
Tokens kept out of state
The auth state holds no tokens, so it's safe to render and to put in route context. Only the code that sends your requests reads them.
Guards for every router
TanStack Router, React Router in all three modes, Expo Router and Next.js send signed-out users to sign in, then back to the page they asked for.
Server rendering
With TanStack Start, Next.js or React Router, the session lives in an encrypted httpOnly cookie.
Native apps
The iOS keychain and Android Keystore, a splash screen held while restoring, and biometric locking.
Made to be debugged
Every failure Session kit reports is an AuthError with a code, and debug mode logs each step without printing a token.

Add it to your app

Releases are published from CI with npm provenance.

About 11 kB gzipped in a single-page app.

npm install @monarcode/session-kit
Get started