Newsletter Signup Popup Modal — Free HTML CSS JS Snippet

Newsletter Signup Popup Modal · Modals · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Auto-appears after a configurable delay via setTimeout, not instantly on page load
sessionStorage remembers dismissal so the popup never nags a visitor twice in one visit
Three separate ways to dismiss — close button, outside click, and successful submission — all funnel through one dismissal function
Scale-and-fade pop-in animation driven by a single toggled visible class
Can also be triggered manually and on-demand, independent of the automatic timer
Inline success message replaces the form after subscribing, without navigating away
Accessible dialog markup with role="dialog", aria-modal, and aria-labelledby
Focus-friendly input with a clear focus ring for keyboard users
Backend call left as a clearly marked placeholder, ready for any email provider's API
No framework, no popup/modal library, no build step required

About this UI Snippet

Newsletter Popup Modal — HTML, CSS & JavaScript Signup Snippet

Screenshot of the Newsletter Signup Popup Modal snippet rendered live

A newsletter popup is one of the highest-converting, most-hated UI patterns on the web — high-converting when it appears at a reasonable time and disappears politely, hated when it reappears every single page load. The difference between the two usually comes down to two implementation details: *when* it triggers, and whether it *remembers* being dismissed.

This snippet implements both correctly using plain HTML, CSS, and vanilla JavaScript.

How the delayed auto-show works

A single setTimeout(openNewsletterModal, AUTO_SHOW_DELAY) call fires 3 seconds after the page loads (configurable via the AUTO_SHOW_DELAY constant), rather than showing the modal instantly. This gives a visitor a moment to actually see the page content before being interrupted — a widely recommended practice for popup timing.

How session-based dismissal memory works

Before that timeout even fires, the script checks sessionStorage.getItem('newsletterPopupDismissed'). sessionStorage (unlike localStorage) is automatically cleared when the browser tab is closed, so "remembers for the session" specifically means: don't show it again on this visit, but a fresh visit next week starts clean. Three actions all set that flag: closing the modal explicitly, submitting the form successfully, and clicking outside the modal on the dark overlay — all of them count as "the user has made a decision, stop showing this."

How the open/close animation works

The overlay is a fixed, full-screen rgba scrim that transitions opacity and visibility together, and the modal card itself scales up from 0.92 and slides up slightly (translateY(10px) to 0) as the .visible class is applied — a subtle pop-in rather than an instant snap.

Manually triggering the modal

The demo page includes an "Open newsletter popup now" button calling openNewsletterModal() directly, showing that the same modal can be triggered on demand — from an exit-intent listener, a scroll-depth trigger, or a manual "Subscribe" link in your footer — independent of the automatic delayed trigger.

Closing behavior

Three separate interactions close the modal: the explicit × button, clicking the dark overlay outside the card, and a successful form submission (which shows a success message for 1.8 seconds before auto-closing). All three funnel through the same closeNewsletterModal/dismissNewsletterModal functions so there's a single source of truth for the modal's visibility state.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Give this snippet's HTML, CSS, and JS to an AI coding assistant like Claude and ask it to explain the tradeoffs between sessionStorage and localStorage for popup dismissal memory, and to help you decide which is more appropriate for your specific product's growth goals. It's also a great snippet to extend with the assistant's help — ask it to add an exit-intent trigger using a mouseleave listener on the document that fires only when the cursor moves toward the top of the viewport, to add a focus trap so Tab cycles only within the open modal for full accessibility compliance, or to wire the placeholder subscribe call to a specific email service provider's API (Mailchimp, ConvertKit, or a custom backend) and handle its success/error responses in the existing success-message UI.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build an auto-appearing newsletter signup popup modal in plain HTML, CSS, and JavaScript — no framework, no modal/popup library.

