Source Code

<div class="container py-5 d-flex justify-content-center">
  <div class="bsmaint-page">
    <div class="alert alert-warning d-flex justify-content-between align-items-center mb-0 rounded-0" id="bsmaintBanner">
      <span class="small">&#9888; Scheduled maintenance begins in <strong id="bsmaintCountdown">--:--:--</strong>. The app may be briefly unavailable.</span>
      <button type="button" class="btn-close" id="bsmaintDismiss" aria-label="Dismiss"></button>
    </div>
    <div class="bsmaint-body p-4 text-center">
      <h6 class="fw-bold mb-1">Dashboard</h6>
      <p class="small text-muted mb-3">The rest of the app underneath the banner.</p>
      <button type="button" class="btn btn-sm btn-outline-secondary" id="bsmaintReload">Reload page (demo)</button>
    </div>
  </div>
</div>

Bootstrap Maintenance Mode Banner — Free HTML CSS JS Snippet

Bootstrap Maintenance Mode Banner · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

A non-blocking banner — the app underneath stays fully interactive, unlike a full maintenance takeover page
A live countdown recomputed every second from one fixed target timestamp, no date library required
Dismissal is saved to sessionStorage, guarded against a blocked or unavailable storage API
The demo "Reload" action re-runs the real dismissed-state check rather than faking persistence
Uses Bootstrap's genuine dismissible alert component as the banner's foundation

About this UI Snippet

Bootstrap Maintenance Mode Banner — HTML, CSS & JavaScript

Screenshot of the Bootstrap Maintenance Mode Banner snippet rendered live

This is deliberately not the same thing as a full maintenance takeover page like bootstrap-maintenance-page — that pattern replaces the entire app because it's actually down; this banner announces an *upcoming* window while the app underneath is still completely usable, which is why it's a plain dismissible Bootstrap alert sitting above normal page content rather than a takeover.

The countdown is computed fresh every second from a fixed target timestamp via renderCountdown(), converting the millisecond difference into hours/minutes/seconds with plain division and modulo — no date library needed for a straightforward countdown like this one.

Dismissal is real, persisted state, not just a hidden DOM node — clicking the close button writes to sessionStorage, guarded in try/catch the same way this collection's other storage-backed snippets are, so a blocked or unavailable storage API just means the banner reappears every load rather than breaking. Because this preview can't trigger an actual page reload, the "Reload page (demo)" button re-runs applyDismissedState() — the exact same check a fresh page load would perform — which is what proves the dismissal is genuinely being read back from storage rather than merely toggling a CSS class that a real reload would immediately undo.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet to an AI coding assistant like Claude and ask it to fetch the actual maintenance target time from a real API endpoint instead of a hardcoded constant, or to automatically re-show the banner (ignoring a stale dismissal) if the maintenance window itself changes to a new date after the user already dismissed the old one.

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 a Bootstrap 5.3 dismissible maintenance-mode banner, using the real Bootstrap CDN framework (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble it.

Requirements:
- A dismissible Bootstrap alert banner (using the real alert + btn-close markup) positioned above normal page content, which must remain fully usable and interactive underneath it.
- The banner text includes a live HH:MM:SS countdown to a fixed target timestamp a couple of hours in the future, updating every second.
- Clicking the banner's close button hides it and persists that dismissal in sessionStorage, guarded with try/catch in case storage is unavailable.
- On page load, check sessionStorage for a prior dismissal and keep the banner hidden if one exists, rather than always showing it — since this can't be demonstrated with a real page reload in a live preview, include a "Reload page (demo)" button that re-runs the same dismissed-state check a real reload's initial load would perform.

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 snippetA warning banner shows above the app content with a live countdown ticking down every second.
  2. 2
    Interact with the app below the bannerIt's fully usable — the banner is informational, not a blocking overlay.
  3. 3
    Click the \u00d7 to dismiss the bannerIt disappears immediately, and the dismissal is saved.
  4. 4
    Click "Reload page (demo)"The banner stays hidden, proving the dismissal genuinely persisted rather than just being a one-time CSS toggle.

Real-world uses

Common Use Cases

Announcing a scheduled maintenance window in advance
Distinct from bootstrap-maintenance-page, which is for the actual downtime itself rather than the advance warning.
SaaS dashboards and admin tools
Give users advance notice of a deploy or migration window without forcing them out of what they're currently doing.
Any product-wide announcement that shouldn't reappear once dismissed
The sessionStorage dismissal pattern generalizes to any "seen it, don't show again this session" banner.

Got questions?

Frequently Asked Questions

sessionStorage means the dismissal only lasts for the current browser tab/session — a maintenance window is a temporary, time-bound notice, so it's reasonable for the banner to reappear in a brand new session rather than being permanently dismissed forever, the way localStorage would make it.

No — it's a normal in-flow element above the rest of the content, not an overlay or modal, so every other part of the app remains fully clickable and usable while it's showing.

This snippet stops at 00:00:00; a real implementation would typically swap the banner's message (e.g. "Maintenance in progress") or trigger the actual maintenance takeover page at that point.

Yes. Keep the target timestamp as a constant, recompute the countdown in a setInterval-driven state update (cleared on unmount), and read/write the dismissed flag from sessionStorage in the same places this snippet does.