→ Session
Session Timeout Banner
Warns before the shared session expires, with a one-click way to log out immediately.
Best used for Warning users before a shared session expires, especially with a short or activity-based timeout.
Requires a <SessionProvider> ancestor — it watches a session something else already established, it
doesn't create one.
- components
- knock-codes
- core
- react
"use client";
import { useEffect, useState } from "react";
import { useKnockCodesContext } from "./KnockCodesProvider.tsx";
import { EXPIRY_POLL_INTERVAL_MS } from "./types.ts";
import { cx } from "./cx.ts";
export interface SessionTimeoutBannerProps {
/** Show the banner once this many ms remain before expiry. @default 60_000 */
warnBeforeMs?: number;
/** Switches to the stronger "about to expire" styling once this many ms remain. @default 10_000 */
criticalBeforeMs?: number;
className?: string;
}
/**
* Warns before the shared session (from `<KnockCodesProvider>`) expires,
* with a one-click way to log out immediately instead of waiting for the
* automatic expiry. Renders nothing outside the warning window or with no
* active session — requires a provider, since a banner needs a session to
* watch that something else already established.
*/
export function SessionTimeoutBanner({ warnBeforeMs = 60_000, criticalBeforeMs = 10_000, className }: SessionTimeoutBannerProps) {
const { session, logout } = useKnockCodesContext();
const [now, setNow] = useState(() => Date.now());
useEffect(() => {
if (!session) return;
const interval = setInterval(() => setNow(Date.now()), EXPIRY_POLL_INTERVAL_MS);
return () => clearInterval(interval);
}, [session]);
if (!session) return null;
const remainingMs = session.expiresAt - now;
if (remainingMs > warnBeforeMs || remainingMs <= 0) return null;
const remainingSeconds = Math.max(0, Math.ceil(remainingMs / 1000));
const critical = remainingMs <= criticalBeforeMs;
return (
<div
role="alert"
style={{ fontFamily: "var(--ag-font, inherit)" }}
className={cx(
"flex items-center justify-between gap-4 rounded-[var(--ag-radius,0.375rem)] border px-4 py-2 text-sm",
critical
? "border-[#e5484d]/40 bg-[#e5484d]/10 text-[#a5262b] dark:border-[#ff6169]/40 dark:bg-[#ff6169]/10 dark:text-[#ffb3b6]"
: "border-[#ffb020]/40 bg-[#ffb020]/10 text-[#7a4f0d] dark:border-[#ffb020]/40 dark:bg-[#ffb020]/10 dark:text-[#ffd18a]",
className
)}
>
<span className="inline-flex items-center gap-2 font-medium">
<span
aria-hidden="true"
className={cx(
"h-1.5 w-1.5 shrink-0 rounded-full",
critical ? "animate-pulse bg-[#e5484d] dark:bg-[#ff6169]" : "bg-[#ffb020]"
)}
/>
{critical ? "Expiring now" : "Expires soon"} — session ends in {remainingSeconds}s.
</span>
<button type="button" onClick={logout} className="font-medium underline underline-offset-2 hover:no-underline">
Log out now
</button>
</div>
);
}
Add this block to your project
CLI
npx shadcn@latest add http://localhost:3000/r/react/session-timeout-banner.jsonRunning this site somewhere other than localhost? Set NEXT_PUBLIC_SITE_URL and this command switches to that host automatically.
Also installs
- Knock Codes Core
- Knock Codes Types
- useKnockCodes
- Session Provider
Files created (8)
- components/knock-codes/core/hash.ts
- components/knock-codes/core/verify.ts
- components/knock-codes/core/session.ts
- components/knock-codes/core/storage.ts
- components/knock-codes/react/types.ts
- components/knock-codes/react/useKnockCodes.ts
- components/knock-codes/react/KnockCodesProvider.tsx
- components/knock-codes/react/SessionTimeoutBanner.tsx
No CLI? Copy the files by hand
- Open the Code tab in the preview above.
- Create each path listed below in your project and paste its contents in.
- Do the same for anything listed under “Also installs”, if present.
→ Props
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
| warnBeforeMs | number | 60000 | Show the banner once this many ms remain before expiry. |
| className | string | — | Extra classes on the banner. |
Accessibility
role="alert" — announced immediately when it appears, not just on next poll of an aria-live region, since an imminent session expiry is time-sensitive.
Customization
Renders nothing outside the warning window or with no active session, so it's safe to mount unconditionally near the top of a layout. Adjust `warnBeforeMs` to change how early it appears.
→ Compose with
Blocks that pair well with this one
These combine naturally with this block, whether as a shared shell, a shared session, or a common fallback.
- Session ProviderShares one useKnockCodes session across a tree, so multiple components stay in sync.
- Logout ButtonReads the shared session from a Session Provider and clears it on click.
- Access ReceiptA small session audit strip shown after unlock — timestamp, storage mode, timeout, and verification strategy.