No Access
betaWhat someone sees when they open something they cannot: what it is, whose it is, and Request access with an 8-second Undo.
Preview
default
Budget review, before Saturday 3 October
Sam Carter owns this
Ask for access and they get an email. You hear back when they answer.
asked before
Pay notes, Design team
Priya Shah owns this
Ask for access and they get an email. You hear back when they answer.
no owner, own hint
Board minutes
Only board members can read these. Ask, and the secretary decides.
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/no-access
Use it for
- Put it where the content would be, when someone opens a page, file, or team they cannot see.
- Name what it is in title, so they know they reached the right thing.
- Pass owner, so they know who will get the request. Put their avatar in owner.avatar.
- Send the request in onRequest. It runs once the 8-second undo window ends, or when the part leaves the page.
- Pass asked when they asked before, so the button shows the record instead of asking twice.
Avoid it for
- Hiding that something exists when its name is private too. Show a plain not-found page instead.
- Sending the request on press. onRequest waits for the undo window, so Undo never takes back a sent email.
- Content that is only loading or empty. Use an empty state or a skeleton.
Props
- title
What it is, like "Budget review, before Saturday 3 October".
string
- owner
Whose it is. avatar is an element, like an Avatar with their photo.
{ person: string; avatar?: ReactNode }
- hint
What happens when they ask.
string · default "Ask for access and they get an email. You hear back when they answer."
- asked
Asked before. The button shows "Asked", with the day and time when at is given.
{ at?: string | Date } | null
- onRequest
Sends the request, once the 8-second undo window ends.
() => void
- headingLevel
The title's heading level, so it fits the page's outline.
1 | 2 | 3 | 4 | 5 | 6 · default 2
- className
Extra classes on the root. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| title | string | - | What it is, like "Budget review, before Saturday 3 October". |
| owner | { person: string; avatar?: ReactNode } | - | Whose it is. avatar is an element, like an Avatar with their photo. |
| hint | string | "Ask for access and they get an email. You hear back when they answer." | What happens when they ask. |
| asked | { at?: string | Date } | null | - | Asked before. The button shows "Asked", with the day and time when at is given. |
| onRequest | () => void | - | Sends the request, once the 8-second undo window ends. |
| headingLevel | 1 | 2 | 3 | 4 | 5 | 6 | 2 | The title's heading level, so it fits the page's outline. |
| className | string | - | Extra classes on the root. Later classes win. |
Accessibility
- The title is a real heading, at the level you give.
- Request access is a native button. After a press it reads "Asked. Press to undo." and focus stays on it.
- Undo arms when the pointer leaves, or after 1.2 seconds for the keys and touch, so a double press never undoes.
- A polite status line says the request was made, and says when it was undone.
- Once sent, the button is a record: it stays in the tab order with aria-disabled and full contrast.
- The lock spot is hidden from screen readers, since the title says what it is.
Examples
A page they cannot see
"use client";
export function LockedPage() {
return (
<NoAccess
title="Budget review, before Saturday 3 October"
owner={{ person: "Sam Carter" }}
onRequest={() => fetch("/api/access-requests", { method: "POST" })}
/>
);
}Asked before
<NoAccess
title="Pay notes, Design team"
owner={{ person: "Priya Shah" }}
asked={{ at: "2026-10-02T09:20:00" }}
/>