→ Routing & Layout
Protected Route
Knock Codes shaped for route-level guarding, with an optional custom unauthorized state.
Best used for Route-level guarding in React Router or similar, especially with a custom denial screen.
Meant for a React Router element or any layout-level guard where "locked" sometimes means "show a
different route entirely" rather than "prompt inline."
- components
- knock-codes
- core
- react
"use client";
import type { ReactNode } from "react";
import { useKnockCodes } from "./useKnockCodes.ts";
import { KnockCodes, type KnockCodesProps } from "./KnockCodes.tsx";
export interface ProtectedRouteProps extends KnockCodesProps {
/**
* Rendered instead of the default PIN prompt while locked — e.g. a
* redirect notice paired with your router's own navigation. When omitted,
* behaves exactly like `<KnockCodes>`.
*/
unauthorizedFallback?: ReactNode;
}
function FallbackGate({ unauthorizedFallback, children, labels: _labels, variant: _variant, className: _className, ...config }: ProtectedRouteProps) {
const { state } = useKnockCodes(config);
return <>{state === "unlocked" ? children : unauthorizedFallback}</>;
}
/**
* `<KnockCodes>` for route-level guarding — a React Router `element`, a
* layout-level redirect guard — with one addition: an optional
* `unauthorizedFallback` for routes that should show something other than
* an inline PIN prompt while locked (e.g. a "redirecting…" notice).
*/
export function ProtectedRoute({ unauthorizedFallback, ...props }: ProtectedRouteProps) {
if (unauthorizedFallback === undefined) return <KnockCodes {...props} />;
return <FallbackGate {...props} unauthorizedFallback={unauthorizedFallback} />;
}
Add this block to your project
CLI
npx shadcn@latest add http://localhost:3000/r/react/protected-route.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
- cx (classname helper)
- Gate Wrapper
- PIN Input
- Knock Codes
Files created (11)
- 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/cx.ts
- components/knock-codes/react/GateWrapper.tsx
- components/knock-codes/react/PinInput.tsx
- components/knock-codes/react/KnockCodes.tsx
- components/knock-codes/react/ProtectedRoute.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 |
|---|---|---|---|
| expectedHash | string | — | SHA-256 hex hash to verify against, for local mode. |
| verify | VerifyFn | — | Custom async verification function, for server mode. |
| children * | ReactNode | — | Rendered once unlocked. |
| unauthorizedFallback | ReactNode | — | Rendered instead of the default access-code prompt while locked. Omit for Knock Codes's default behavior. |
| labels | KnockCodesLabels | — | Overrides for every user-facing string. |
| variant | "page" | "inline" | "page" | Outer positioning. |
Accessibility
Identical to Knock Codes when unauthorizedFallback is unset. When it is set, make sure whatever you pass as the fallback is itself accessible — this block doesn't manage focus for you across a route transition.
Customization
Pass `unauthorizedFallback` to render something other than an inline access-code prompt while locked — pair it with your router's own navigation for a redirect-on-lock pattern, or pass an `<AccessDeniedScreen>` for a hard-denial route.
→ 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.