Does Tailwind CSS include an alert component?
No. Tailwind works through single-purpose utilities (the Tailwind basics take five minutes to read), so you assemble the alert yourself. The Tailwind v1 documentation already described alerts as something easy to build from existing utilities.
If you’d rather not assemble it, finished versions exist. Tailwind Plus, the paid collection from Tailwind Labs, lists them under Application UI, Feedback. daisyUI and Flowbite add alert classes or markup of their own. The plain route is still there too: a div, a role attribute and a handful of classes.
Bootstrap goes the other way. Its documentation lists eight contextual classes, from alert-primary to alert-dark, so one class name gives you a finished box (Bootstrap 5 documentation).
Tailwind gives up that convenience and hands you control over every spacing and color value in return. Weighing the two? This comparison of Tailwind and Bootstrap goes into the wider trade-off.
How to build a Tailwind alert with utility classes
Start with a flex container, give it padding, a rounded border and a tinted background, then put the icon and the message inside. Most of the work happens in flex, gap-3, rounded-lg, border, p-4 and text-sm.
- Create a
div and give it the utilities above. - Pick one color family and match background, border and text, for example
bg-blue-50 border-blue-200 text-blue-800. A pale fill with dark text from the same family stays readable. - Add the icon as an inline SVG with
size-5 shrink-0, so wrapping text can’t squash it. - Write the title in
font-medium and put the description underneath. - Set
role="alert" for errors and role="status" for routine information. - Paste the markup into the Tailwind Play browser editor and test it before it goes anywhere near a project.
<div role="status" class="flex gap-3 rounded-lg border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800">
<svg class="size-5 shrink-0" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
<circle cx="10" cy="10" r="8" />
<path d="M10 9v5M10 6.5v.01" stroke-linecap="round" />
</svg>
<div>
<p class="font-medium">Plan renews soon</p>
<p>Your subscription renews in 3 days.</p>
</div>
</div>
Adding an icon and a title
Heroicons is the natural pick. Tailwind Labs maintains it, and the SVGs take utility classes directly. It comes in outline and solid at 24px, mini at 20px and micro at 16px.
The micro set arrived in version 2.1.0 on December 18, 2023, according to the Heroicons changelog. The 20px mini set matches size-5 and sits well beside text-sm copy.
I’d take an inline SVG straight into the HTML over an icon font here. The icon picks up the text color through currentColor, so it follows the variant with no extra classes.
Decorative icons get aria-hidden="true", which is what Flowbite’s own alert markup does.
Short messages read fine with a bold lead-in on the same line, which is how Flowbite writes its examples. Longer ones need a stacked title and description, like the sample above.
Tailwind alert variants for success, error, warning and info
The structure never changes. What differs between the four is the color family and the icon, plus the ARIA role.
| Variant | Color classes | Role | Heroicons icon |
|---|
| Success | bg-green-50 border-green-200 text-green-800 | status | check-circle |
| Error | bg-red-50 border-red-200 text-red-800 | alert | x-circle |
| Warning | bg-amber-50 border-amber-200 text-amber-800 | status, or alert if urgent | exclamation-triangle |
| Info | bg-blue-50 border-blue-200 text-blue-800 | status | information-circle |
Color is only one axis. The same four states also come in several visual treatments.
The soft version is the tinted fill from the table (alert-soft in daisyUI). Outline drops the fill and keeps the border (alert-outline in daisyUI, and an outlined variant in Material Tailwind). daisyUI also has alert-dash for a dashed edge, and Material Tailwind builds gradients from from- and to- utilities. An accent border puts a thick edge in the variant color, border-l-4 for the left or border-s-4 for layouts that flip in RTL.
daisyUI covers the whole grid with 4 color classes (alert-info, alert-success, alert-warning, alert-error) and 3 style modifiers, per its documentation.
Libraries disagree on what to call the red one. daisyUI says error, Flowbite and Bootstrap say danger, and shadcn/ui says destructive. Material Tailwind skips semantic names and takes a color, so its red alert is color="red".
Pick one name for your own components and map the library names onto it.
Copy accent-border snippets with care. The Flowbite Svelte docs describe the accent as a left edge, yet the example code uses border-t-4, which draws it on top.
How to make a Tailwind alert dismissible
A dismissible alert needs a close button and something that hides the alert when the button is clicked. That something can be a few lines of JavaScript, an Alpine.js x-show binding, a React state flag or Flowbite’s data-dismiss-target attribute.
Whichever route you pick, the button should have type="button" so it never submits a surrounding form. Add aria-label="Close", since the visible content is only a symbol, and give it a visible focus ring such as focus:ring-2.
Style it like any other Tailwind button, with small padding, rounded corners, ms-auto to push it to the end of the flex row and a hover background in the variant color.
Plain JavaScript and Flowbite data attributes
Honestly, a few lines of JavaScript cover every case here, and the first one is a single click handler that adds hidden to the alert.
<div id="alert-1" role="alert" class="flex rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800">
<p>Payment failed. Update your card and try again.</p>
<button type="button" aria-label="Close" class="ms-auto rounded-lg p-1.5 hover:bg-red-100 focus:ring-2 focus:ring-red-300" onclick="document.getElementById('alert-1').classList.add('hidden')">×</button>
</div>
With Flowbite you put data-dismiss-target="#alert-1" on the button and a matching id on the alert. Its Dismiss class adds opacity and transition classes, then applies hidden after the configured duration (Flowbite 2.3.0 source).
Bootstrap’s documentation recommends listening for closed.bs.alert and moving focus somewhere sensible, because a dismissed alert can take keyboard focus with it. Flowbite’s Dismiss accepts an onHide callback that does the same job.
One snag turns up in a GitHub discussion in the htmx repository (#1158). Flowbite’s dismiss button worked on first load but not inside an HTML fragment swapped in later, because the listeners were never attached to the new nodes.
Binding the handler to a parent that survives the swap (event delegation) avoids the problem.
Alpine.js x-show
Alpine.js dismisses an alert with one state value and one directive.
<div x-data="{ open: true }" x-show="open" x-transition role="status" class="flex rounded-lg border border-green-200 bg-green-50 p-4 text-sm text-green-800">
<p>Profile saved.</p>
<button type="button" @click="open = false" aria-label="Close" class="ms-auto rounded-lg p-1.5 hover:bg-green-100">×</button>
</div>
The Alpine.js docs state that x-show toggles display: none, and they recommend x-cloak when the default state is hidden, to stop page flicker. Adding x-transition gives a fade for free.
Because x-show only hides the node, the alert stays in the DOM. That suits a live region that gets filled later.
React state
React needs one state flag and nothing else. Start with const [open, setOpen] = useState(true), render the alert only when open is true, and call setOpen(false) from the button’s onClick.
Material Tailwind’s React Alert uses the same shape, controlled through open and onClose props (Material Tailwind docs). In Vue, flowbite-vue’s FwbAlert passes an onCloseClick handler into its default slot.
Unmounting removes the live region along with the alert. If screen reader announcements matter, keep an empty container mounted and render the alert inside it.
How to make a Tailwind alert accessible
Give errors role="alert" and routine messages role="status". The first interrupts the screen reader at once, the second waits for a pause, and neither takes keyboard focus.
MDN Web Docs documents role="alert" as equal to aria-live="assertive" plus aria-atomic="true", and role="status" as aria-live="polite" plus aria-atomic="true" (WAI-ARIA).
Both are ARIA live regions, so stacking an extra aria-live="assertive" on role="alert" is redundant, and MDN notes that combining the two causes double speaking in VoiceOver on iOS. A react-hot-toast issue (#336) cites MDN for exactly that double-speaking problem.
The sources don’t agree on everything, though. MDN reserves alert for content that appears dynamically, yet the Material Tailwind, Flowbite and daisyUI examples put role="alert" on static markup. Vuetify0’s docs warn that an assertive region present at load interrupts the screen reader’s first pass of the page, while a polite status can stay mounted.
GitLab UI keeps role="alert" on its success variant but sets aria-live to polite, and gives info and tip variants role="status" (merge request #3054). HextaUI announces only destructive and warning alerts assertively.
A workable rule is assertive for errors, polite for everything else, and a warning upgraded to alert only when the user must act now.
Injection is the other trap. A report in Freedom Scientific’s standards-support repository (#696) describes JAWS not announcing a role="status" region injected into the DOM, with NVDA behaving the same, while VoiceOver on Safari announced it.
So render the container first and swap the content in, which is the pattern MDN describes. The empty wrapper has no padding, so it takes no space until a message lands inside it.
<div id="flash" role="alert"></div>
<script>
const flash = document.getElementById("flash");
function showError(message) {
const box = document.createElement("div");
box.className = "rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800";
box.textContent = message;
flash.replaceChildren(box);
}
</script>
WCAG 2.2 success criterion 4.1.3, Status Messages (Level AA), requires status messages to be programmatically determinable through role or properties, according to W3C. Messages that move focus, such as an error shown inside a dialog, fall outside it.
Alert copy is body text, so color contrast rules apply to it, which means 4.5:1 for normal text and 3:1 for large text (W3C, Understanding SC 1.4.3).
The W3C adds that computed ratios are not rounded, so 4.499:1 fails. Measure each pale-fill pair from the variants table with a checker instead of trusting the shade numbers.
How to style Tailwind alerts for dark mode and Tailwind CSS v4
In Tailwind CSS v4 the dark: variant follows prefers-color-scheme out of the box. A manual toggle needs one line in your CSS file, because v4 moved configuration into CSS.
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
Left alone, the alert follows the operating system, with no JavaScript and no toggle button. A theme toggle wants the selector approach instead, where a dark class on an ancestor flips everything. To drive it from a data-theme="dark" attribute, swap the selector in the variant line to @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));.
In v3 the same job was a darkMode: 'selector' entry in the config, which replaced the class strategy in v3.4.1 (Tailwind v3 docs). Skip the override in v4 and a .dark class on the html element does nothing, because dark: still listens to the media query.
Each alert variant needs a dark pair. The error variant from the earlier table flips like this.
bg-red-50 becomes dark:bg-red-950border-red-200 becomes dark:border-red-900text-red-800 becomes dark:text-red-200
shadcn/ui’s docs use the same idea, adding bg-amber-50 dark:bg-amber-950 to its Alert for a custom amber variant. Run both sets through an accessible color palette generator, since a pair that passes in light mode can fail in dark.
Four variants with three dark classes each get noisy. Semantic tokens define the pairs once.
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
:root {
--alert-error-bg: #fef2f2;
--alert-error-text: #991b1b;
}
.dark {
--alert-error-bg: #450a0a;
--alert-error-text: #fecaca;
}
@theme inline {
--color-alert-error-bg: var(--alert-error-bg);
--color-alert-error-text: var(--alert-error-text);
}
Variables in the --color-* namespace generate utilities, so --color-alert-error-bg gives you bg-alert-error-bg (Tailwind theme docs). The inline option is what lets a theme variable point at another variable.
The markup then carries no dark: classes at all. For one or two alerts, inline pairs are simpler, and tokens only pay off once every variant repeats the same dark treatment.
Which Tailwind alert library should you use?
Write the utilities yourself for one or two alerts. Reach for a library when four variants, dismissal and dark mode repeat across screens.
| Library | Alert API | Dismiss needs JavaScript | Frameworks |
|---|
| Flowbite | HTML markup with Tailwind classes | Yes, Dismiss class | HTML, Vue, Svelte, Angular ports |
| daisyUI | Class names such as alert-error | Not included | Any, pure CSS |
| shadcn/ui | React components, variant prop | You write the state | React |
| Preline UI | HTML markup, data-hs-remove-element attribute | Yes, Preline script | HTML, framework guides |
| Headless UI | No alert component | Not applicable | React, Vue |
Headless UI is not an alert library. The React listing on headlessui.com (v2.1 at the time of writing) names sixteen components, from Menu to Textarea, and none is an alert.
What it does offer is Transition for a dismiss animation and Dialog for the blocking case. Preline UI ships solid, soft and bordered alerts with a dismiss attribute, and its script has to be loaded for that attribute to work.
Flowbite
The docs cover info, danger, success, warning and dark alerts, with icon, border and dismissible variants. Ports exist for Vue, Svelte and Angular (flowbite-vue, flowbite-svelte, flowbite-angular), and it works from plain HTML.
The downsides are real, though. Dismissal needs Flowbite’s JavaScript, and the current docs use theme tokens such as bg-danger-soft and text-fg-danger-strong, which are not Tailwind defaults, so a pasted snippet renders unstyled without Flowbite’s theme.
daisyUI
daisyUI 5 is built for Tailwind CSS 4, and its release notes put the full compressed stylesheet at 34 kB, with single component files such as toggle.css loadable on their own.
Alert classes are plain CSS, so no script loads, and alert-vertical or alert-horizontal changes the layout. A responsive form such as alert-vertical sm:alert-horizontal, stacked on phones and side by side from the sm breakpoint, works in v5.
What you don’t get is any dismiss behavior. The class names also hide the utilities, so a tweak means overriding the plugin’s CSS.
shadcn/ui
This is the React option, and it copies the source into your project.
The composition is Alert, AlertTitle, AlertDescription and AlertAction, with a variant prop that accepts default or destructive (shadcn/ui docs). Every other state comes from classes you add yourself.
Since you own the code, dark pairs and variants live in one component. The catch is two built-in variants and a React-only design. Set up Tailwind CSS in React first if the project does not have it yet.
Tailwind Plus
Tailwind Labs designed and built it, and the alert blocks are aimed at billing issues, form validation errors and confirmations. It’s paid, and the markup is copy-paste, so you maintain it.
Worth it when the whole application UI comes from it. Overkill for a single alert box.
When a Tailwind alert is the wrong component
An alert suits a message that should stay visible without stopping the user. It goes wrong when the message must be answered or should vanish on its own, and it goes wrong when several fire together.
- Short-lived confirmations are toast territory. Tailwind Plus files toasts under Overlays, Notifications, apart from its Feedback alerts, and daisyUI keeps a separate Toast beside Alert.
- Decisions that block the task call for a modal with
role="alertdialog" and focus moved inside it. - For field-level errors, inline validation next to the field usually works better than an alert.
- MDN calls simultaneous and unnecessary alerts a poor experience, so keep one live region and one message at a time.
A toast notification suits confirmations the user can ignore, because it disappears on its own. An alert stays until someone clears it.
Where the line between alert and dialog sits depends on the source. MDN says that if the user is expected to close the alert, use alertdialog instead. The W3C ARIA Authoring Practices Guide keeps alerts away from keyboard focus so the user can keep working, and describes alertdialog as a modal dialog that interrupts the workflow to get a response.
The test that settles it is whether the user has to respond before continuing. A banner they can ignore stays an alert with a close button.
A message that has to be answered becomes a modal dialog, with an accessible name and description wired through aria-labelledby and aria-describedby (MDN). Capturing the focus of the entire page is the hard part, and the Tailwind Labs v1.0 announcement presents Headless UI’s Dialog as the component for exactly that.
Tailwind Alert FAQ
Are Tailwind Plus alert components free?
No. Tailwind Plus is a paid collection from Tailwind Labs.
daisyUI and Preline UI are open-source alternatives, and a basic alert needs only a handful of utility classes anyway.
How do I auto-dismiss a Tailwind alert after a few seconds?
Start a timer when the alert appears and hide the alert when it ends. In Alpine.js, one setTimeout call inside x-init flips the open flag after your chosen delay.
Keep errors on screen until the user closes them, since a timer can remove the message before a screen reader finishes reading it.
Can a Tailwind alert fade in and out?
Yes. Pair transition-opacity and duration-300 with opacity-0, then apply hidden once the duration ends so the element leaves the layout.
Add motion-reduce:transition-none for visitors who ask their operating system for less motion.
How do I make a Tailwind alert full width and responsive?
A flex container already fills its parent. Add w-full only inside shrink-wrapping layouts, and cap the width on large screens with a max-w utility such as max-w-xl.
For narrow screens, stack the icon above the text with flex-col and return to a row using sm:flex-row. Flowbite takes a lighter route, switching alignment with items-start sm:items-center.
Testing a Tailwind Alert with a Screen Reader
A Tailwind alert needs a spoken test in at least two screen readers, NVDA on Windows and VoiceOver on Safari, because a pass in one predicts nothing about the other.
Start with NVDA, since it costs nothing. Move to VoiceOver on Safari after that, and finish on whichever screen reader your own visitors use.
Automated checkers confirm that role=”alert” exists but cannot hear whether the message was spoken, so each of the four variants takes a manual pass. That time is the price of this approach.
Revisit the order whenever NVDA or VoiceOver changes how it treats injected live regions.
Alerts make up one item in a longer audit, and a web accessibility checklist covers the rest of the page.