npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@userspace-auth/react

v1.12.9

Published

React SDK for Userspace Auth, session state, tokens, and auth guards

Downloads

1,029

Readme

Userspace React SDK (@userspace-auth/react)

This SDK gives session state, session tokens and auth guards to a React application that uses Userspace.

The sign-in occurs on the hosted pages of your Userspace instance. This package tells your application who is signed in and gives you a session token for calls to your backend.

Install

npm i @userspace-auth/react

React 18 or 19 is necessary, and the package declares them as peer dependencies. The only runtime dependency is @userspace-auth/userspace-js. The package imports it lazily for the x-* elements and the settings popup. Thus a page that only uses session tokens never loads it.

The package is ESM only. It works in Vite, Next.js, Remix, Parcel, esbuild, webpack 5 and each other tool that resolves import. A CommonJS-only setup cannot require() it. An example is an older Jest config without ESM support.

Quick start

Wrap your application one time:

import { UserspaceProvider } from '@userspace-auth/react'

export function App() {
  return (
    <UserspaceProvider baseUrl="https://acme.userspace.tech">
      <Dashboard />
    </UserspaceProvider>
  )
}

Then read the session in any component below the provider:

import { useSession, SignedIn, SignedOut } from '@userspace-auth/react'

function Dashboard() {
  const { session, isLoading } = useSession()

  if (isLoading) return <p>Loading…</p>

  return (
    <>
      <SignedIn>
        <p>Signed in as {session?.identity?.email}</p>
      </SignedIn>
      <SignedOut>
        <a href="https://acme.userspace.tech/sign-in">Sign in</a>
      </SignedOut>
    </>
  )
}

Calling your backend

Your own backend needs no token at all

By default, <UserspaceProvider> keeps the session token of the signed-in user in a __Host-session cookie on your host. The provider refreshes the cookie before the session token expires. Your backend reads the session token from the cookie. Thus a page load, a form post or a plain fetch to your origin is authenticated:

// No Authorization header: the browser sends the cookie on its own.
const res = await fetch('/api/me')

The cookie transport is off by default in your backend. Set it to on. For Go:

u, err := userspace.New(userspace.Options{CookieTransport: true})

The cookie transport has two effects. First, your backend refuses a request that changes state (POST, PUT, PATCH, DELETE) when the request sends the cookie from a different origin. This prevents an action by a different site in the name of your user. Second, your backend gets a third answer together with signed in and signed out.

The third answer: "unknown". The session token in the cookie has a life of minutes. A browser that was idle can send a request with no session token while the user is signed in. This occurs because the SDK did not refresh the session token yet. Your backend reports this state as unknown, not as signed out.

This is important for pages that you render on the server. For React, these are the App Router and getServerSideProps. After a pause longer than the life of the session token, the first request renders unknown. The page becomes correct a moment later, when this SDK refreshes the session token on the client.

Thus render unknown with your application shell, not with your sign-in page. If you render the sign-in page, a signed-in user sees it for a moment. A server-side refresh that fully removes this flicker is not available yet.

unknown never gives permission for an action. For each authorization decision, use unknown the same as signed out. The backend SDK does this.

Running on bearer headers alone

Set cookie={false}. Then the provider writes no cookie, runs no refresh timer, and gets a session token when a call asks for one. Use this mode if you only call an API on a different origin, or if you do not want a cookie on your host.

The SDK also uses this mode automatically in one condition. __Host- cookies must have Secure. If the browser does not store such a cookie on your page, the SDK continues as with cookie={false}. The SDK writes one message about this to the console. This is important for development on plain http://localhost, where browsers do not agree if a page can set a Secure cookie.

Sending the token yourself

getToken() returns a session token that your backend can verify locally. Use it for an API on a different origin, where the browser sends no cookie of yours. Send the session token as a bearer token:

