Arc
ComponentsBlocksDocsPricingTry the demo
Get Pro
Introduction
Installation
Theming
Motion
AI and MCP
Changelog
Metrics dashboardPro
KPI drilldownPro
Metric explorerPro
Revenue explorerPro
MRR waterfallPro
Cohort retentionPro
Usage forecastPro
Journey flowPro
Uptime statusNewPro
Choropleth explorerPro
Connected scatterPro
Horizon chartPro
Account healthNewPro
Market terminalPro
Budget variancePro
Small multiplesPro
Line replayPro
AI chatPro
AI composerPro
AI answerPro
Model pickerPro
Model arenaPro
Prompt studioPro
Document extractionPro
Agent memoryPro
AI side panelPro
Research runPro
AI insight cardNewPro
Catch upNewPro
AI feature launchNewPro
AI search barNewPro
AI setup wizardNewPro
Call insightsNewPro
Lead scoringNewPro
Agent runPro
Agent tasksPro
Agent approvalsPro
Agent routinesPro
Agent orchestratorPro
MCP serversPro
Knowledge basePro
Eval suitePro
Agent builderNewPro
Agent galleryNewPro
SidebarPro4
Page header
Command palette
Notification center
Selection toolbarPro
Empty states
Search resultsPro
Login and sign up5, free and Pro variants
Onboarding illustrationPro
OTP input
Multi-step formPro
Two-factor setupNewPro
Magic linkNewPro
Settings pagePro
Settings commandPro
Security settingsPro
Billing overviewPro
Usage billingPro
Cancel flowPro
Onboarding surveyNewPro
Spend alertNewPro
Team membersPro
Invite peoplePro
Team directoryPro
Roles and permissionsPro
Profile pageNewPro
API keysPro
WebhooksPro
IntegrationsPro
Environment variablesPro
Domain verifyNewPro
Webhook testerNewPro
Plan comparison
Pricing calculatorPro
Usage pricingPro
Cart drawerPro
Checkout summaryPro
Live checkoutPro
Seat managerNewPro
Credit top-upNewPro
Invoice studioPro
Wallet cardPro
Delivery trackerPro
Product listingPro
Product detailPro
Activity feedNewPro
Inbox triagePro
Support conversationPro
Support widgetPro
Help centerPro
Feature votingNewPro
File upload
Media playerPro
Week calendarPro
Availability pickerPro
Project boardPro
Release readinessPro
Kanban boardPro
Smart schedulerNewPro
Task breakdownNewPro
Goal trackerNewPro
Hero section
Hero signupPro
Scroll storyPro
Spotlight gridPro
Feature bentoPro
Comparison table
Logo marquee
Studio perspectivesPro
Stats band
Changelog feed
FAQ section
Contact section
Team showcasePro
Blog grid
Site header
Site footer
CTA section
Newsletter signup
Activity terrainPro
Revenue globePro
Customer galaxyPro
Semantic zoomPro
Layout morphPro
Exploded viewPro
What’s new74 new
BlocksMagic link

Magic link

Pro

A passwordless sign in card where an email with inline validation and typo fixes sends a magic link, and the card crossfades in place to a check your inbox step with a sealing envelope, Gmail and Outlook shortcuts and a resend countdown. Google and GitHub sign in sit above, with their real marks.

Live block · sample data

When to use

  • Passwordless sign in or sign up by email link, with optional Google and GitHub sign in.
  • B2B products that want a work email gate with a clear what happens next screen.
  • Replacing a password form to cut sign in friction and reset requests.

When not to use

  • Password plus email sign in or MFA codes; use sign-in or a login block instead.
  • One time codes typed into the app; use an OTP input flow.
  • Full page marketing sign in layouts with imagery; use login-split.

Installation

Pro source and install commands unlock with a Pro plan.

Usage

Drop `<MagicLink />` on a sign in page or in a dialog. Connect `onSendLink(email, signal)` to your auth provider (Clerk, Supabase, Auth.js, Better Auth, WorkOS). It covers the first send and every resend. Throw from it to show the error state; when it resolves, the card moves to the inbox step. Connect `onProviderSignIn(provider, signal)` to the provider's social redirect. Set `appName` and `sender`, and use `validateEmail` to require a work domain. `providers={[]}` and `mailApps={[]}` hide the social buttons and the inbox shortcuts. `resendAfter` and `expiresInMinutes` set the countdown and the expiry copy. By default the inbox shortcuts open a Gmail search for the sender, or outlook.live.com / outlook.office.com depending on the address. Pass `onOpenMail` to change that.

example.tsx
1import { MagicLink } from "@/components/arc/blocks/magic-link/magic-link";2import { authClient } from "@/lib/auth-client";3 4export function SignInPage() {5  return (6    <MagicLink7      appName="Halden"8      sender="login@halden.app"9      onSendLink={async (email, signal) => {10        const res = await fetch("/api/auth/magic-link", {11          method: "POST",12          body: JSON.stringify({ email, redirectTo: "/dashboard" }),13          signal,14        });15        if (!res.ok) throw new Error("Send failed");16      }}17      onProviderSignIn={provider => authClient.signIn.social({ provider, callbackURL: "/dashboard" })}18      validateEmail={email => /@(gmail|yahoo)\./i.test(email) ? "Use your work email" : null}19      termsHref="/terms"20      privacyHref="/privacy"21    />22  );23}
Built fromButton

API reference

MagicLink

A passwordless sign in card. Google and GitHub buttons sit above an email field with inline validation and a typo check ("Did you mean maya@gmail.com?"). Send magic link shows a spinner, then the card crossfades in place to Check your inbox: an envelope seals the letter, the address and expiry are shown, Open Gmail and Open Outlook jump to the inbox, a resend countdown ticks in tabular numerals and Use a different email goes back with the field focused. Both steps share one grid cell and one footer hairline, so the card never changes size. With no props every send is simulated.

