Expired Link Recovery

Link Expired
410

That link has already done its job.

Sign-in links live for fifteen minutes and can only be opened once. Ask for a fresh one and it will be in your inbox before you finish reading this.

Expired23 Jul 2026, 13:42 UTC

Why this happens

  • The link was opened once already, on this or another device
  • More than fifteen minutes passed between request and click
  • A newer link was requested, which retires every older one
About this block

Expired Link RecoveryPRO

Expired magic-link page with a square eyebrow and status code, a bold accent headline, the expiry timestamp in a bordered strip, a seal button that swaps to a sent confirmation, and a square-bullet list explaining the expiry.

The page a stale magic link lands on: the expiry timestamp sits in a bordered strip, a seal button sends a fresh link and swaps itself for a confirmation, and a square-bullet list explains why single-use links expire.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/error18?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/error18?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/error18.tsx, plus the two design-system primitives it renders through: badge6 for the square eyebrow and button1 for the resend seal button.

Quick start

The installed file exports error18Demo alongside the block: the exact props behind the preview above. Spread it to get a working expiry page in one line.

tsx
import { Error18, error18Demo } from "@/components/beste/block/error18";

export default function LinkExpired() {
  return <Error18 {...error18Demo} />;
}

Sending the new link is the caller's job, so pass a handler:

tsx
"use client";

import { Error18 } from "@/components/beste/block/error18";

export default function LinkExpired({ email }: { email: string }) {
  return (
    <Error18
      label="Link Expired"
      code="410"
      heading="That link has <strong>already done its job</strong>."
      description="Sign-in links live for fifteen minutes and can only be opened once."
      labels={{
        resend: "Send a new link",
        sent: "A new link is on its way. It expires in fifteen minutes.",
        reasonsTitle: "Why this happens",
        expiredTitle: "Expired",
      }}
      expiredAt="23 Jul 2026, 13:42 UTC"
      reasons={[
        "The link was opened once already, on this or another device",
        "More than fifteen minutes passed between request and click",
      ]}
      link={{ label: "Sign in with a password instead", href: "/sign-in" }}
      onResend={() => requestMagicLink(email)}
    />
  );
}

Props

PropTypeDefaultDescription
labelstringEyebrow text rendered through the square-marker badge
codestringStatus code shown after the eyebrow divider
headingstringHeadline HTML, rendered as an h1; <strong> marks the accent clause
descriptionstringExplanatory paragraph under the headline
labelsError18Labels{}Resend button text, sent confirmation, and the two section captions
expiredAtstringExpiry timestamp shown in the bordered strip
reasonsstring[][]Square-bullet list explaining the expiry
linkActionLinkText link at the bottom of the block
onResend() => voidCalled when the resend button is pressed
classNamestringExtra classes for the outer <section>
ts
type Error18Labels = {
  resend?: string;
  sent?: string;
  reasonsTitle?: string;
  expiredTitle?: string;
};

type ActionLink = {
  label: string;
  href: string;
};

Behavior notes

  • Pressing resend flips local state and calls onResend. The button is replaced by the confirmation strip immediately, without waiting on the handler, so a slow request never leaves the button looking dead.
  • The switch is one way for that mount: there is no second attempt from the UI, which stops a visitor from queuing five links in a row.
  • Without onResend the button still swaps to the confirmation, so the block can be dropped in before the mail flow exists. Wire the handler before shipping.
  • The resend button only renders when labels.resend is set, and the expiry strip only when expiredAt is set, so a bare heading plus reasons is a valid configuration.
  • heading is injected with dangerouslySetInnerHTML and accepts inline HTML; <strong> takes the accent colour. Keep the value under your own control, never user input.

More Error blocks

View all Error
PRO

error27

Session Expired Sign In

Timed-out session page: an accent headline over an account strip showing the avatar, address and sign-out timestamp, an autosave reassurance line, and a sign-in seal button beside an account switch link.

PRO

error24

404 With Featured Work

Studio 404 that recovers a dead project URL: a square eyebrow and status code over a hairline rule, an accent headline and seal button, then two large project cards with category lines and hover arrow chips.

PRO

error33

Retired Page Record

Monochrome page for deliberately removed content: a display headline, a ruled record of when it was published, retired and why, then a pill link to the replacement beside an archive link.

PRO

error15

500 Incident Report

Server error page with a square eyebrow and status code over a hairline rule, a bold accent headline, a bordered incident table of reference, timestamp and service rows, and a retry seal button beside a status page link.

PRO

error25

Rate Limit Cooldown

Too-many-requests page with a square eyebrow and status code, an accent headline, three bordered usage cells, and a live cooldown counter that swaps itself for a retry seal button when it reaches zero.

PRO

error17

Maintenance Window Notice

Planned downtime page with a square eyebrow and status code, a bold accent headline, three bordered cells for the maintenance window, a working notify-me email form with a success state, and a live progress link.