---
title: Hero
description: Hero sections for clear, confident first impressions.
group: Blocks
order: 28
---

Hero layouts for setting the tone and getting to the point. Pick one, then make the words and visuals yours.

## Simple

A headline, short supporting line, primary action, and ghost action. Left-aligned on mobile, centered on larger screens.

```tsx
"use client";

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

type Hero01Props = {
  className?: string;
};

export function Hero01({ className }: Hero01Props) {
  return (
    <section className={cn("@container w-full bg-background", className)}>
      <div className="mx-auto flex h-full w-full max-w-2xl flex-col items-start justify-center px-6 py-16 text-left @[32rem]:items-center @[32rem]:text-center">
        <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[32rem]:text-3xl">
          Ship interfaces that feel finished
        </h1>
        <p className="mt-3 max-w-md text-pretty text-sm tracking-wide text-muted-foreground">
          Ready made blocks you can restyle with your tokens.
        </p>
        <div className="mt-6 flex items-center justify-start gap-2 @[32rem]:justify-center">
          <Button type="button" size="sm">
            Get started
          </Button>
          <Button type="button" size="sm" variant="ghost">
            Browse blocks
          </Button>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-01
```

### Usage

```tsx
import { Hero01 } from "@/components/blocks/hero/hero-01"
```

```tsx
<Hero01 />
```

## Horizon glow

A headline and single action over a dark half-circle horizon, with soft glow and subtle noise.