Requirements:
- A modal overlay and card, hidden by default, that automatically opens once via a configurable setTimeout delay after page load — do not show it immediately on load.
- Before showing it, check sessionStorage for a dismissal flag; if the user already dismissed or subscribed earlier in this browser session, do not auto-show the popup again for the rest of the session.
- Three distinct interactions must all count as a dismissal and set that sessionStorage flag: clicking an explicit close button, clicking the dark overlay outside the modal card, and successfully submitting the signup form.
- The modal must also be triggerable manually and on demand via a separate exposed function, independent of the automatic delayed trigger, so it can be wired to other triggers like a footer link or a scroll-based event later.
- On successful form submission, hide the form and show an inline success message in its place (no page navigation), then automatically close the modal after roughly two seconds.
- Include proper dialog accessibility markup — role="dialog", aria-modal="true", and aria-labelledby pointing at the modal's heading — and a smooth scale-and-fade transition driven by a single toggled CSS class, not by adding/removing the modal from the DOM.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the snippetClick "Newsletter Signup Popup Modal" in the sidebar Library tab. Wait about 3 seconds and the modal auto-appears in the preview.
  2. 2
    Dismiss it and reloadClose the modal, then reload the preview — it will not reappear, because sessionStorage remembers the dismissal for this browser tab.
  3. 3
    Trigger it manuallyClick "Open newsletter popup now" on the demo page to show the modal on demand, independent of the automatic timer.
  4. 4
    Submit the formEnter an email and submit — the form swaps to a success message before the modal auto-closes.
  5. 5
    Adjust the delayIn the JS panel, change the AUTO_SHOW_DELAY constant (in milliseconds) to control how long visitors see the page before the popup appears.
  6. 6
    Wire it to your backendReplace the commented-out fetch() placeholder in the submit handler with a real call to your email service provider's subscribe endpoint.

Real-world uses

Common Use Cases

MODAL
Blog and content site email capture
Grow an email list from blog readers without showing the popup on every single page load during one visit.
Learn sessionStorage-based dismissal memory
Study the difference between sessionStorage and localStorage and why session-scoped memory is the right choice for a once-per-visit popup.
Prototype a full email-capture strategy
Combine this with an exit-intent or scroll-depth trigger by calling openNewsletterModal() from your own custom event listener instead of only the timer.
Match your brand and offer
Restyle the modal card, icon, and copy to reflect your actual newsletter's value proposition and subscriber count.
Build accessible dialog patterns
The role="dialog", aria-modal, and aria-labelledby wiring here is a solid reference for any custom modal you build elsewhere on your site.
Connect to a real email service provider
Wire the submit handler's placeholder fetch call to Mailchimp, ConvertKit, or your own backend's subscribe endpoint.

Got questions?

Frequently Asked Questions

sessionStorage is automatically cleared when the browser tab or window closes, so "dismissed for this session" means the popup stays hidden for the rest of the current visit but reappears on a brand new visit later — which is friendlier than localStorage, which would suppress it permanently across every future visit until manually cleared.

Edit the AUTO_SHOW_DELAY constant near the top of the JS panel — it is the number of milliseconds after page load before the popup automatically appears. 3000 means 3 seconds.

Yes. Remove or keep the setTimeout call and add your own listener — for example a scroll listener checking window.scrollY against a percentage of document height, or a mouseleave listener on the document that fires when the cursor moves toward the top of the viewport — and call openNewsletterModal() from within it.

Yes. The overlay has a click listener that checks if the click target is the overlay itself (not the modal card), and if so calls dismissNewsletterModal(), which also sets the sessionStorage flag just like the close button does.

The form is hidden and a success message appears in its place, the sessionStorage dismissal flag is set immediately, and the modal automatically closes after about 1.8 seconds. You should replace the commented placeholder with a real fetch call to your email provider's API.

The modal has role="dialog", aria-modal="true", and aria-labelledby pointing at its heading, and all interactive elements are real buttons and inputs reachable via Tab. For full production accessibility, also add a focus trap that keeps Tab cycling within the modal while it is open, and return focus to the trigrigger element when it closes.

Yes. Add the code to the modal's markup (e.g. inside the success message, revealed after subscribing) or generate one dynamically from your backend's response to the subscribe request and inject it into the DOM.

Switch the storage calls from sessionStorage to localStorage — that persists the dismissal flag indefinitely across browser sessions until the user clears their site data.