20 Tailwind CSS Empty States

6 Pure Tailwind14 Tailwind + JSPublished

20 Tailwind CSS empty states for zero-result searches, empty carts and wishlists, inbox-zero inboxes, no-data dashboards and tables, and 404 / 403 / 500 / offline pages — spanning onboarding checklists, error-with-retry panels, skeleton-to-empty transitions, a live-region announcement build, dark-mode and pure-CSS variants, and inline micro empty states. Scoped under .tes-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related20 CSS Empty States30 CSS Notifications25 Tailwind CSS FAQ Accordion

Premium icon pickPartner: Nucleo

Affiliate link

Need icons for the components you just copied?

You just browsed 20 Tailwind empty states. Nucleo is a library of 44,000+ SVG icons in five families (Core, UI, Sharp, Pixel and Micro Bold), with web, Mac and Windows apps to manage them. The All Icons Bundle is $149 with lifetime updates.

Get the Nucleo bundle

Nucleo. We may earn a commission, at no extra cost to you.

01. Tailwind CSS No Search Results Empty State (opens in a new tab)

Published

Tailwind + JS
A zero-result search inside a dark issue tracker, ported to utilities: removable filter chips, a scope tab bar and an empty block that names which filter caused the miss. The page state lives in one data-state attribute on the root, and group-data-[state=found]/root: swaps the empty block for the result list — the script never touches a class.

02. Tailwind CSS Onboarding Empty State with Setup Checklist (opens in a new tab)

Published

Tailwind + JS
The first-run screen of a project tool: a three-step setup checklist whose headline rewrites itself as you tick, a sidebar progress ring drawn with conic-gradient, and a template picker as the secondary path. Ticks are aria-pressed buttons, so every row styles itself with has-aria-pressed: — strike-through, colour and check icon included.

03. Tailwind CSS Empty Shopping Cart Page (opens in a new tab)

Published

Tailwind + JS
An e-commerce bag that is empty but never a dead end: an editorial headline, two clear paths (shop, reorder) and a shelf of best-sellers you can add straight into the bag. Add one and the empty state gives way to line items, a subtotal and a free-delivery progress bar — a conversion detail the CSS original didn't have.

05. Tailwind CSS Empty Table State (No Rows Found) (opens in a new tab)

Published

Tailwind + JS
An invoices table with no unpaid rows, kept a real <table> throughout: the empty message lives in a single colspan cell, layered over ghost rows so the user still sees the shape data will take. Add an invoice and the empty row hides itself with has-[[data-row]]: — the table decides when it's empty, not the script.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

07. Tailwind CSS Empty Dashboard / No Data State (opens in a new tab)

Published

Tailwind + JS
An analytics dashboard before its first event: four chart cards drawn as quiet ghosts under a frosted "Connect a source" panel. Pick a source and the panel steps aside while every chart animates into real data — bars grow to h-(--h), the line draws via stroke-dashoffset, cohort cells fade up — all from a single data-live attribute.

08. Tailwind CSS 500 Internal Server Error Page (opens in a new tab)

Published

Tailwind + JS
A 500 page that treats the user as a colleague: a plain-English cause, a retry that actually replays, a copyable incident reference and a live recovery log. The giant 500 is outlined text with [-webkit-text-stroke]; the retry button's three states are data-busy: and data-done: variants.

09. Tailwind CSS Empty Wishlist / Favorites Page (opens in a new tab)

Published

Pure Tailwind
A fashion store's "Saved" page with three empty tabs — saved pieces, price alerts, size holds — switched by radio inputs, no JavaScript. The heart on each "Last looked at" card is a real checkbox: tap one and the empty Saved tab fills with that piece, because the panel reads the hearts with group-has-[#id:checked]/w:.

11. Tailwind CSS Error State with Retry Button (opens in a new tab)

Published

Tailwind + JS
One dashboard panel fails while its neighbours stay live — the most common error a product ships and the least designed. The panel keeps its exact footprint through error → loading → ok, all three driven by data-state on the panel and read with group-data-[state=…]/p:, so the grid never jumps.

12. Tailwind CSS Offline / No Internet Connection Page (opens in a new tab)

Published

Tailwind + JS
A notes app that has lost its connection and says exactly what that means: edits are safe on this device, three are queued, nothing will be overwritten. The queue rows tick to "synced" one by one when the connection returns — and the demo listens to the browser's real online/offline events, so it reacts to your network too.

13. Tailwind CSS 403 Forbidden / Access Denied Page (opens in a new tab)

Published