PropTypeDefaultDescription
appNamestring"Halden"Your product's name, used in the title ("Sign in to Halden") and the card's accessible name.
senderstring"login@halden.app"The address the link comes from. Shown on the inbox step and used by Open Gmail to search for the email.
defaultEmailstring""Prefills the email field, for example from a previous session or an invite.
emailLabelstring"Work email"The label above the email field.
providers("google" | "github")[]["google", "github"]Social sign in buttons, in order, with their real marks. One provider spans the row. Pass [] to hide the buttons and the divider.
mailApps("gmail" | "outlook")[]["gmail", "outlook"]Inbox shortcuts on the Check your inbox step. Pass [] to hide them.
resendAfternumber30Seconds before the link can be sent again. The countdown restarts after every resend.
expiresInMinutesnumber15How long the link stays valid. Display only; enforce it on the server.
onSendLink(email: string, signal: AbortSignal) => Promise<void> | void–Sends the magic link, for the first send and every resend. Throw to show "We couldn't send the link" (or Retry resend). The signal aborts when the person goes back or the card unmounts. Leave it out to simulate a send; nothing is sent and a status message says so.
onProviderSignIn(provider: "google" | "github", signal: AbortSignal) => Promise<void> | void–Starts a social sign in, usually a redirect to your auth provider. The button shows a spinner until it settles; throw to show an inline error. Leave it out to simulate one.
onOpenMail(app: "gmail" | "outlook", email: string) => void–Handles the inbox shortcuts. By default Gmail opens a search for the sender in a new tab, and Outlook opens outlook.live.com for personal addresses or outlook.office.com for work ones.
validateEmail(email: string) => string | null | undefined–Extra validation after the format check, such as allowing only company domains. Return the error message to show, or nothing to accept.
termsHrefstring"#"Link to your terms in the footer.
privacyHrefstring"#"Link to your privacy policy in the footer.
classNamestring–Class on the root wrapper, which spans its container and centers the card.

Keyboard interactions

Tab
Moves through Google, GitHub, the email field, Send magic link and the footer links; on the inbox step through the inbox shortcuts, Use a different email and Resend.
Enter
Submits the email field. On a typo hint, pressing Enter again sends to the address as typed.
SpaceorEnter
Activates the focused button, including the suggested address in a typo hint.

Accessibility

  • The card is a labelled region ("Sign in to Halden"); the hidden step is inert, so it leaves the tab order and the accessibility tree.
  • Focus follows the step: the Check your inbox heading takes focus when the link is sent, and Use a different email returns focus to the field with the address selected.
  • The field has a real label, autocomplete="email" and an email keyboard on touch. Errors set aria-invalid, are linked with aria-describedby and announced as alerts, and carry an icon as well as color.
  • A polite live region announces sends, resends and simulated actions ("Simulated, nothing was sent").
  • Resend stays focusable during the countdown (aria-disabled), so focus never drops when a resend starts.

Motion

  • Steps crossfade in one grid cell: the old step fades out in 100ms, the new one fades in over 200ms with a 4px rise. Nothing resizes.
  • The envelope plays once per send in about 0.8s: the letter drops in, the flap folds shut, a success check settles. It replays on every resend. No bounce, no loop.
  • Messages under the field and resend labels swap with a single opacity crossfade inside reserved space; the countdown digits change in place in tabular numerals.
  • Reduced motion drops the rise and the envelope travel and renders its sealed end state, keeping the crossfades short.

Responsive behavior

  • Fills its container up to 400px. Under 480px the padding and corner radius step down.
  • Provider and inbox buttons stay in two equal columns down to 320px; long addresses truncate with an ellipsis and keep the full value in a tooltip.
  • Touch targets are 44px buttons and 32px footer links; the email field opens the email keyboard.

Performance

  • Both steps stay mounted (the hidden one inert), so switching never measures or reflows the card.
  • The countdown ticks every 250ms only while it runs and stops at zero. The envelope is one small inline SVG.
  • Brand marks are inline SVG; no images or network requests.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use for passwordless email sign in or sign up. Wire onSendLink to your auth provider (Clerk, Supabase, Auth.js, Better Auth, WorkOS) and onProviderSignIn to its social redirect.
  • Throw from onSendLink to show the error state; resolve to advance to the inbox step. Pass the AbortSignal to fetch.
  • Keep it centered on a plain page or in a dialog; it is a self-contained card about 400px wide.
  • Hide social sign in with providers={[]} for B2B email-only flows, and use validateEmail to require a work domain.
/components/blocks/magic-link/markdown

The full library index for assistants is at /llms.txt.

Related

Login and sign up: Email codeBlockA sign in card that morphs from email to a six digit code to your account.Login and sign up: CenteredBlockA passkey-first login card on a quiet ring backdrop that morphs through email and code.Login and sign up: SplitProBlockA full screen split login: an email-first form that morphs into a password step beside a photo.InputComponentA single line field with clear labels and useful states.ButtonComponentA clear, responsive action with quiet secondary states.
PreviousAccount healthNextFeature voting
Arc · 129 block pagesllms.txt

On this page

  • Preview
  • When to use
  • When not to use
  • Installation
  • Usage
  • API reference
  • Keyboard
  • Accessibility
  • Motion
  • Responsive behavior
  • Performance
  • Notes for AI
  • Related
View as Markdown

Sign in to Halden

Welcome back. No password needed.

or

By continuing, you agree to the Terms and Privacy Policy.

Check your inbox

We sent a sign in link to

 

From login@halden.app · expires in 15 minutes