Newsletter signup
An email signup framed by a stack of past issues; subscribing drops the next issue, addressed to you, onto the front.
pnpm dlx shadcn@latest add @uiarc/newsletter-signup- Collecting emails for a newsletter, digest, or changelog that has real issues to show.
- The end of a blog post or docs page.
- Account creation with a password. Use Signup form.
- Longer forms with several fields. Use Contact section.
- A launch waitlist at the top of a page. Use Hero signup.
Installation
Add Newsletter signup with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/newsletter-signupAdds the block and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
Usage
Use this to collect newsletter or waitlist emails. Pass onSubscribe to call your email provider and throw on failure; subscribing in the preview is simulated.
import { NewsletterSignup } from "@/registry/blocks/newsletter-signup/newsletter-signup"; export function Newsletter() { return ( <NewsletterSignup variant="card" onSubscribe={async email => { const response = await fetch("/api/subscribe", { method: "POST", body: JSON.stringify({ email }) }); if (!response.ok) throw new Error("Subscribe failed"); }} /> );}NewsletterSignup
A newsletter signup framed by the newsletter itself: a stack of recent issues. Subscribing drops the next issue, addressed to the new reader, onto the front of the stack. Inline section or card.
variant"inline" | "card""inline"inline puts the copy and form beside the issue stack; card is a self-contained card with the stack in a tray on top.titlestring–Heading. Each variant has sample copy.descriptionstring–One sentence on what people get and how often.placeholderstring"you@company.com"Email field placeholder.buttonLabelstring"Subscribe"Submit button label.privacyNoteReactNode"No tracking pixels. Unsubscribe with one click."Line under the form about frequency and privacy.privacyLink{ label: string; href: string } | null–Link after the note. Pass null to hide it.publicationNewsletterPublication | null–The issue stack: { name, upcoming, recent }. Each issue has a number, a date, a subject, and up to three stories with a thumbnail and read time. Pass null for the form alone.readers{ count: number; faces: string[] } | null–Reader count and up to three faces, under the stack or at the foot of the card. The count ticks up by one on success. Pass null to hide.onSubscribe(email: string) => void | Promise<void>–Called with a valid, trimmed email. Resolve to show success; reject to show an error and keep the email.classNamestring–Extra class on the section.- Enter
- In the email field, submits the form. The phone keyboard shows a send key.
- Tab
- Moves from the field to the button, the privacy link, and after success to "Use a different email".
- The email field has a visually hidden label, type email, inputmode email, autocomplete email, and aria-invalid when the address is wrong.
- The line under the field is its description: privacy note, error, or confirmation. Errors use role alert; the line is a polite live region and names the address the link went to.
- Validation starts when the field loses focus with text in it, then updates as you type. On an invalid submit, focus stays in the field.
- While sending, the button sets aria-busy and aria-disabled and ignores repeat submits. On success, focus moves to "Use a different email", which restores the form and refocuses the field.
- Only the front issue is exposed to assistive technology; the issues behind it and the thumbnails are hidden as decoration.
- On success the next issue, addressed to you, rises onto the front of the stack while the others step back and the oldest drops away. It is one no-overshoot spring, and opacity resolves fast so two issues never read through each other.
- The reader count rolls up by one, and "including you" fades in after it.
- An invalid or failed submit shakes the field a few pixels and tints its border; the message swaps in place of the note.
- Every message, button label, and issue has its space reserved, so nothing below the form moves between states.
- Reduced motion places the issues at once with a short fade, removes the shake and the roll, and keeps the check and labels as quick fades.
- Inline puts the stack under the copy and form below 860px container width.
- Below 400px read times hide and an addressed issue shows the recipient instead of the date.
- When the form is narrower than 300px the field and button separate and stack, both full width.
- The card is at most 460px wide; its tray crops the stack and fades it out at the edge.
- Issue thumbnails and faces use next/image at their rendered size.
- Motion is transform and opacity only; no layout animation.
- No network code: bring your own onSubscribe.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Pass onSubscribe and throw on failure; the component handles validation, busy, error, retry, and success states.
- Give it your own publication so the stack shows real past issues. Use short subjects and story titles; titles clamp at two lines.
- Keep the privacy note honest and specific.
- Use inline between page sections; use card in a sidebar, at the end of a post, or centered in a footer band.
- Edit newsletter-signup-data.ts for copy, issues, and reader faces.
The full library index for assistants is at /llms.txt.