```tsx
"use client";

import { useId } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

type Hero02Props = {
  className?: string;
};

export function Hero02({ className }: Hero02Props) {
  const noiseId = useId().replace(/:/g, "");

  return (
    <section
      className={cn(
        "@container relative w-full overflow-hidden bg-black text-white",
        className,
      )}
    >
      <div
        aria-hidden="true"
        className="absolute inset-0"
        style={{
          backgroundImage: [
            "radial-gradient(ellipse 125% 48% at 50% 78%, rgb(255 255 255 / 0.55) 0%, rgb(210 170 255 / 0.42) 8%, rgb(150 70 210 / 0.32) 22%, rgb(90 30 140 / 0.16) 40%, transparent 62%)",
            "radial-gradient(ellipse 90% 35% at 50% 100%, rgb(40 10 60 / 0.9) 0%, transparent 55%)",
          ].join(", "),
        }}
      />
      <div
        aria-hidden="true"
        className="absolute left-1/2 bottom-[-48%] h-[95%] w-[165%] -translate-x-1/2 rounded-[100%] bg-black"
        style={{
          boxShadow:
            "0 -1px 0 0 rgb(255 255 255 / 0.75), 0 -8px 28px 2px rgb(255 255 255 / 0.2), 0 -28px 90px 24px rgb(170 90 230 / 0.28)",
        }}
      />
      <svg
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 size-full opacity-[0.7] mix-blend-soft-light"
      >
        <filter id={noiseId}>
          <feTurbulence
            type="fractalNoise"
            baseFrequency="0.95"
            numOctaves="4"
            stitchTiles="stitch"
          />
        </filter>
        <rect width="100%" height="100%" filter={`url(#${noiseId})`} />
      </svg>

      <div className="relative z-10 mx-auto flex h-full min-h-[28rem] w-full max-w-2xl -translate-y-14 @[32rem]:-translate-y-20 flex-col items-center justify-center px-6 py-16 text-center">
        <h1 className="text-balance text-xl font-medium tracking-wide text-white @[32rem]:text-2xl">
          Build once, reuse everywhere
        </h1>
        <div className="mt-6 flex items-center justify-center">
          <Button
            type="button"
            size="sm"
            className="bg-white text-black hover:opacity-85"
          >
            Get started
          </Button>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-02
```

### Usage

```tsx
import { Hero02 } from "@/components/blocks/hero/hero-02"
```

```tsx
<Hero02 />
```

## Inset image

A full-bleed image with just enough space around the edges to let the page breathe.

```tsx
"use client";

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

type Hero03Props = {
  className?: string;
};

export function Hero03({ className }: Hero03Props) {
  return (
    <section
      className={cn("@container w-full bg-background p-3 sm:p-4", className)}
    >
      <div className="relative min-h-[28rem] w-full flex-1 overflow-hidden rounded-md">
        <img
          src="/assets/brand/demos/card-cover.png"
          alt=""
          className="absolute inset-0 size-full object-cover"
        />
        <div
          aria-hidden="true"
          className="absolute inset-0 bg-black/45"
        />
        <div className="absolute inset-0 z-10 flex flex-col items-start justify-end p-6 text-left sm:p-8">
          <h1 className="max-w-xl text-balance text-2xl font-medium tracking-wide text-white @[32rem]:text-3xl">
            Ship interfaces that feel finished
          </h1>
          <p className="mt-3 max-w-md text-pretty text-sm tracking-wide text-white/75">
            Ready made blocks you can restyle with your tokens.
          </p>
          <Button type="button" size="sm" className="mt-6">
            Get started
          </Button>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-03
```

### Usage

```tsx
import { Hero03 } from "@/components/blocks/hero/hero-03"
```

```tsx
<Hero03 />
```

## Split

Copy on the left and a cover image on the right. The layout stacks on narrow widths.

```tsx
"use client";

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

type Hero04Props = {
  className?: string;
};

export function Hero04({ className }: Hero04Props) {
  return (
    <section className={cn("w-full bg-background", className)}>
      <div
        data-slot="block-split"
        className="flex h-full min-h-[28rem] w-full flex-col gap-4 p-4 lg:flex-row lg:items-stretch lg:gap-6 lg:p-6"
      >
        <div className="flex flex-1 flex-col items-start justify-center py-6 text-left lg:py-10">
          <h1 className="max-w-md text-balance text-2xl font-medium tracking-wide text-foreground lg:text-3xl">
            Ship interfaces that feel finished
          </h1>
          <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground">
            Ready made blocks you can restyle with your tokens.
          </p>
          <Button type="button" size="sm" className="mt-6">
            Get started
          </Button>
        </div>
        <div className="relative min-h-[16rem] w-full flex-1 overflow-hidden rounded-md lg:min-h-0">
          <img
            src="/assets/brand/demos/card-cover.png"
            alt=""
            className="absolute inset-0 size-full object-cover"
          />
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-04
```

### Usage

```tsx
import { Hero04 } from "@/components/blocks/hero/hero-04"
```

```tsx
<Hero04 />
```

## Logo cloud

Centered copy surrounded by floating logos. On narrow widths, the logos become two marquees.

```tsx
"use client";

import { useEffect, useRef, type CSSProperties } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

type Hero05Props = {
  className?: string;
};

const LEFT_LOGOS = [
  { src: "/assets/icons/logos/slack.svg", x: "6%", y: "14%", s: "3.4rem" },
  { src: "/assets/icons/logos/notion.svg", x: "15%", y: "52%", s: "2.9rem" },
  { src: "/assets/icons/logos/linear.svg", x: "4%", y: "74%", s: "3.1rem" },
  { src: "/assets/icons/logos/github.svg", x: "24%", y: "22%", s: "2.7rem" },
  { src: "/assets/icons/logos/figma.svg", x: "21%", y: "78%", s: "2.9rem" },
  { src: "/assets/icons/logos/stripe.svg", x: "11%", y: "33%", s: "2.6rem" },
  { src: "/assets/icons/logos/intercom.svg", x: "26%", y: "58%", s: "2.5rem" },
] as const;

const RIGHT_LOGOS = [
  { src: "/assets/icons/logos/hubspot.svg", x: "88%", y: "16%", s: "3.3rem" },
  { src: "/assets/icons/logos/jira.svg", x: "79%", y: "48%", s: "2.8rem" },
  { src: "/assets/icons/logos/zendesk.svg", x: "91%", y: "70%", s: "3rem" },
  { src: "/assets/icons/logos/asana.svg", x: "72%", y: "20%", s: "2.6rem" },
  { src: "/assets/icons/logos/dropbox.svg", x: "72%", y: "76%", s: "2.9rem" },
  { src: "/assets/icons/logos/snowflake.svg", x: "84%", y: "34%", s: "2.5rem" },
  { src: "/assets/icons/logos/airtable.svg", x: "74%", y: "58%", s: "2.5rem" },
] as const;

const MARQUEE_A = [
  "slack",
  "notion",
  "linear",
  "github",
  "figma",
  "stripe",
  "intercom",
  "airtable",
  "hubspot",
  "jira",
  "zendesk",
  "asana",
  "dropbox",
  "snowflake",
] as const;

const MARQUEE_B = [
  "hubspot",
  "jira",
  "zendesk",
  "asana",
  "dropbox",
  "snowflake",
  "slack",
  "notion",
  "linear",
  "github",
  "figma",
  "stripe",
  "intercom",
  "airtable",
] as const;

function LogoTile({
  src,
  className,
  style,
}: {
  src: string;
  className?: string;
  style?: CSSProperties;
}) {
  return (
    <span
      data-slot="hero-logo-tile"
      className={cn(
        "flex items-center justify-center rounded-xl bg-card shadow-sm",
        className,
      )}
      style={style}
    >
      <img
        src={src}
        alt=""
        className="size-[52%] object-contain brightness-0 dark:invert"
      />
    </span>
  );
}

export function Hero05({ className }: Hero05Props) {
  const stageRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const stage = stageRef.current;
    if (!stage) return;
    if (!window.matchMedia("(hover: hover) and (pointer: fine)").matches) {
      return;
    }

    const tiles = Array.from(
      stage.querySelectorAll<HTMLElement>("[data-slot=hero-logo-tile]"),
    ).filter((tile) => tile.offsetParent !== null);

    const depths = tiles.map((_, i) => 0.008 + (i % 5) * 0.005);
    let targetX = 0;
    let targetY = 0;
    let currentX = 0;
    let currentY = 0;
    let raf: number | null = null;

    const tick = () => {
      currentX += (targetX - currentX) * 0.08;
      currentY += (targetY - currentY) * 0.08;

      tiles.forEach((tile, i) => {
        const d = depths[i];
        const dir = i % 2 === 0 ? 1 : -1;
        tile.style.transform = `translate(${currentX * d * dir}px, ${currentY * d * dir}px)`;
      });

      if (
        Math.abs(targetX - currentX) > 0.1 ||
        Math.abs(targetY - currentY) > 0.1
      ) {
        raf = requestAnimationFrame(tick);
      } else {
        raf = null;
      }
    };

    const schedule = () => {
      if (!raf) raf = requestAnimationFrame(tick);
    };

    const onMove = (event: MouseEvent) => {
      const rect = stage.getBoundingClientRect();
      targetX = event.clientX - (rect.left + rect.width / 2);
      targetY = event.clientY - (rect.top + rect.height / 2);
      schedule();
    };

    const onLeave = () => {
      targetX = 0;
      targetY = 0;
      schedule();
    };

    stage.addEventListener("mousemove", onMove);
    stage.addEventListener("mouseleave", onLeave);

    return () => {
      stage.removeEventListener("mousemove", onMove);
      stage.removeEventListener("mouseleave", onLeave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);

  return (
    <section
      className={cn(
        "@container flex w-full items-center justify-center overflow-hidden bg-background",
        className,
      )}
    >
      <style>{`
        @keyframes hero-05-scroll {
          from { transform: translateX(0); }
          to { transform: translateX(-33.333%); }
        }
      `}</style>
      <div
        ref={stageRef}
        className="relative flex h-full min-h-[28rem] w-full flex-col items-center justify-center gap-10 px-0 py-16 @[40rem]:min-h-[34rem] @[40rem]:gap-0 @[40rem]:px-6"
      >
        <div
          aria-hidden="true"
          className="pointer-events-none absolute inset-0 hidden @[40rem]:block"
        >
          {LEFT_LOGOS.map((logo) => (
            <LogoTile
              key={`${logo.src}-${logo.x}`}
              src={logo.src}
              className="absolute will-change-transform"
              style={{
                top: logo.y,
                left: logo.x,
                width: logo.s,
                height: logo.s,
              }}
            />
          ))}
          {RIGHT_LOGOS.map((logo) => (
            <LogoTile
              key={`${logo.src}-${logo.x}`}
              src={logo.src}
              className="absolute will-change-transform"
              style={{
                top: logo.y,
                left: logo.x,
                width: logo.s,
                height: logo.s,
              }}
            />
          ))}
        </div>

        <div className="relative z-10 flex max-w-md flex-col items-center px-6 text-center">
          <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[40rem]:text-3xl">
            Plays nice with your whole stack
          </h1>
          <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground">
            Drop in blocks that already speak the tools your team uses every day.
          </p>
          <Button type="button" size="sm" className="mt-6">
            Get started
          </Button>
        </div>

        <div
          aria-hidden="true"
          className="flex w-full flex-col gap-3 overflow-hidden @[40rem]:hidden"
        >
          <div className="flex w-max" style={{ animation: "hero-05-scroll 32s linear infinite" }}>
            {[...MARQUEE_A, ...MARQUEE_A, ...MARQUEE_A].map((name, i) => (
              <LogoTile
                key={`a-${name}-${i}`}
                src={`/assets/icons/logos/${name}.svg`}
                className="mr-3 size-11 shrink-0"
              />
            ))}
          </div>
          <div
            className="flex w-max"
            style={{
              animation: "hero-05-scroll 32s linear infinite reverse",
            }}
          >
            {[...MARQUEE_B, ...MARQUEE_B, ...MARQUEE_B].map((name, i) => (
              <LogoTile
                key={`b-${name}-${i}`}
                src={`/assets/icons/logos/${name}.svg`}
                className="mr-3 size-11 shrink-0"
              />
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-05
```

### Usage

```tsx
import { Hero05 } from "@/components/blocks/hero/hero-05"
```

```tsx
<Hero05 />
```

## Social proof

A centered statement, short supporting line, one action, and a little social proof through avatars and a rating.

```tsx
"use client";

import { Button } from "@/components/ui/button";
import {
  Avatar,
  AvatarFallback,
  AvatarGroup,
  AvatarImage,
} from "@/components/ui/avatar";
import { cn } from "@/lib/utils";

type Hero06Props = {
  className?: string;
};

const AVATARS = [
  { src: "/assets/brand/demos/avatars/avatar-1.png", alt: "User 1" },
  { src: "/assets/brand/demos/avatars/avatar-2.png", alt: "User 2" },
  { src: "/assets/brand/demos/avatars/avatar-3.png", alt: "User 3" },
  { src: "/assets/brand/demos/avatars/avatar-4.png", alt: "User 4" },
  { src: "/assets/brand/demos/avatars/avatar-5.png", alt: "User 5" },
] as const;

function StarIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="currentColor"
      aria-hidden="true"
      className={className}
    >
      <path d="M12 2.5l2.9 6.1 6.6.9-4.8 4.6 1.2 6.5L12 17.8 6.1 20.6l1.2-6.5L2.5 9.5l6.6-.9L12 2.5z" />
    </svg>
  );
}

export function Hero06({ className }: Hero06Props) {
  return (
    <section className={cn("@container w-full bg-background", className)}>
      <div className="mx-auto flex h-full min-h-[28rem] w-full max-w-2xl flex-col items-center justify-center px-6 py-16 text-center">
        <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[32rem]:text-3xl">
          Ship interfaces that feel finished
        </h1>
        <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground">
          Blocks you can drop in and restyle with your tokens.
        </p>
        <Button type="button" size="sm" className="mt-6">
          Get started
        </Button>

        <div className="mt-10 flex flex-col items-center gap-3 @[24rem]:flex-row @[24rem]:justify-center">
          <AvatarGroup overlap="sm">
            {AVATARS.map((avatar) => (
              <Avatar key={avatar.src} size="sm">
                <AvatarImage src={avatar.src} alt={avatar.alt} />
                <AvatarFallback>?</AvatarFallback>
              </Avatar>
            ))}
          </AvatarGroup>
          <div className="flex flex-col items-center gap-1 @[24rem]:items-start">
            <div className="flex items-center gap-1.5">
              <div className="flex items-center gap-0.5 text-amber-400" aria-hidden="true">
                {Array.from({ length: 5 }).map((_, i) => (
                  <StarIcon key={i} className="size-3.5" />
                ))}
              </div>
              <span className="text-xs tracking-wide text-foreground">4.9</span>
            </div>
            <p className="text-xs tracking-wide text-muted-foreground">
              from 200+ reviews
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}
```

### Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add hero-06
```

### Usage

```tsx
import { Hero06 } from "@/components/blocks/hero/hero-06"
```

```tsx
<Hero06 />
```
