# Hero Section
*URL: https://www.grettech.com/components/hero-section*

A personal profile hero with a photo, roles, short bio, contact details, skills and social links.

- Profile photo with a soft glow
- Roles, bio and contact details
- Skill badges and social links
- Responsive, centred on mobile

## Install with the shadcn CLI
```bash
npx shadcn@latest add https://www.grettech.com/api/registry/hero-section
```
With its demo: `npx shadcn@latest add https://www.grettech.com/api/registry/hero-section-demo`

**Dependencies:** npm: clsx, tailwind-merge, lucide-react, class-variance-authority

## Manual installation
**Step 1: Install dependencies**
Install the packages this component uses and the shadcn/ui components it builds on. It also needs the `cn` helper in `lib/utils.ts`, which `npx shadcn@latest init` adds.

```bash
npm install clsx tailwind-merge lucide-react class-variance-authority
```

**Step 2: Copy the source code**
Add these files to your project components directory.

File: `components/ui/custom-badge.tsx`
```tsx
import { cn } from "@/lib/utils"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react"
const badgeVariants = cva(
  "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
  {
    variants: {
      variant: {
        default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
        secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
        destructive: "border-transparent bg-destructive text-white shadow hover:bg-destructive/80",
        outline: "text-foreground",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
  return <div className={cn(badgeVariants({ variant }), className)} {...props} />
}
export { Badge, badgeVariants }
```

