IdleLock
Locks the app after a period of inactivity: a Still there countdown dialog first, then your LockScreen over the inert app.
Code
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";export function Shell({ children, user }: { children: React.ReactNode; user: { name: string } }) { return ( <IdleLock timeoutSeconds={600} warningSeconds={30} lockScreen={({ unlock }) => <LockScreen user={user} methods={["password"]} onUnlock={async (attempt) => (await verify(attempt)) ? unlock() : { error: "Wrong password." }} />} > {children} </IdleLock> );}declare function verify(attempt: unknown): Promise<boolean>;Auth · beta
Live examples and controls: IdleLock in the lab.
Install
npx shadcn@latest add https://docs.nasaqui.com/r/idle-lock.jsonWraps the app so it locks itself after inactivity. Ten seconds of warning is not enough for a person who stepped away,
so the "Still there?" dialog counts down first and only its buttons answer it. Then your LockScreen covers the app,
which stays mounted but inert and hidden from assistive tech. The timer is a courtesy: expire the session on the server too.
When to use
- An app with sensitive data on shared or unattended screens.
- A "Lock" menu item (controlled
locked).
When not to use
- A session that already ended: use
session-expired. - Pages that must not lock, such as a live call: set
disabled.
Import
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"Quick start
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";
export function Shell({ children, user }: { children: React.ReactNode; user: { name: string } }) {
return (
<IdleLock
timeoutSeconds={600}
warningSeconds={30}
lockScreen={({ unlock }) => <LockScreen user={user} methods={["password"]} onUnlock={async (attempt) => (await verify(attempt)) ? unlock() : { error: "Wrong password." }} />}
>
{children}
</IdleLock>
);
}
declare function verify(attempt: unknown): Promise<boolean>;Anatomy
IdleLock data-slot="idle-lock"
├─ app data-slot="idle-lock-app" (inert while locked)
├─ IdleWarningDialog data-slot="idle-warning" role="timer"
└─ lock overlay data-slot="idle-lock-screen"API
IdleLock: div props plus children, lockScreen (a node, or ({ unlock }) => node), timeoutSeconds (300),
warningSeconds (30, 0 for none), locked / defaultLocked / onLockedChange(locked, reason), disabled, unmountWhenLocked, labels.
useIdleLock: { timeoutSeconds, warningSeconds, disabled, onIdle } returns { phase, secondsLeft, stayActive, reset }.
Build your own UI on it. It re-checks when the tab becomes visible, so a sleeping laptop locks on wake.
IdleWarningDialog: open, secondsLeft, warningSeconds, onStay, onLockNow, labels.
Examples
A manual lock button
import { Button, IdleLock } from "@fadymondy/nasaq/web";
import { useState } from "react";
export function App() {
const [locked, setLocked] = useState(false);
return (
<IdleLock locked={locked} onLockedChange={setLocked} lockScreen={<p>Locked</p>}>
<Button onClick={() => setLocked(true)}>Lock</Button>
</IdleLock>
);
}Accessibility
- The countdown is a
role="timer", announced every ten seconds and in the last five, not every tick. - The warning is an alert dialog with focus on "Stay signed in". Input during the warning does not cancel it.
- The locked app is
inertandaria-hidden, so focus and screen readers cannot reach it.
RTL & i18n
- English and Arabic built in; pass
labels. The countdown is always left to right with Latin digits.
Styling & tokens
- Tokens only. The overlay is
z-[60], above dialogs.
Do / Don't
- Do end the session on the server as well.
- Do use
unmountWhenLockedwhen the page content is confidential. - Don't set a warning longer than the timeout (it is clamped).
Related
Lab
https://docs.nasaqui.com/?path=/docs/components-auth-pages-idle-lock--docs
ForgotPasswordForm
Request a password reset link by email, then a Check your inbox state with a resend countdown and a change-email link; en and ar built in.
InviteAccept
The public accept-invitation page. It covers a valid invitation with accept and decline or sign in first, an expired one, one sent to another account, one already used and one that was cancelled.