function Profile() {
  const { getToken } = useSession()

  const load = async () => {
    const token = await getToken()
    if (!token) return // signed out

    const res = await fetch('https://api.example.com/me', {
      headers: { Authorization: `Bearer ${token}` },
    })
    return res.json()
  }

  // …
}

From your api layer, outside React

Your data layer can be a plain module of functions that use one shared fetcher. Then import getToken directly, and do not pass a hook through the data layer. It is the same function that useSession() returns, and thus the two cannot become different:

import { getToken } from '@userspace-auth/react'

async function authorized(path: string, init: RequestInit = {}) {
  const token = await getToken()
  const headers = new Headers(init.headers)
  if (token) headers.set('Authorization', `Bearer ${token}`)
  return fetch(`https://api.example.com${path}`, { ...init, headers })
}

export const fetchUsers = () => authorized('/users').then((r) => r.json())

If you call getToken before <UserspaceProvider> renders, it waits until the provider configures the SDK. It does not send a request to a base URL that is not fully configured. The provider stays the only location of the configuration. If the provider never mounts, the call writes a warning after a few seconds and does not wait with no sign.

On the backend, verify the session token with the Userspace SDK for your stack. For Go, that SDK is github.com/userspace-auth/sdk-go. Any JOSE library also works. The session token is a standard RS256 JWT, and your instance publishes the keys that sign it at https://<your-instance>/.well-known/jwks.json.

Send this session token only to your backend. A session token gives access to no API of the platform. The Frontend API, which is the auth server of your instance, accepts only its own token with a separate signature. But each backend with the same base URL accepts the session token until it expires.

API

<UserspaceProvider>

| Prop | Type | Meaning | | --------------- | ----------- | ------- | | baseUrl | string | The base URL of your instance, for example https://acme.userspace.tech. | | webcomponents | boolean | Registers the x-* custom elements (<x-signed-in>, <x-trigger>, ...). Then you can use them in JSX directly. Off by default. | | preload | boolean | Loads the bundle of the settings popup when the browser is idle. Then the first openSettings() opens without a download. Off by default. | | autoLogin | boolean | Redirects signed-out visitors to the hosted sign-in page, with the current URL as redirect_uri. Off by default. | | cookie | boolean | Keeps the session token in a __Host-session cookie on your host, and refreshes the cookie before the session token expires. Thus your backend can read it. On by default. With false, the SDK uses only bearer headers, writes no cookie and runs no timer. | | children | ReactNode | Your application. |

The provider must wrap each component that uses this package. Outside the provider, hooks throw a clear error and do not return an empty session.

webcomponents loads the element bundle lazily. When the flag is off, the browser downloads none of the bundle. The tags have TSX types when the flag is on and when it is off.

autoLogin reads the session one time on mount, and it shares the request that useSession() makes. It redirects only when the result is signed out. A transport error is an unknown state, not a signed-out state, and never causes a redirect. Use autoLogin for applications with no public pages. For applications with a signed-out state, render <SignedOut> and do not use autoLogin.

useSession()

const { session, getToken, isLoading, error } = useSession()

| Field | Type | Meaning | | ----------- | ------------------------------- | ------- | | session | SessionData \| null | The session, or null while the session loads or when the user is signed out. | | getToken | () => Promise<string \| null> | Returns a session token for your backend, or null if the user is signed out. Rejects on transport errors. | | isLoading | boolean | True until the first load is complete. | | error | unknown | A real failure. The signed-out state is not an error. It is session: null with error: null. |

session.identity.id is the stable user id. Use it as the key of your data. Never use session.session.id as the key, because it changes at each sign-in.

getToken()

import { getToken } from '@userspace-auth/react'

The type is () => Promise<string | null>. It is the same function that useSession() returns. You can import it without a hook, and thus a fetcher at module scope can use it. It resolves to null when the user is signed out, and it rejects on transport errors. If you call it before <UserspaceProvider> configures the SDK, it waits for the provider. It works only on the client, and Next.js gives the cause.