Tailwind + JS
A restricted document rendered as a blurred silhouette behind a calm access card — the user can see the file exists, not what it says. "Request access" swaps the actions for a pending confirmation naming the two owners, and a withdraw link reverses it, all through one data-pending attribute.

14. Tailwind CSS 404 Page Not Found Template (opens in a new tab)

Published

Tailwind + JS
A newspaper's 404 that apologises like an editor: a tall serif numeral, an explanation of why the link broke (the archive rebuild), live archive search with highlighted matches, and a numbered "Most read" list. The list numbers come from a CSS counter in utilities — [counter-increment:tes14] before:content-[counter(tes14,decimal-leading-zero)].
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

19. Tailwind CSS Dark Mode Empty State (dark: Variant) (opens in a new tab)

Published

Pure Tailwind
An integrations settings page with no services connected, built to look right in both themes. A three-way System / Light / Dark switch is plain radios; Tailwind v4's @custom-variant dark reads them with :has() (and prefers-color-scheme for System), and @theme inline tokens flip in one place — no JavaScript, no flash.

20. Tailwind CSS Inline Micro Empty State Component (opens in a new tab)

Published

Pure Tailwind
Not every empty state deserves a hero. This finance dashboard shows one micro component in four homes — a sidebar rail, a feed card, a narrow budget tile and inside a table — and each adapts to its container, not the viewport, with @container and @max-2xs:. One icon, one line, one action, every time.
FAQ

Frequently asked questions

How do I build a no-search-results empty state with removable filter chips?
Demo 01 renders each active filter as a removable chip — a pill with a label and an icon <button> carrying an aria-label such as Remove status: open. The empty message is driven by the fact that nothing matched, not by the chips, so the copy stays honest: it names the scope that was searched and offers a one-click Clear all filters reset. The detail developers skip is that the reset must actually clear state, not just hide the message — in a real app wire the Clear button to reset your query params or state, then re-run the fetch. Keep the chips inside a real <ul> so each is a list item, mark the result region with role=status so the count change is announced, and give every remove button a visible focus-visible ring so a keyboard user can delete filters one at a time.
Can I toggle an empty state without any JavaScript in Tailwind?
Yes — Demo 06 (the filter no-matches pipeline) and Demo 10 (the notification flyout) both run on native form controls instead of a click handler. A hidden <input> checkbox or radio holds the state, a <label> is the clickable trigger, and the panel reacts with the peer-checked or has-[:checked] variant. For the filter case, checking a sidebar box narrows the list and reveals the empty copy entirely through CSS. The trap is specificity and source order: a peer-checked rule only wins if the peer is a previous sibling in the DOM, so the input has to come before the element it styles. Also remember pure-CSS state does not survive a reload and is not readable by your app logic, so use it for self-contained demos and disclosure UI — not for state your backend or router needs to know about.
How do I show an empty state inside a table without breaking the table structure?
Demo 05 keeps a real <table> the whole time rather than swapping in a <div> when rows run out. The empty message lives in a single <td> that spans the full width with colspan set to the number of header columns — otherwise the cell collapses under column one and the layout looks broken. Keep the <thead> rendered so the user still sees what the columns would hold, and put the message row inside <tbody>. The common mistake is conditionally hiding the table and rendering a separate empty block, which throws away the header context and causes a layout jump when data arrives. For assistive tech, the message cell reads as a normal table cell; if you want it announced on refresh, wrap the message text in an element with role=status so a fetch that returns zero rows is spoken, not silent.
How do I make an empty state accessible to screen reader users?
Demo 17 is built to be heard. The core piece is an aria-live region wrapping the results area, set to polite, so when a search resolves to nothing the message No records match your query is announced without stealing focus. Three rules matter. First, the live region must already be in the DOM before the text changes — if you insert the whole region at the same moment, many readers miss it. Second, keep the announcement short and specific; do not read the entire illustration. Third, don't trap focus on the empty card — a focus-revealed skip link lets keyboard users jump straight to the search input to try again. Decorative SVG art gets aria-hidden=true so it is skipped entirely, and the heading stays a real <h2> so the empty state appears in the document outline rather than being an orphaned block of text.
How do I transition from a skeleton loader to an empty state correctly?
This is the moment Demo 16 targets, and the bug it fixes is the one most products ship: the skeleton shimmer finishes and resolves into nothing, leaving a user staring at a blank panel wondering if it is still loading. The sequence is explicit — shimmer rows fade out, and the empty message fades in only after, so the two never overlap and the switch reads as deliberate. Drive it off your actual request lifecycle: show the skeleton while the promise is pending, then branch on the resolved result, not on a timer, so a slow network never flips to empty before the data lands. Guard the shimmer animation with motion-reduce so it stops for users who ask for reduced motion. Keep the skeleton's row count and spacing close to the real content's so there is no layout jump when either the data or the empty card takes its place.
How do I build an empty state that works in both light and dark mode?
Demo 19 pairs every surface color with its counterpart using Tailwind's dark: variant — for example bg-white with dark:bg-slate-900, and muted text as text-slate-500 with dark:text-slate-400. The piece developers forget is the illustration: a decorative icon or ghost shape that looks right on white often disappears on a dark panel, so its stroke and fill need dark: overrides too, not just the card background. Demo 19 also ships a three-way System / Light / Dark switch, which means your toggle has to cover the System case — that is driven by the OS prefers-color-scheme media query, while the explicit choices set a class on the root. Decide up front whether dark: keys off the media query or a class and configure Tailwind's dark mode to match, or the manual toggle and the system default will fight each other.
When should I use an inline micro empty state instead of a full-page one?
Demo 20 exists for exactly this decision: not every empty state deserves a hero. It shows one small component dropped into four homes — a sidebar list, a widget, a table cell, a summary tile — where a centered illustration and a big headline would be absurdly out of scale. Use the micro form when the empty region is one part of a populated screen and the rest of the page is doing fine; a single muted line like No alerts yet plus maybe one small action is enough. Reserve the full illustrated hero (Demos 03, 18) for when the empty region is the whole screen — an empty cart or an untouched cookbook. Getting this wrong in either direction hurts: a hero inside a sidebar looks broken, and a one-liner on a blank full page reads as an error. Match the weight of the empty state to the size of the space it fills.
What's the difference between an empty state and an error state, and how do I handle each?
They look similar but mean opposite things, and this collection separates them. An empty state (Demos 03, 15) says nothing is here yet, and that's fine — it guides the user forward with a next action. An error state (Demos 08, 11, 12) says something failed — it needs a cause and a recovery path, not encouragement. Demo 11 keeps one panel's failure isolated so its neighbours stay live, and its retry must actually re-run the request, not just clear the message. Demo 08 (a 500) offers a retry that replays the action plus a copyable error reference. Demo 12 (offline) reassures that local edits are safe and retries when the connection returns. The common bug is showing a cheerful empty illustration for a failed fetch — users read that as no data exists and stop trying. Branch on your request result: zero rows is empty, a rejected promise is an error, and each gets its own copy.

