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 🙏

© 2025 – Pkg Stats / Ryan Hefner

contection-react-router-cookie-adapter

v2.3.0

Published

A React Router cookie-based persistence adapter for contection that automatically saves and restores state to browser cookies

Readme

contection-react-router-cookie-adapter

A cookie-based persistence adapter for contection that automatically saves and restores state to browser cookies. Designed for React Router [v7] applications with full server-side rendering support.

Overview

The ReactRouterCookieAdapter seamlessly integrates with contection stores to provide automatic state persistence using HTTP cookies. Unlike localStorage-based adapters, cookies are accessible on both server and client, enabling true SSR with state hydration. The adapter handles serialization, validation, cookie management, and gracefully degrades when cookies are unavailable.

Installation

npm install contection contection-react-router-cookie-adapter
# or
pnpm add contection contection-react-router-cookie-adapter

Basic Usage

Step 1: Prepare Store

Use prepareStore to set up the adapter and create a getStore function for server-side data fetching:

// stores/data.ts
import { prepareStore } from "contection";
import { ReactRouterCookieAdapter } from "contection-react-router-cookie-adapter";

interface Store {
  theme: "light" | "dark";
  userId: string;
  language: string;
}

const appStoreInitialData: Store = {
  theme: "light",
  userId: "",
  language: "en",
};

export const { getStore, initialData, options } = prepareStore(
  appStoreInitialData,
  { adapter: new ReactRouterCookieAdapter() }
);

Step 2: Create Store

Create the store instance using the prepared initialData and options:

// stores/index.ts
import { createStore } from "contection";
import { initialData, options } from "./data";

export const AppStore = createStore(initialData, options);

Step 3: Add Provider to root or route

// routes/index/page.tsx
export default function HomePage() {
  return (
    <AppStore>
      // ...
    </AppStore>
  );
}

Step 4: Add server store to Provider (Optional)

If you need to use cookie data when rendering client-side components on the server (with SSR or cacheComponents), use getStore in the provider component to populate the store with cookie data:

// routes/index/page.tsx
import { getStore } from "../stores/data";

export const loader = async ({ request }: Route.LoaderArgs) => {
  const store = await getStore(request);

  return store;
}

export async function ServerComponent({ loaderData }: { loaderData: Awaited<ReturnType<typeof loader>> }) {
  return <AppStore value={loaderData}>Theme: {loaderData.theme}</AppStore>;
}

Step 5: Use Store

Use the store in client components with contection hooks, or access it via getStore in React Server Components:

// components/ThemeToggle.tsx
"use client";

import { useStore } from "contection";
import { AppStore } from "../stores";

export const ThemeWrapper = () => {
  const store = useStore(AppStore);

  return (
    <div data-theme={store.theme}>
      // ...
    </div>
  );
};
// app/page.tsx (React Server Component)
import { type Route } from "./+types/page";
import { getStore } from "../stores/data";

export const loader = async ({ request }: Route.LoaderArgs) => {
  const store = await getStore(request);

  return store;
}

export async function ServerComponent({ loaderData }: { loaderData: Awaited<ReturnType<typeof loader>> }) {
  return <div>Theme: {loaderData.theme}</div>;
}

Configuration Options

prefix (string, default: "__ctn_")

A prefix added to all cookie keys to avoid conflicts with other applications or cookies.

new ReactRouterCookieAdapter({
  prefix: "my-app-",
});

saveKeys (array, optional)

An array of store keys to persist. If provided, only these keys will be saved to cookies. If omitted, all keys are persisted. This is particularly useful for excluding sensitive, large data or non-serializable data that shouldn't be stored in cookies.

new ReactRouterCookieAdapter({
  saveKeys: ["theme", "preferences"],
  // userId and other keys won't be persisted
});

rawLimit (number, default: 4096)

Maximum size (in bytes) for each cookie value. Values exceeding this limit are not persisted. This helps prevent cookie overflow issues, as browsers typically limit cookie sizes to 4KB. The limit includes the key name and prefix.

new ReactRouterCookieAdapter({
  rawLimit: 2048, // 2KB limit
});

flags (CookieFlags, default: {})

Cookie attributes that control security, expiration, and scope. The adapter provides sensible defaults but allows full customization.

new ReactRouterCookieAdapter({
  flags: {
    path: "/",
    domain: ".example.com",
    expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days
    maxAge: 60 * 60 * 24 * 30, // 30 days in seconds
    secure: true, // HTTPS only
    sameSite: "strict", // "strict" | "lax" | "none"
  },
});

Default values:

  • path: "/"
  • expires: 30 days from now
  • maxAge: 30 days (in seconds)
  • secure: true
  • sameSite: "strict"

validate (function, optional)

A validation function that receives the loaded data and returns true if valid, or false/null/undefined if invalid. Invalid data is automatically removed from cookies. This is essential for handling schema migrations or corrupted cookie data.

import { z } from "zod";

const storeSchema = z.object({
  theme: z.enum(["light", "dark"]),
  preferences: z.object({
    language: z.string(),
    notifications: z.boolean(),
  }),
});

new ReactRouterCookieAdapter({
  validate: (data) => {
    const partialSchema = validate.pick(
      Object.fromEntries(Object.keys(data).map((k) => [k, true]))
    );
    const result = partialSchema.safeParse(data);
    return result.success ? result.data : false;
  },
});

autoSync (number | null, default: null)

Enables periodic synchronization from cookies to the store at the specified interval (in milliseconds). Useful for keeping the store in sync with cookie changes from other browser tabs, external modifications, or server updates. When set to null, automatic synchronization is disabled. It is recommended to use values greater than 200ms to avoid performance issues.

new ReactRouterCookieAdapter({
  autoSync: 1000, // Sync every second
});

onDestroy ("cleanup" | "ignore", default: "ignore")

Behavior when the store is destroyed:

  • "cleanup" - Removes all persisted cookies
  • "ignore" - Leaves cookies intact
new ReactRouterCookieAdapter({
  onDestroy: "cleanup",
});

Server-Side Rendering

The adapter automatically provides getServerSnapshot for SSR support. When using prepareStore, the adapter's getServerSnapshot method is extracted and made available via the returned getStore function.

See the Basic Usage section above for a complete example of setting up SSR with prepareStore and getStore.

Browser Compatibility

The adapter automatically detects cookie availability and gracefully degrades if cookies are disabled or unavailable (e.g., in private browsing mode with strict cookie policies). When cookies are unavailable, the adapter becomes a no-op for persistence but doesn't break your application.

Cookie Size Considerations

Cookies have strict size limitations (typically 4KB per cookie). The adapter:

  • Automatically checks size before persisting (via rawLimit)
  • Skips persistence for values that exceed the limit
  • Uses URL encoding for safe cookie value storage
  • Splits data across multiple cookies (one cookie per store key)

For large state objects, consider using saveKeys to persist only essential data, or use a combination of cookies for small critical data and localStorage for larger data.

Examples

The repository includes example applications demonstrating react-router-cookie adapter capabilities:

  • react-routerjs-bsky - Showcases performance improvements in Next.js applications using cacheComponents and a combined client-server architecture with react-router-cookie adapter and storage adapter for state persistence. Preview

License

MIT