DEV3 Technologies home

Empty-state panel

A centered empty-state block with an icon, a short explanation and primary and secondary actions for blank lists.

Tailwind CSSFeedback and empty statesempty-statepanelonboardingux

Live preview

Code

<section class="mx-auto max-w-xl rounded-2xl border-2 border-dashed border-slate-300 px-6 py-16 text-center">
  <span class="mx-auto flex size-14 items-center justify-center rounded-full bg-indigo-50 text-indigo-600">
    <svg class="size-7" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"
      stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
      <path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/>
    </svg>
  </span>

  <h2 class="mt-5 text-balance text-lg font-semibold text-slate-900">No projects yet</h2>
  <p class="mx-auto mt-2 max-w-sm text-sm text-slate-600">
    Projects group your files, members and reports in one place. Create the first one to get going.
  </p>

  <div class="mt-6 flex flex-col items-center justify-center gap-3 sm:flex-row">
    <a href="/projects/new"
      class="inline-flex items-center rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
      Create project
    </a>
    <a href="/docs/projects" class="text-sm font-medium text-slate-600 underline-offset-4 hover:underline">
      Read the guide
    </a>
  </div>
</section>

How it works

What it solves

A blank table or list looks broken unless the page explains why it is empty and what to do next. An empty-state panel turns that dead end into a first step, and it should be reusable across lists with different wording.

How it works

The panel is a dashed-border container so it reads as a placeholder rather than content. An icon tile at the top gives the eye an anchor, followed by a heading, one supporting sentence limited with max-w-sm, and two actions: a filled primary button and a quieter text link for documentation. The wrapper uses role="status" only when the state appears dynamically after a search or filter, not on initial render. Spacing comes from space-y-* and py-16, which keeps the block tall enough to feel intentional inside a card but compact inside a small column. Colour tokens stay neutral so the primary action is the only strong colour.

Adapting it

Create variants by changing copy: "No results" with a clear-filters button, "Nothing here yet" with a create button, or "No access" with a request link. Replace the icon path to match the object type. Use text-balance on the heading for long translations.

Pitfalls

  • Do not show the panel while data is still loading; render a skeleton first.
  • Make the primary action do the thing the empty list is missing.
  • Avoid apologetic copy; say what the area is for.

How to use it

  1. Paste empty-state.html where your list or table would render when it has no rows.
  2. Show it only when the count is zero and loading has finished.
  3. Edit the heading, sentence and the two links.
  4. Add role="status" if you toggle it in after a filter change.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets