Newsletter — Split with Preview
blockmarketingSignup section pairing the pitch and form with a preview of the last issue — subject line, date, and opening paragraph — so the reader sees what they are subscribing to.
Installation
$pnpm dlx uipkge-ng@latest add newsletter-split-preview$npx uipkge-ng@latest add newsletter-split-preview$yarn dlx uipkge-ng@latest add newsletter-split-preview$bunx uipkge-ng@latest add newsletter-split-previewManifest:
https://uipkge.dev/r/angular/newsletter-split-preview.jsonInstalls to:src/app/components/blocks/newsletter-split-preview/Variants
Loading interactive previews…
Loading interactive previews…
Files installed (2)
src/app/components/blocks/newsletter-split-preview/newsletter-split-preview.component.ts4.7 kB
// Signup section pairing the pitch and form with a preview of the last issue — // subject line, date, and opening paragraph. Port of the Vue/React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { Check, LucideAngularModule, Mail } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' import { UiInputComponent } from '@/ui/input/input.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' export const lastIssue = { subject: 'Why we version metric definitions instead of dashboards', date: 'Feb 18, 2026', readTime: '9 min', opening: 'Dashboards are the output. The definition is the thing teams argue about, so that is what belongs under review — with a diff, an owner, and a revert path. This issue walks through what changed when we moved ours into the repo.', } @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-newsletter-split-preview, [ui-newsletter-split-preview]', standalone: true, // The block renders its own root <section>: keep the host out of layout. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiInputComponent, UiSeparatorComponent, ], template: ` <section data-slot="newsletter-split-preview" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28"> <div class="grid items-center gap-10 lg:grid-cols-2 lg:gap-16"> <div> <span ui-badge variant="secondary" class="gap-1.5"> <lucide-icon [img]="Mail" class="size-3" aria-hidden="true" /> Monthly </span> <h2 class="mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl"> Notes from the people who build it </h2> <p class="text-muted-foreground mt-3 text-lg"> One email a month on metric modelling, access control, and warehouse cost. No product announcements. </p> <form class="mt-6 max-w-md" (submit)="subscribe($event)"> <div class="flex gap-2"> <ui-input [value]="email()" (valueChange)="email.set($event)" type="email" required placeholder="[email protected]" autoComplete="email" aria-label="Email address" /> <button ui-button type="submit" class="shrink-0">Subscribe</button> </div> <p class="mt-2 min-h-5 text-xs" aria-live="polite"> @if (submitted()) { <span class="text-success inline-flex items-center gap-1.5"> <lucide-icon [img]="Check" class="size-3" aria-hidden="true" /> Check your inbox to confirm. </span> } @else { <span class="text-muted-foreground">8,400 subscribers · unsubscribe in one click.</span> } </p> </form> </div> <!-- Showing the last issue is the point: nobody should have to subscribe to find out what they are subscribing to. --> <div ui-card> <div ui-card-content class="p-6"> <div class="flex flex-wrap items-center gap-3"> <span ui-badge variant="outline">Last issue</span> <span class="text-muted-foreground text-xs">{{ issue.date }} · {{ issue.readTime }}</span> </div> <h3 class="mt-4 text-lg leading-snug font-semibold tracking-tight text-balance"> {{ issue.subject }} </h3> <ui-separator class="my-4" /> <p class="text-muted-foreground text-sm leading-relaxed">{{ issue.opening }}</p> <button ui-button variant="link" class="mt-4 h-auto p-0 text-sm">Read the full issue</button> </div> </div> </div> </div> </section> `, }) export class UiNewsletterSplitPreviewComponent { protected readonly Check = Check protected readonly Mail = Mail protected readonly issue = lastIssue @Input('class') className?: string readonly email = signal('') readonly submitted = signal(false) get rootClass(): string { return cn('bg-background', this.className) } subscribe(event?: Event): void { event?.preventDefault() if (!this.email().trim()) return this.submitted.set(true) } }src/app/components/blocks/newsletter-split-preview/index.ts0.1 kB
export { UiNewsletterSplitPreviewComponent, lastIssue } from './newsletter-split-preview.component'
Raw manifest:https://uipkge.dev/r/angular/newsletter-split-preview.json