# 404 With Site Search

> Search-first 404 page: a controlled search field with a submit handler, plus a bordered list of suggested pages with titles, descriptions, and hover arrows.

A shadcn/ui block from the [@beste-ui](https://ui.beste.co) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/beste-ui/error2).

- Page, with a live preview: https://shadcn.exchange/r/beste-ui/error2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/beste-ui/error2-light-312f792c612bc5c9.png
- Registry item: https://ui.beste.co/r/error2.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.beste.co/r/error2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `clsx`, `lucide-react`, `tailwind-merge`
- Registry items: `button`, `input`

## Files

### `registry/error2/error2.tsx`

```tsx
"use client";

import { ArrowRight, Search } from "lucide-react";
import Link from "next/link";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";

interface SuggestionLink {
  title: string;
  description?: string;
  href: string;
}

interface Error2Labels {
  placeholder?: string;
  submit?: string;
  suggestions?: string;
}

interface Error2Props {
  code?: string;
  heading?: string;
  description?: string;
  labels?: Error2Labels;
  suggestions?: SuggestionLink[];
  onSearch?: (query: string) => void;
  className?: string;
}

export const error2Demo: Error2Props = {
  code: "Error 404",
  heading: "We could not find that page",
  description:
    "Search the site instead, or jump straight to one of the pages people land on most.",
  labels: {
    placeholder: "Search documentation, guides, and posts",
    submit: "Search",
    suggestions: "Suggested pages",
  },
  suggestions: [
    {
      title: "Getting started",
      description: "Install the library and ship your first page",
      href: "https://beste.co",
    },
    {
      title: "Components",
      description: "Every block, grouped by category",
      href: "https://beste.co",
    },
    {
      title: "Pricing",
      description: "Plans, licensing, and what is included",
      href: "https://beste.co",
    },
    {
      title: "Support",
      description: "Get help from a human within one business day",
      href: "https://beste.co",
    },
  ],
};

export function Error2({
  code,
  heading,
  description,
  labels = {},
  suggestions = [],
  onSearch,
  className,
}: Error2Props) {
  const { placeholder, submit, suggestions: suggestionsLabel } = labels;
  const [query, setQuery] = React.useState("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    onSearch?.(query.trim());
  };

  return (
    <section className={cn("py-16 md:py-24 w-full", className)}>
      <div className="mx-auto max-w-2xl px-4 md:px-6">
        <div className="text-center">
          {code && (
            <p className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
              {code}
            </p>
          )}

          {heading && (
            <h1 className="mt-4 text-balance text-3xl font-bold tracking-tight md:text-4xl">
              {heading}
            </h1>
          )}

          {description && (
            <p className="mt-4 text-balance text-base text-muted-foreground md:text-lg">
              {description}
            </p>
          )}
        </div>

        <form onSubmit={handleSubmit} className="mt-8 flex flex-col gap-3 sm:flex-row">
          <div className="relative flex-1">
            <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              type="search"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder={placeholder}
              aria-label={placeholder}
              className="h-10 pl-9"
            />
          </div>
          {submit && (
            <Button type="submit" size="lg">
              {submit}
            </Button>
          )}
        </form>

        {suggestions.length > 0 && (
          <div className="mt-10">
            {suggestionsLabel && (
              <p className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
                {suggestionsLabel}
              </p>
            )}
            <div className="mt-4 divide-y rounded-md border">
              {suggestions.map((suggestion, index) => (
                <Link
                  key={index}
                  href={suggestion.href}
                  className="group/error2 flex items-center justify-between gap-4 px-4 py-4 transition-colors hover:bg-muted"
                >
                  <span>
                    <span className="block text-base font-semibold text-foreground">
                      {suggestion.title}
                    </span>
                    {suggestion.description && (
                      <span className="mt-1 block text-sm text-muted-foreground">
                        {suggestion.description}
                      </span>
                    )}
                  </span>
                  <ArrowRight className="size-4 shrink-0 text-muted-foreground transition-transform group-hover/error2:translate-x-0.5" />
                </Link>
              ))}
            </div>
          </div>
        )}
      </div>
    </section>
  );
}
```
