# 404 3

> Full-bleed photo 404 with two actions and a row of next-step links (from Marbrook)

A shadcn/ui block from the [@cnippet-blocks](https://blocks.cnippet.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cnippet-blocks/404-3).

- Page, with a live preview: https://shadcn.exchange/r/cnippet-blocks/404-3
- Screenshot: https://shadcn.exchange/shot/cnippet-blocks/404-3/light.png
- Registry item: https://blocks.cnippet.dev/r/404-3.json
- Categories: 404
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/cnippet-blocks/404-3.json
```

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

## Dependencies

- Registry items: `@cnippet/button`

## Files

### `registry/default/sections/404-3/index.tsx`

```tsx
import Image from "next/image";
import Link from "next/link";

import { Button } from "@/components/ui/button";

import type { NotFoundContent } from "../_types/not-found";
import { defaultNotFound3Content } from "./data";

/** A full-bleed photo 404 with two actions and a row of places to go next. */
export default function NotFound3({
  content = defaultNotFound3Content,
}: {
  content?: NotFoundContent;
}) {
  const [primaryAction, secondaryAction] = content.actions;

  return (
    <section className="relative isolate flex min-h-[80vh] flex-col justify-end pt-28 pb-20 sm:pb-28">
      <div className="absolute inset-0 -z-10 overflow-hidden">
        <Image
          alt={content.image.alt}
          className="object-cover object-center"
          fill
          priority
          sizes="100vw"
          src={content.image.src}
        />
        <div className="absolute inset-0 bg-linear-to-t from-primary/90 via-primary/50 to-primary/30" />
        <div className="absolute inset-0 bg-linear-to-r from-primary/70 via-primary/20 to-transparent" />
      </div>

      <div className="mx-auto w-full max-w-7xl px-5 sm:px-8 lg:px-12">
        <p className="inline-flex items-center gap-2 font-medium text-[0.6875rem] text-white/50 uppercase tracking-[0.22em]">
          <span className="size-1 bg-secondary" />
          {content.eyebrow}
        </p>

        <h1 className="mt-5 max-w-3xl font-display text-5xl text-white leading-[1.04] sm:text-6xl">
          {content.title}
        </h1>

        <p className="mt-6 max-w-lg text-white/70 leading-relaxed">
          {content.description}
        </p>

        <div className="mt-10 flex flex-wrap items-center gap-3">
          {primaryAction && (
            <Button
              className="border-none bg-white px-7 text-primary shadow-none hover:bg-white/90"
              render={<Link href={primaryAction.href} />}
              size="xl"
            >
              {primaryAction.label}
            </Button>
          )}
          {secondaryAction && (
            <Button
              className="border-white/40 bg-transparent px-7 text-white shadow-none before:shadow-none hover:bg-white/10 dark:bg-transparent"
              render={<Link href={secondaryAction.href} />}
              size="xl"
              variant="outline"
            >
              {secondaryAction.label}
            </Button>
          )}
        </div>

        {content.links.length > 0 && (
          <ul className="mt-12 grid gap-px overflow-hidden rounded-lg border-white/15 border-t bg-white/15 sm:grid-cols-2 lg:grid-cols-4">
            {content.links.map((item) => (
              <li key={item.href}>
                <Link
                  className="block h-full bg-primary/50 p-6 text-sm text-white/80 backdrop-blur-sm transition-colors hover:bg-primary/70 hover:text-white"
                  href={item.href}
                >
                  {item.label}
                </Link>
              </li>
            ))}
          </ul>
        )}
      </div>
    </section>
  );
}
```

### `registry/default/sections/404-3/data.ts`

```ts
import type { NotFoundContent } from "../_types/not-found";

export const defaultNotFound3Content: NotFoundContent = {
  actions: [
    {
      href: "/",
      label: "Back to the homepage",
    },
    {
      href: "/properties",
      label: "Browse listings",
    },
  ],
  description:
    "The page you asked for has either sold, been withdrawn, or never existed. All three happen. Here is where everything else lives.",
  eyebrow: "Error 404",
  image: {
    alt: "A shuttered cottage on Alder Row after dark",
    src: "https://res.cloudinary.com/qgiongel/image/upload/q_auto/f_auto/v1786950553/templates/estate/marbrook/cndn_marbrook004.jpg",
  },
  links: [
    {
      href: "/properties",
      label: "Every home on our books",
    },
    {
      href: "/neighbourhoods",
      label: "The six district guides",
    },
    {
      href: "/journal",
      label: "The market letter",
    },
    {
      href: "/contact",
      label: "Talk to an advisor",
    },
  ],
  title: "That address is not on our books.",
};
```

### `registry/default/sections/_types/common.ts`

```ts
/**
 * Shapes shared by every token-based section. Each section ships this file
 * alongside its own category types, so installing several sections simply
 * rewrites the same file.
 */

/** Which band a section sits on. A style choice, never part of `content`. */
export type Surface = "default" | "muted" | "primary" | "secondary";

export type ImageAsset = {
  alt: string;
  src: string;
};

export type LinkAction = {
  href: string;
  label: string;
};

/** Eyebrow + heading + optional lede, the header most sections open with. */
export type SectionIntro = {
  description?: string;
  eyebrow: string;
  title: string;
};

/** A figure that counts up when it scrolls into view. */
export type Stat = {
  decimals?: number;
  label: string;
  prefix?: string;
  suffix?: string;
  value: number;
};

/** A figure that is displayed as written ("1,240+", "4.9/5"). */
export type Highlight = {
  label: string;
  value: string;
};

export type FormField = {
  label: string;
  name: string;
  /** Only for `type: "select"`. The first option is the default. */
  options?: string[];
  placeholder?: string;
  required?: boolean;
  /** `full` spans both columns of a two-column form. */
  span?: "full" | "half";
  type?: "email" | "select" | "tel" | "text" | "textarea";
};

export type FormContent = {
  description?: string;
  fields: FormField[];
  note?: string;
  submitLabel: string;
  success: {
    description: string;
    resetLabel: string;
    title: string;
  };
  title: string;
};
```

### `registry/default/sections/_types/not-found.ts`

```ts
import type { ImageAsset, LinkAction } from "./common";

export type NotFoundContent = {
  actions: LinkAction[];
  description: string;
  eyebrow: string;
  image: ImageAsset;
  links: LinkAction[];
  title: string;
};
```
