Share Link

Reads every share-link setting back as one sentence and warns about public edit access, links that never expire and widening an already-copied link.

StopsAnyone with the link could edit

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/share-link.json

The files land in your project. They're yours to edit.

Usage

share-link-usage.tsx
import { ShareLink, type ShareSettings } from "@/components/ui/share-link"

<ShareLink
  key={url}
  label="Share “Q4 budget.xlsx”"
  url={url}
  organization="Acme"
  value={settings}
  onValueChange={setSettings}
  onRevoke={() => new Promise<void>((resolve) => setTimeout(resolve, 600))}
  onCreate={() => {
    setUrl("https://files.acme.app/s/q4-budget-Rm9x")
    setSettings({ access: "organization", role: "view", expiresAt: null })
  }}
/>

What it catches

  • Anyone-with-the-link edit access, with a one-tap view-only fix
  • Public links with no expiry, with a suggested expiry
  • Widening access after the link was already copied
  • Time left before the link expires
  • Revoking, confirmed first with Cancel focused

Props

ShareLink props
PropTypeDefault
url* (required)

The link being shared.

string—
value

Controlled settings.

ShareSettings—
defaultValue

Initial settings when uncontrolled.

ShareSettings{ access: "invited", role: "view", expiresAt: null }
onValueChange

Called with the settings after every change.

(settings: ShareSettings) => void—
onRevoke* (required)

Kills the link. Return a promise to show a pending state.

() => void | Promise<void>—
onCreate

Shown after revoking, to make a fresh link.

() => void—
organization

Organisation name for the "people in …" option.

string"your organisation"
expiryDays

Expiry presets in days.

number[][1, 7, 30]
suggestedExpiryDays

Days the one-tap "add an expiry" fix sets on a public link.

number7
locale

Locale for dates and countdowns.

string—
timeZone

IANA zone for the expiry readback.

string—
label

Visible label of the link field.

string"Share link"
whoText

Who the link reaches, used in the readback. `{organization}` is filled in.

Partial<Record<ShareAccess, string>>—
accessLabels

Options of the "who" menu. `{organization}` is filled in.

Partial<Record<ShareAccess, string>>—
roleLabels

Options of the "what they can do" menu.

Partial<Record<ShareRole, string>>—
verbText

The `{verb}` in the readback for each role.

Partial<Record<ShareRole, string>>—
settingsLabel

Accessible names of the settings group and its three menus.

string"Link settings"
accessLabel

Accessible label of the who-can-access select.

string"Who can open it"
roleLabel

Accessible label of the role select.

string"What they can do"
expiryLabel

Accessible label of the expiry select.

string"Expires"
expiryOptionText

Expiry preset options. `{days}` is filled in; `expiryOptionOneText` is used for 1 day.

string"{days} days from now"
expiryOptionOneText

Expiry option text for one day.

string"1 day from now"
formatRemaining

Formats the time left in `expiresInText`. Defaults to `formatCountdown` in `locale`.

(ms: number) => string—
readbackText

Templates: `{who}`, `{verb}` and `{until}` are filled in.

string"{who} can {verb} {until}."
untilText

Readback suffix with the expiry time.

string"until {date}"
noExpiryText

Readback suffix for a link that never expires.

string"with no expiry"
noExpiryOptionText

The "never expires" option of the expiry menu.

string"No expiry"
expiresInText

Countdown text.

string"Expires in {time}"
expiredText

Shown once the link has expired.

string"This link has expired."
publicEditText

Warning for public edit access.

string"Anyone who gets this link, including people it's forward…
viewOnlyLabel

Label of the switch-to-view-only fix.

string"Make it view-only"
noExpiryHint

Warning for a public link with no expiry.

string"Public links get forwarded. An expiry limits the damage."
addExpiryLabel

Label of the add-expiry fix.

string"Expire in {days} days"
widenedText

Warning after widening an already-copied link.

string"You already copied this link. Everyone you sent it to ge…
copyLabel

Label of the copy button.

string"Copy"
copiedLabel

Announced after copying.

string"Copied"
revokeLabel

Label of the revoke button.

string"Revoke link"
revokeConfirmText

Revoke confirmation question.

string"People with the link lose access immediately."
cancelLabel

Label of the Cancel button.

string"Cancel"
revokedText

Shown after the link is revoked.

string"Link revoked. Nobody can open it any more."
createLabel

Label of the create-new-link button.

string"Create new link"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Link field has a <label>; settings are a labelled group of labelled selects
  • Readback in a polite live region; copy result via role=status
  • Revoke confirmation focuses Cancel first
  • Countdown highlight respects reduced motion

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Enforce access level and expiry on every request to the link, and make revoke invalidate the token server-side.

  • Settings take effect only when your server applies them
  • Cannot recall copies of the link already shared

What it stops

Anyone with the link could edit

Also known as

share link · anyone with the link · permissions · expiry · revoke · public link · google drive · copy link

Field type

Dependencies

Pairs well with