→ Feedback
Access Denied Screen
A static "you don't have access" view — no access-code form in it at all.
Best used for A hard-denial state after too many failed attempts, or a custom unauthorizedFallback on Protected Route.
Distinct from the access-code entry UI <KnockCodes> renders while locked — this screen means "no more attempts,"
not "try again."
- components
- knock-codes
- react
import type { ReactNode } from "react";
import { GateWrapper, type GateWrapperVariant } from "./GateWrapper.tsx";
import { cx } from "./cx.ts";
export interface AccessDeniedScreenProps {
heading?: string;
message?: ReactNode;
action?: ReactNode;
variant?: GateWrapperVariant;
className?: string;
}
/**
* A static "you don't have access" view — distinct from the PIN entry UI
* `<KnockCodes>` renders while locked, because it never accepts another
* attempt. Use it as a custom `unauthorizedFallback` (e.g. on
* `<ProtectedRoute>`) or anywhere a route/component needs a denial state
* with no verification form in it at all.
*/
export function AccessDeniedScreen({
heading = "Access denied",
message = "You don't have permission to view this page.",
action,
variant = "page",
className,
}: AccessDeniedScreenProps) {
return (
<GateWrapper variant={variant} className={className}>
<div className={cx("w-full max-w-sm space-y-3 text-center")}>
<h1 className="text-lg font-semibold text-gray-900 dark:text-gray-50">{heading}</h1>
<p className="text-sm text-gray-600 dark:text-gray-400">{message}</p>
{action}
</div>
</GateWrapper>
);
}
Add this block to your project
CLI
npx shadcn@latest add http://localhost:3000/r/react/access-denied-screen.jsonRunning this site somewhere other than localhost? Set NEXT_PUBLIC_SITE_URL and this command switches to that host automatically.
Also installs
- cx (classname helper)
- Gate Wrapper
Files created (3)
- components/knock-codes/react/cx.ts
- components/knock-codes/react/GateWrapper.tsx
- components/knock-codes/react/AccessDeniedScreen.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 |
|---|---|---|---|
| heading | string | "Access denied" | The main heading. |
| message | ReactNode | "You don’t have permission to view this page." | Body copy under the heading. |
| action | ReactNode | — | Optional call to action under the message. |
| variant | "page" | "inline" | "page" | Outer positioning. |
Accessibility
A plain heading + message + optional action — no live region needed since nothing here changes without a full re-render triggered by the parent.
Customization
Not wired to any verification strategy on purpose. Use it as a custom `unauthorizedFallback` on `<ProtectedRoute>`, or anywhere you need a denial state that never accepts another attempt — pass `action` for a "contact us" link or a retry button that resets your own flow.
→ 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.