Back to Widget Patterns
v2.2Lead Capture
Newsletter Signup Inline
Email signup with optional headline and supporting copy. Three style variants: default, minimal, and card. Shares form-state patterns with the inline single field form.
Live preview
Rendered with real component code. Each example demonstrates a documented variant.
Default
Unsubscribe anytime. No spam.
With headline
Get our weekly digest
One piece on product, growth, and distribution. Every Thursday.
Unsubscribe anytime. No spam.
Card style
Get our weekly digest
One piece on product, growth, and distribution. Every Thursday.
Unsubscribe anytime. No spam.
React props
Configure the component via props in React or via attributes in HTML.
| Prop | Type | Default | Description |
|---|---|---|---|
| onSubmit | (email: string) => void | Promise<void> | required | Called with the trimmed email on submit; throw to trigger error state |
| headline | string | undefined | Optional heading displayed above the form row |
| description | string | undefined | Optional supporting copy displayed below the headline |
| placeholder | string | "you@company.com" | Input placeholder text |
| buttonLabel | string | "Subscribe" | Submit button label |
| disclaimerText | string | "Unsubscribe anytime. No spam." | Fine print shown below the form |
| successText | string | "Thanks for subscribing." | Message shown after a successful submit |
| style | "default" | "minimal" | "card" | "default" | Visual treatment of the container |
| className | string | undefined | Extra class applied to the root element |
HTML usage
The HTML variant uses the same shared styles and class names. Drop into any stack.
<link rel="stylesheet" href="path/to/newsletter-signup-inline/styles.css">
<!-- Default style -->
<section class="nsi nsi--default" id="my-newsletter">
<h2 class="nsi__headline">Get the weekly essay</h2>
<p class="nsi__description">One piece on B2B pricing, every Thursday.</p>
<form class="nsi__form" novalidate>
<label class="nsi__label nsi__label--visually-hidden" for="nsi-email">
Email address
</label>
<input
id="nsi-email"
class="nsi__input"
type="email"
placeholder="you@company.com"
autocomplete="email"
required
>
<button class="nsi__button" type="submit">Subscribe</button>
</form>
<p class="nsi__disclaimer">Unsubscribe anytime. No spam.</p>
<p class="nsi__success" hidden aria-live="polite">Thanks for subscribing.</p>
<p class="nsi__error" hidden role="alert"></p>
</section>
<script>
(function () {
var section = document.getElementById('my-newsletter');
var form = section.querySelector('.nsi__form');
var input = section.querySelector('.nsi__input');
var button = section.querySelector('.nsi__button');
var success = section.querySelector('.nsi__success');
var error = section.querySelector('.nsi__error');
form.addEventListener('submit', function (e) {
e.preventDefault();
var email = input.value.trim();
if (!email) return;
input.disabled = true;
button.disabled = true;
error.hidden = true;
// Place your fetch() call here.
fetch('/api/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email }),
})
.then(function (res) {
if (!res.ok) throw new Error('Request failed');
success.hidden = false;
form.reset();
})
.catch(function () {
error.textContent = 'Something went wrong. Check your entry and try again.';
error.hidden = false;
input.disabled = false;
button.disabled = false;
});
});
}());
</script>Customization
Override these CSS custom properties to integrate the component into your brand.
.nsi {
--nsi-bg: transparent;
--nsi-padding: 0;
--nsi-border: none;
--nsi-border-radius: 0.5rem;
--nsi-gap: 0.75rem;
--nsi-headline-size: 1.25rem;
--nsi-headline-weight: 700;
--nsi-headline-color: inherit;
--nsi-description-size: 0.9375rem;
--nsi-description-color: var(--brand-muted, #6b7280);
--nsi-input-border: var(--brand-border, #d1d5db);
--nsi-input-border-radius: 0.375rem;
--nsi-input-bg: #ffffff;
--nsi-input-padding-y: 0.625rem;
--nsi-input-padding-x: 0.875rem;
--nsi-input-font-size: 1rem;
--nsi-focus-ring: var(--brand-accent, #1e5fcf);
--nsi-button-bg: var(--brand-accent, #1e5fcf);
--nsi-button-bg-hover: color-mix(in srgb, var(--nsi-button-bg) 88%, black);
--nsi-button-text: #ffffff;
--nsi-button-font-weight: 600;
--nsi-disclaimer-size: 0.8125rem;
--nsi-disclaimer-color: var(--brand-muted, #6b7280);
--nsi-success-color: var(--brand-success, #16a34a);
--nsi-error-color: var(--brand-error, #dc2626);
}