Related collections

30 CSS Badges preview

30 CSS Badges

30 hand-coded CSS badges for status indicators, notifications, membership tiers, live-data displays, and SEO / DevOps / financial dashboards — upload progress, typing indicator, transit line status, Core Web Vitals, ECG heartbeat, CI/CD build pipeline, countdown ring, live price ticker, keycap shortcut, wax seal, conference lanyard, and holographic collectibles. Copy-paste HTML and CSS, WCAG 2.2 accessible, MIT licensed.

20 CSS Banners & Alerts preview

20 CSS Banners & Alerts

20 hand-coded CSS banner and alert bars for e-commerce storefronts, SaaS dashboards, marketing sites, and compliance-bound products. Covers GDPR cookie consent with a real preferences panel, promo bars with countdown timers, free-shipping threshold progress, app-install smart banners, maintenance and incident status bars, newsletter signup bars, age verification gates, limited-stock urgency banners, live event announcements, browser upgrade notices, geo and currency switchers, sticky top announcement bars, bottom cookie bars, inline form validation alerts, icon-aligned alert banners, left-border accent alerts, diagonal stripe promos, full-width hero banners, animated gradient border alerts, and a text-wrapping laboratory that shows five strategies for the unbreakable string that breaks more banners than anything else. Every bar is scoped under a .ba-NN prefix for no-collision pasting, guards prefers-reduced-motion, carries the correct aria-live and role semantics, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

25 CSS Blockquotes preview

25 CSS Blockquotes

25 CSS blockquotes for article pull quotes, testimonial cards, docs callout admonitions, statement banners, and social-post embeds. Covers giant decorative quotation marks, minimalist hairline and classic left borders, animated gradient and glowing neon borders, glassmorphism and aurora backgrounds, brutalist cards, speech bubbles, thick accent rails, highlighted mark text, newspaper multi-column layouts, an expandable read-more disclosure, a scroll-snap testimonial slider, and one-click copy.

Search CodeFronts

Loading…