File: `components/ui/hero.tsx`
```tsx
"use client"

import * as React from "react"
import { Badge } from "@/components/ui/custom-badge"
import { ChevronDown, Mail, MapPin, Phone } from "lucide-react"
import { cn } from "@/lib/utils"

export interface HeroProps extends React.HTMLAttributes<HTMLElement> {
  personalData?: {
    name?: string
    email?: string
    phone?: string
    address?: string
    imageUrl?: string
  }
  professionalData?: {
    designations?: string[]
    about?: string
  }
  skills?: string[]
  socialLinks?: { url: string; platform: string }[]
  imageAs?: React.ElementType
}

// --- SUB-COMPONENTS ---

const HeroBackground = () => (
  <>
    {/* Background gradient for visual interest */}
    <div
      aria-hidden="true"
      className="pointer-events-none absolute inset-0 -z-10 bg-linear-to-br from-primary/10 via-transparent to-transparent dark:from-primary/20"
    />

    {/* Abstract pattern overlay */}
    <div
      aria-hidden="true"
      className="pointer-events-none absolute inset-0 -z-10 mask-[radial-gradient(ellipse_at_center,black,transparent_75%)] text-foreground opacity-[0.06]"
    >
      <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <pattern
            id="heroGrid"
            width="40"
            height="40"
            patternUnits="userSpaceOnUse"
          >
            <path
              d="M 40 0 L 0 0 0 40"
              fill="none"
              stroke="currentColor"
              strokeWidth="1"
            />
          </pattern>
        </defs>
        <rect width="100%" height="100%" fill="url(#heroGrid)" />
      </svg>
    </div>
  </>
)

const HeroProfileImage = ({
  imageUrl,
  name,
  ImageTag,
}: {
  imageUrl?: string
  name?: string
  ImageTag: React.ElementType
}) => {
  if (!imageUrl) return null

  return (
    <div className="group relative z-10 shrink-0">
      <div
        aria-hidden="true"
        className="pointer-events-none absolute -inset-1.5 rounded-full bg-linear-to-br from-primary/60 to-primary/10 opacity-50 blur-lg transition duration-700 group-hover:opacity-90"
      />
      <div className="relative transition duration-500 group-hover:scale-[1.03]">
        <ImageTag
          src={imageUrl}
          alt={name || "Profile"}
          width={192}
          height={192}
          className="size-24 rounded-full border-4 border-background object-cover shadow-xl sm:size-32 md:size-44"
        />
      </div>
    </div>
  )
}

const HeroScrollIndicator = () => (
  <div
    aria-hidden="true"
    className="mt-auto flex flex-col items-center pb-6 opacity-60 sm:pb-8"
  >
    <span className="mb-2 text-[10px] font-semibold tracking-widest text-muted-foreground uppercase">
      Scroll Down
    </span>
    <div className="rounded-full border border-border bg-background/50 p-1.5 shadow-sm backdrop-blur-sm motion-safe:animate-bounce sm:p-2">
      <ChevronDown className="h-3 w-3 text-primary sm:h-4 sm:w-4" />
    </div>
  </div>
)

// --- MAIN COMPONENT ---

export const Hero = React.forwardRef<HTMLElement, HeroProps>(
  (
    {
      personalData = {
        name: "John Doe",
        email: "john@example.com",
        phone: "+1 234 567 890",
        address: "San Francisco, CA",
        imageUrl:
          "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop&q=80",
      },
      professionalData = {
        designations: ["Software Engineer", "UI/UX Designer"],
        about:
          "Passionate software developer specializing in creating exceptional digital experiences. With expertise in modern web technologies and a focus on clean, maintainable code.",
      },
      skills = ["React", "Next.js", "TypeScript", "Tailwind CSS"],
      socialLinks = [],
      imageAs: ImageTag = "img",
      className,
      ...props
    },
    ref
  ) => {
    return (
      <section
        ref={ref}
        className={cn(
          "relative isolate flex min-h-[80vh] flex-col overflow-hidden rounded-3xl border border-border/60 bg-card/40 shadow-xl shadow-primary/5 backdrop-blur-xl lg:min-h-[85vh]",
          className
        )}
        {...props}
      >
        <HeroBackground />

        <div className="flex flex-1 flex-col justify-center p-4 sm:p-6 md:p-10">
          <div className="mx-auto flex w-full max-w-5xl flex-col items-center gap-5 sm:gap-8 md:flex-row md:gap-12">
            <HeroProfileImage
              imageUrl={personalData?.imageUrl}
              name={personalData?.name}
              ImageTag={ImageTag}
            />

            <div className="flex-1 space-y-3 text-center sm:space-y-5 md:text-left">
              {/* Name and title */}
              <div className="space-y-1">
                <p className="mb-1 text-xs font-semibold tracking-widest text-primary uppercase sm:text-sm md:text-base">
                  Hello, I&apos;m
                </p>
                <h1 className="text-3xl font-bold tracking-tight text-balance text-foreground sm:text-4xl md:text-6xl">
                  {personalData?.name || "John Doe"}
                </h1>
                <p className="text-lg font-medium text-muted-foreground sm:text-xl md:text-2xl">
                  {professionalData?.designations?.join(" • ") ||
                    "Software Engineer • Designer"}
                </p>
              </div>

              {/* Short bio */}
              <p className="max-w-2xl text-sm leading-relaxed text-pretty text-muted-foreground sm:text-base md:text-lg">
                {professionalData?.about ||
                  "Passionate software developer specializing in creating exceptional digital experiences. With expertise in modern web technologies and a focus on clean, maintainable code."}
              </p>

              {/* Contact info */}
              <div className="flex flex-wrap justify-center gap-3 text-xs font-medium sm:text-sm md:justify-start md:gap-6 md:text-base">
                {personalData?.email && (
                  <div className="group flex items-center gap-1.5 sm:gap-2">
                    <div className="rounded-full bg-primary/10 p-1.5 shadow-sm transition-all duration-300 group-hover:bg-primary group-hover:text-primary-foreground sm:p-2">
                      <Mail className="h-3 w-3 text-primary group-hover:text-primary-foreground sm:h-4 sm:w-4" />
                    </div>
                    <a
                      href={`mailto:${personalData.email}`}
                      className="max-w-[150px] truncate decoration-primary/30 underline-offset-4 transition-colors hover:text-primary hover:underline sm:max-w-none"
                    >
                      {personalData.email}
                    </a>
                  </div>
                )}
                {personalData?.phone && (
                  <div className="group flex items-center gap-1.5 sm:gap-2">
                    <div className="rounded-full bg-primary/10 p-1.5 shadow-sm transition-all duration-300 group-hover:bg-primary group-hover:text-primary-foreground sm:p-2">
                      <Phone className="h-3 w-3 text-primary group-hover:text-primary-foreground sm:h-4 sm:w-4" />
                    </div>
                    <a
                      href={`tel:${personalData.phone}`}
                      className="decoration-primary/30 underline-offset-4 transition-colors hover:text-primary hover:underline"
                    >
                      {personalData.phone}
                    </a>
                  </div>
                )}
                {personalData?.address && (
                  <div className="group flex items-center gap-1.5 sm:gap-2">
                    <div className="rounded-full bg-primary/10 p-1.5 shadow-sm transition-all duration-300 group-hover:bg-primary group-hover:text-primary-foreground sm:p-2">
                      <MapPin className="h-3 w-3 text-primary group-hover:text-primary-foreground sm:h-4 sm:w-4" />
                    </div>
                    <span className="max-w-[150px] truncate text-muted-foreground transition-colors group-hover:text-foreground sm:max-w-none">
                      {personalData.address}
                    </span>
                  </div>
                )}
              </div>

              {/* Skills badges */}
              {skills && skills.length > 0 && (
                <div className="flex flex-wrap justify-center gap-1.5 pt-1 sm:gap-2 md:justify-start">
                  {skills.map((skill, i) => (
                    <Badge
                      key={`${skill}-${i}`}
                      variant="secondary"
                      className="cursor-default rounded-full border-border/60 bg-background/60 px-2.5 py-0.5 text-[10px] font-medium backdrop-blur-sm sm:text-xs md:text-sm"
                    >
                      {skill}
                    </Badge>
                  ))}
                </div>
              )}

              {/* Social Links */}
              {socialLinks && socialLinks.length > 0 && (
                <div className="flex flex-wrap justify-center gap-4 pt-2 sm:gap-6 md:justify-start">
                  {socialLinks.map((link, i) => (
                    <a
                      key={`${link.url}-${i}`}
                      href={link.url}
                      target="_blank"
                      rel="noopener noreferrer"
                      className="group flex items-center gap-1.5 text-xs font-medium text-muted-foreground underline-offset-4 transition-colors hover:text-primary hover:underline sm:text-sm"
                    >
                      <span className="h-1.5 w-1.5 rounded-full bg-primary/40 transition-colors group-hover:bg-primary"></span>
                      {link.platform}
                    </a>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>

        <HeroScrollIndicator />
      </section>
    )
  }
)
Hero.displayName = "Hero"

```

