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

better-auth-offline

v0.0.2

Published

Caches better-auth API responses so your app keeps working when the network doesn't.

Readme

better-auth-offline

NPM Version NPM Downloads

Offline-first plugin for better-auth. Transparently caches auth API responses so your app keeps working when the network doesn't.

Why offline-first?

The offline-first community is growing — and for good reason. Users expect apps to work everywhere: on flaky Wi-Fi, in airplane mode, in rural areas with spotty coverage, and on mobile devices that constantly switch between networks. PWAs, local-first architectures, and edge computing are making offline-capable apps the norm, not the exception.

Authentication is often the first thing that breaks when the network drops. A user who was just signed in gets thrown to a login screen — or worse, a blank page. better-auth-offline fixes that. It's a drop-in plugin that caches auth responses so your users stay authenticated and your UI stays functional, even offline.

No changes to your existing code. Just add the plugin.

Features

  • Network-first with cache fallback — always tries the network first, serves cached data when offline
  • Drop-in integration — one line to add, zero changes to your existing auth code
  • Automatic cache invalidation — clears cache on sign-in/sign-out to prevent cross-user data leaks
  • Configurable allowlist — choose which endpoints to cache, extend or replace the defaults
  • IndexedDB storage — persistent cache that survives page refreshes, with a pluggable storage adapter interface
  • Online status tracking — reactive useOnlineStatus() hook for your UI
  • SSR safe — works in server-side rendering environments without errors

Quick Start

Install the plugin:

npm install better-auth-offline

Add it to your auth client:

import { createAuthClient } from "better-auth/react";
import { offlinePlugin } from "better-auth-offline";

const authClient = createAuthClient({
  plugins: [offlinePlugin()],
});

That's it. Your auth API responses are now cached and served when offline.

How It Works

  1. Your app makes a GET request to an allowlisted auth endpoint (e.g. /api/auth/get-session)
  2. The plugin lets the request go to the network as normal
  3. On success, the JSON response is cached in IndexedDB (fire-and-forget — doesn't slow down the response)
  4. If the network request fails (no connectivity), the plugin serves the cached response instead
  5. Cached responses include an X-Offline-Cache: true header so you can detect them if needed

Only GET requests to allowlisted paths are cached. Mutations (sign-in, sign-out, etc.) always go to the network.

Configuration

Default mode — extend or exclude paths

By default, the plugin caches a curated set of common auth endpoints. You can extend or trim this list:

offlinePlugin({
  // Add your custom endpoints to the default list
  includePaths: ["/my-custom-endpoint", "/user/preferences"],

  // Remove endpoints you don't need cached
  excludePaths: ["/admin/list-users"],
})

Custom mode — full control

Replace the default allowlist entirely:

offlinePlugin({
  mode: "custom",
  allowlist: [
    "/get-session",
    "/list-accounts",
    "/my-app/specific-endpoint",
  ],
})

Custom storage adapter

Swap out IndexedDB for any storage backend:

import { offlinePlugin } from "better-auth-offline";

offlinePlugin({
  storage: myCustomAdapter,
})

See Custom Storage Adapters for the interface.

Default Allowlist

These endpoints are cached by default (in mode: "default"):

| Category | Endpoints | |----------|-----------| | Core | /get-session, /list-sessions, /list-accounts, /account-info | | Organization | /organization/list, /organization/get-active-member, /organization/get-active-member-role, /organization/get-full-organization, /organization/list-members, /organization/list-teams, /organization/list-invitations | | Admin | /admin/list-users | | Multi-session | /multi-session/list-device-sessions | | Passkey | /passkey/list-user-passkeys | | API Key | /api-key/get, /api-key/list |

Path matching uses suffix matching, so these work regardless of your baseURL or path prefix configuration.

Online Status

The plugin exposes a reactive online/offline status hook:

function MyComponent() {
  const onlineStatus = authClient.useOnlineStatus();

  return (
    <div>
      {onlineStatus ? "Online" : "Offline"}
    </div>
  );
}

This tracks the browser's navigator.onLine property and listens for online/offline events. In SSR environments, it defaults to true.

Cache Management

Automatic invalidation

The cache is automatically cleared when a user signs in or signs out. This prevents stale data from one user being served to another.

Manual cache clearing

await authClient.clearCache();

Detecting cached responses

Responses served from cache include the header X-Offline-Cache: true.

Custom Storage Adapters

The plugin uses IndexedDB by default, but you can provide any storage backend that implements the StorageAdapter interface:

import type { StorageAdapter } from "better-auth-offline";

const myAdapter: StorageAdapter = {
  async get(key: string): Promise<unknown | null> {
    // Return cached value or null
  },
  async set(key: string, value: unknown): Promise<void> {
    // Store the value
  },
  async delete(key: string): Promise<void> {
    // Remove a single entry
  },
  async clear(): Promise<void> {
    // Remove all entries
  },
};

offlinePlugin({ storage: myAdapter });

The built-in IndexedDB adapter can also be customized with a different database name:

import { createIndexedDBAdapter } from "better-auth-offline";

offlinePlugin({
  storage: createIndexedDBAdapter("my-custom-db-name"),
});

API Reference

Exports from better-auth-offline

| Export | Type | Description | |--------|------|-------------| | offlinePlugin | (options?: OfflinePluginOptions) => BetterAuthClientPlugin | Main plugin factory | | createIndexedDBAdapter | (dbName?: string) => StorageAdapter | Creates an IndexedDB storage adapter | | createOnlineStatusAtom | () => Atom<boolean> | Creates a nanostores atom tracking online status | | StorageAdapter | Type | Interface for custom storage backends | | OfflinePluginOptions | Type | Plugin configuration options | | CacheEntry | Type | Shape of cached entries ({ data: unknown, cachedAt: number }) |

Exports from better-auth-offline/adapters

| Export | Type | Description | |--------|------|-------------| | createIndexedDBAdapter | (dbName?: string) => StorageAdapter | IndexedDB storage adapter (alternative import path) |

Example App

The example/ directory contains a full Next.js app demonstrating the plugin in action:

  • Sign in with demo credentials ([email protected] / demo)
  • See session data cached and served offline
  • Toggle offline mode in DevTools to test
  • Inspect the IndexedDB cache directly

Run it:

cd example
bun install
bun dev

Roadmap

  • Prewarming / Eager Data Loading — Mechanism to eagerly fetch and cache endpoints on app initialization, so the cache is warm before going offline. Critical for apps that go offline unpredictably (mobile, field workers).

Contributing

Contributions are welcome! This project uses:

  • bun as the package manager
  • vitest for testing
  • tsup for building
  • turbo for monorepo orchestration
# Install dependencies
bun install

# Run tests
cd better-auth-offline && bun test

# Build
cd better-auth-offline && bun run build

License

MIT