rsc-swr
v0.1.0
Published
rsc-swr provides ergonomic React Server Component refreshers.
Downloads
122
Maintainers
Readme
rsc-swr
Ergonomic React Server Component refreshers for the Next.js App Router.
rsc-swr wraps router.refresh() from next/navigation with SWR-shaped semantics so your server-rendered UI stays fresh without reaching for useSWR or a client data layer. Drop in one component, or call one hook, and pages re-fetch their RSC payload on focus, tab visibility, reconnect, or a polling interval.
Install
bun add rsc-swr
# or
pnpm add rsc-swrPeer dependencies:
next>= 16.0.0react>= 19.0.0react-dom>= 19.0.0
Usage
Drop-in component
Mount RscSoftRefresh anywhere inside a client component tree. It renders nothing.
"use client";
import { RscSoftRefresh } from "rsc-swr";
export function LiveLayout({ children }: { children: React.ReactNode }) {
return (
<>
<RscSoftRefresh refreshInterval={30_000} />
{children}
</>
);
}Hook with programmatic mutate
Use the hook when you want to trigger a refresh yourself, for example after a server action.
"use client";
import { useRscSoftRefresh } from "rsc-swr";
export function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
const { mutate } = useRscSoftRefresh({ refreshInterval: 10_000 });
return (
<button
onClick={async () => {
await onSave();
mutate();
}}
>
Save
</button>
);
}Options
All options mirror SWR's vocabulary.
| Option | Type | Default | Meaning |
| ----------------------- | --------- | ------- | ------------------------------------------------------- |
| refreshInterval | number | 0 | Polling interval in ms. 0 disables polling. |
| revalidateOnFocus | boolean | true | Refresh when the window regains focus. |
| revalidateOnVisible | boolean | true | Refresh when the tab becomes visible again. |
| revalidateOnReconnect | boolean | true | Refresh when the browser fires online. |
| refreshWhenHidden | boolean | false | Allow auto-refreshes while the tab is hidden. |
| refreshWhenOffline | boolean | false | Allow auto-refreshes while navigator.onLine is false. |
| dedupingInterval | number | 2000 | Minimum ms between any two refreshes. |
| focusThrottleInterval | number | 5000 | Minimum ms between focus-triggered refreshes. |
Behavior notes
mutate()is imperative. It bypasses every guard:refreshWhenHidden,refreshWhenOffline,focusThrottleInterval, anddedupingInterval. Every call triggers a freshrouter.refresh(), so amutate()right after an auto-refresh still shows the latest data. It does bump the internal dedupe timestamp, so auto-refreshes that fire immediately after amutate()are still deduped.- Per-instance dedupe state. Timestamps live in
useRef, so navigating between routes does not leak throttle state from the previous page. - No
isValidating, no error callback.router.refresh()does not return a Promise, and the App Router routes errors through its own error boundary. There is nothing meaningful to expose, sorsc-swrdoes not invent it. - Default polling is off.
refreshInterval: 0means no timer is created.
Development
This project uses Bun for builds and tests.
bun install
bun test # run tests (happy-dom + @testing-library/react)
bun run typecheck # tsc --noEmit
bun run build # esm bundle + .d.ts
bun run check # ultracite lint + format check
bun run fix # ultracite autofixSource lives in src/, tests in test/, built output in dist/. CI runs typecheck, test, and build on every push and PR.
License
MIT (c) Sunghyun Cho
