Skip to Content

Introduction

Session kit keeps your app signed in with your backend's tokens, through reloads, expired tokens, other tabs, and outages.

Session kit is a React library for apps whose own backend signs users in and issues access and refresh tokens. You hand it the tokens your backend returns, and it manages the session from there. It works with TanStack Router, React Router, Expo Router, TanStack Start, and Next.js.

This page explains why it exists, what it does, and the terms the rest of the docs use. To start using it, see Installation.

Why Session kit

Signing in is the easy part. Your backend checks a password and answers with an access token and a refresh token, and for a moment the app is done.

Then the user reloads the page. The access token expires halfway through a form. They open a second tab, sign out of one, and switch accounts in the other. The backend goes down for a minute, just as a refresh was due.

Every app with its own backend has to get these moments right. It usually takes a few hundred lines spread across an auth context, a fetch wrapper, and the route guards, and one or two cases stay missing until a user finds them. Session kit is that code, written once and tested with the routers and frameworks it supports.

What it does

Session kit handles each of these moments for you:

When…Session kit…
The page reloadsRestores the saved session and checks the user against your schema again. With localStorage and a synchronous schema, before the first paint.
The access token is about to expireRefreshes it early, in the background, so requests don't wait.
A request gets a 401 anywayRefreshes once, however many requests failed, then replays the safe ones.
Two tabs need a refresh at the same momentLets one tab spend the refresh token and the other reuse what it saved, where the browser supports Web Locks.
The user signs out or switches account in one tabFollows in every tab, and remounts guarded pages so nothing of the previous account stays on screen.
The backend rejects the refresh tokenSigns out in every tab and clears storage.
The backend is down when a refresh is dueKeeps the session, reports the error, and waits for a retry. An outage is not a sign-out.
A signed-out user opens a private pageSends them to sign-in, and back to where they were going afterwards.
The page renders on the serverReads the session from an encrypted cookie, so the first response is already signed in.

What it doesn't do

Session kit doesn't sign users in. It has no sign-in form, no OAuth flow, and no 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.

How it works

Session kit sits between your backend and your components. Tokens travel on the left, and only state reaches the right:

    Your backend             Session kit             Your components
 ┌───────────────┐      ┌───────────────────┐      ┌──────────────────┐
 │ POST /login   │ ───▶ │ signIn()          │ ───▶ │ useAuth()        │
 │ POST /refresh │ ◀──▶ │ refresh, on time  │      │ route guards     │
 │ GET  /orders  │ ◀─── │ authFetch()       │ ◀─── │ loaders, queries │
 └───────────────┘      └───────────────────┘      └──────────────────┘
   issues tokens          keeps the session        state, never tokens

Your backend issues the tokens. Session kit saves them, refreshes them on time, and attaches them to your requests. Your components and route guards read the session's state, and never see a token.

Five terms come up throughout:

  • Tokens are what your backend issues. The access token goes with each request and is short-lived. The refresh token, if there is one, is exchanged for a new access token when the old one expires.
  • The client, from createAuth, holds the tokens and does the work: saving, restoring, refreshing, and telling other tabs. An app usually has one.
  • A session is who is signed in: { sessionId, user }. It never holds tokens, so it is safe to render and to pass around.
  • State is what components read: status, user, sessionId, and error, updated as the session changes.
  • Credentials are the access token and its expiry, given only to code that attaches them to requests, such as createAuthFetch.

status says where the session stands, and checking it narrows user:

statusMeaninguser
initializingThe saved session is being restored.null
authenticatedAccess is usable.Set
refreshingA refresh is replacing expired or rejected access.Set
lockedWith locking, a session is saved, but its tokens wait for unlock().Set
unauthenticatedNo session; sign-in is needed.null
unavailableRestoring failed, or a failed refresh left no usable access; see error.null

The session is kept in one of two places, depending on where your app renders:

In the browser or on the deviceOn the server
ForSingle-page apps and Expo appsTanStack Start, Next.js, React Router with SSR
ClientcreateAuthcreateServerAuth, with createMirrorAuth in the browser
Saved inlocalStorage, sessionStorage, the iOS keychain, or Android encrypted storageAn encrypted httpOnly cookie
The refresh tokenStays in that storageNever reaches the browser

Hooks, guards, and tab sync work the same in both.

Next steps

  • Installation: install the package and find the guide for your setup.
  • Getting started: build a small app that signs in, stays signed in, and calls an API.
  • Server rendering: keep the session on the server with TanStack Start, Next.js, or React Router.

On this page