**Step 3: Framework Adjustments (Optional)**
If you are manually copying this code into a Vite or Remix project (without using the CLI), make sure to remove the `"use client";` directive from the very top of the file.

## Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| `personalData` | `{ name?: string; email?: string; phone?: string; address?: string; imageUrl?: string }` | `{ name: "John Doe", email: "john@example.com", phone: "+1 234 567 890", address: "San Francisco, CA", imageUrl: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop&q=80" }` | No | Personal details. Passing this object replaces the whole sample profile, and only the fields you set are shown (name falls back to "John Doe"). |
| `personalData.name` | `string` | - | No | Name in the main heading. Falls back to "John Doe". |
| `personalData.email` | `string` | - | No | Shown as a mailto: link. |
| `personalData.phone` | `string` | - | No | Shown as a tel: link. |
| `personalData.address` | `string` | - | No | Shown as plain text. |
| `personalData.imageUrl` | `string` | `"https://images.unsplash.com/photo-1507003211169-…"` | No | Profile picture URL. Hidden when empty. |
| `professionalData` | `{ designations?: string[]; about?: string }` | `{ designations: ["Software Engineer", "UI/UX Designer"], about: "Passionate software developer specializing in creating exceptional digital experiences. With expertise in modern web technologies and a focus on clean, maintainable code." }` | No | Roles and bio. Passing this object replaces the sample. |
| `professionalData.designations` | `string[]` | - | No | Roles, joined with " • ". Falls back to "Software Engineer • Designer". |
| `professionalData.about` | `string` | - | No | Short bio under the roles. Falls back to a sample bio. |
| `skills` | `string[]` | `["React", "Next.js", "TypeScript", "Tailwind CSS"]` | No | Shown as badges. Pass an empty array to hide them. |
| `socialLinks` | `{ url: string; platform: string }[]` | `[]` | No | Links shown under the skills. Each opens in a new tab. |
| `socialLinks.url` | `string` | - | Yes | Profile URL. |
| `socialLinks.platform` | `string` | - | Yes | Link text, e.g. "GitHub". |
| `imageAs` | `React.ElementType` | `"img"` | No | Component used for the photo, e.g. next/image's Image. It receives src, alt, width (192), height (192) and className. |
| `className` | `string` | - | No | Extra classes for the root <section>. |
| `...props` | `React.HTMLAttributes<HTMLElement>` | - | No | Any other section attributes (id, style, etc.). |

## Usage
```tsx
"use client"
import { Hero } from "@/components/ui/hero"

export default function HeroDemo() {
  return (
    <Hero
      personalData={{
        name: "Jane Smith",
        email: "jane@example.com",
        phone: "+1 987 654 321",
        address: "New York, NY",
        imageUrl:
          "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop&q=80",
      }}
      professionalData={{
        designations: ["Product Designer", "Frontend Engineer"],
        about: "Building beautiful and accessible web applications.",
      }}
      skills={["Figma", "React", "Next.js", "Tailwind"]}
      socialLinks={[
        { platform: "GitHub", url: "https://github.com" },
        { platform: "Twitter", url: "https://twitter.com" },
        { platform: "LinkedIn", url: "https://linkedin.com" },
      ]}
    />
  )
}

```