openSettings()

import { openSettings } from '@userspace-auth/react'

;<MenuItem onClick={() => openSettings()}>Account settings</MenuItem>

openSettings() opens the Userspace settings popup from your components: a profile menu item, a toolbar button, or any element that the user can click. All callers share one popup instance. It works with the webcomponents flag and without it, because the popup code loads lazily on the first call.

Give preload to the provider to move that download (and the config load of the SDK) to idle time. Then the first click opens the popup immediately. The popup opens immediately and loads its content in position. Without an active session, the popup closes again and writes a console warning. Thus keep the control in <SignedIn>.

signIn()

import { signIn } from '@userspace-auth/react'

;<Button onClick={() => signIn()}>Sign in</Button>

signIn() goes to the hosted sign-in page. It sends the current URL as redirect_uri, and thus the user returns to the same page after the sign-in. With signIn({ redirectUri }), the user goes to a different address. Use this for a page that sends the visitor to a different location. redirectUri must be an absolute URL, and its origin must be on the allowlist of the instance. The server validates the target, and a target that the server does not trust becomes the app URL.

signIn() is the function equivalent of the autoLogin flag. It does the same navigation, but on a click and not on each signed-out visit. If you call it before <UserspaceProvider> configures the SDK, it waits for the provider, the same as getToken().

signOut()

import { signOut } from '@userspace-auth/react'

;<MenuItem onClick={() => signOut()}>Sign out</MenuItem>

signOut() signs the user out and goes to the hosted sign-in page. With signOut({ redirectUri }), it goes to an absolute URL whose origin is on the allowlist of the instance. The server validates the target, and a target that the server does not trust becomes the sign-in page.

A sign-out stops each session in this browser, not only the current session. The server stops the full cookie-scoped session, and thus other tabs also sign out. If you call signOut() before <UserspaceProvider> configures the SDK, it waits for the provider, the same as getToken().

<SignedIn> / <SignedOut>

Each component renders its children only in its own state. The two components render nothing while the session loads. Thus no component shows for a moment before the session is known. A session with an error is signed out for these components. SignedIn never renders on an unknown state.

Types

The package exports SessionData, SessionInfo, UserspaceUser, EmailAddress, UseSessionResult, UserspaceProviderProps, GuardProps, and UserspaceTagName. The package includes its own declarations. Thus strict: true works with no @types/* package and no any.

Caching behaviour

The SDK fetches the session one time for each page load and keeps it. Each component that calls useSession() shares that one request. This is also true when many components mount at the same time on a cold cache.

These functions are intentionally not included: revalidation on window focus or reconnect, polling, and manual mutation. If the SDK must read the session again after an action that changes it, reload the page or move that state into your own store. This contract is intentionally smaller than the contract of a general data-fetching library. As a result, the package has no data-fetching dependency. Thus the package cannot conflict with the data layer that you use.

The session token is a different subject, and the SDK does refresh it automatically. When the cookie transport is on, the SDK refreshes the session token on a timer before it expires. The timer stops while the tab is hidden, and the SDK refreshes the session token when the tab shows again. When the cookie transport is off, the SDK gets a new session token each time a call needs one. These refreshes do not change the session that this section describes.

Next.js

The bundle has a 'use client' directive. Thus you can import it from an App Router server component, and you do not have to add the directive. Components that use useSession are client components, the same as all components that use hooks.

The directive does not change server rendering. Next.js prerenders and hydrates client components as usual, and this package renders a defined loading state on the server.

The directive does prevent a call to getToken from a server component, a server action or a route handler. This is intentional, not an error. The token cache and the configured base URL are module singletons. Thus in a long-lived server process, the session token of one request stays in the same cache that the next request reads.

To serve session tokens on the server, each call must get the identity of its request. That is a different contract, not a second build of this contract. Use this package from the client, and let your server read the session that your backend trusts.

License

MIT. Refer to LICENSE.