Empty Search

( Empty State / Search / Flat / Animated )

Zero-state illustration of a magnifier sweeping over empty result rows, with optional title and description.

Live preview and source code for Empty Search

A zero-state illustration for search: a magnifier drifting over a card of blank result rows, with a headline and a supporting line underneath. It is inline SVG with a single accent colour, so it inherits the page font, scales to any size without an asset, and needs no image request. The float and the sparks can be switched off with one prop.

Installation

Copy the source from the Code tab above into your project as EmptySearch.tsx. The artwork is inline SVG and the two lines of copy are styled inline, so there is no image to host and no stylesheet to import.

It imports framer-motion for the float and the pulsing sparks:

Terminal
npm install framer-motion

If you only ever render it with still, you can drop that import and change the two motion elements back to plain SVG elements, leaving a file with no dependencies beyond React.

Usage

Every prop is optional. size is the rendered width in pixels and the height follows at 78 percent of it:

Example.tsx
import EmptySearch from "./components/EmptySearch";

export default function Example() {
    return (
        <EmptySearch
            size={260}
            accent="#7c3aed"
            title="No results found"
            description="Try a different search term or clear your filters."
        />
    );
}

Pass title={null} and description={null} for the artwork on its own, and still to freeze it.

When to reach for it

A results list that came back empty, where the next step is changing the query rather than creating something. The default copy points at the search term and the filters for exactly that reason.

A filtered table or directory with no matches, sitting in the space the rows would occupy.

A command palette or in-app search panel at a smaller size, where the artwork has to fit a dropdown rather than a page.

When not to

If nothing exists yet. A first-run empty state should point at the action that fills it, not at a search that found nothing. Different illustration, different copy.

If something went wrong. A failed request is not an empty result, and telling someone to change their search term when the server is down sends them in the wrong direction. Reach for Error 404 or a plain error message instead.

If it needs to be the page's heading. The title renders as a styled <span>, not an <h1> or <h2>. That is deliberate, so the illustration can sit inside a page that already has a heading, but it means you supply your own if this is the main content.

Accessibility

The SVG has role="img" and a fixed label describing it as having no search results, so the whole drawing is announced as one thing and none of its parts leak into the reading order. The title and description below it are ordinary text and are read normally.

Because the label is fixed in the source, translating the copy or changing title does not change it. Update the aria-label in the copied file when you change the language of the page.

The animation does not check prefers-reduced-motion. The magnifier floats and the sparks pulse on an infinite loop until you pass still. Read the media query yourself and pass still from it if you support that setting.

Notes

The <defs> block declares the ids es-card and es-lens. Rendering two of these on one page duplicates those ids, so give them a unique suffix in the copied file if that ever happens.

still removes the sparks entirely rather than just stopping them, so the still version is slightly plainer than a paused frame of the animated one.

Colours other than the accent are fixed greys drawn to sit on a light background. On a dark surface the result card and its rows will need recolouring in the copied file.