Skip to content
Facade UI
Colour mode
GitHub (opens in a new tab)
uiatom

Heading

Renders h1-h6 from a numeric level so visual size and outline level stay independent.

Preview

Open full width (opens in a new tab)
Preview width

Installation

Package manager
pnpm dlx shadcn@latest add https://facadeui.dev/r/heading.json

Pulls in types. The CLI installs them for you.

Usage

The exact source of the preview above.

demos/heading.tsx
import { Heading } from "@registry/ui/heading"

export function Demo() {
  return (
    <div className="flex flex-col gap-4">
      {/* Same visual size, four different outline levels. */}
      <Heading level={2} className="text-display-sm font-semibold">
        Rendered as h2
      </Heading>
      <Heading level={3} className="text-display-sm font-semibold">
        Rendered as h3
      </Heading>
      <Heading level={4} className="text-display-sm font-semibold">
        Rendered as h4
      </Heading>
      <p className="text-muted-foreground text-sm">
        Visual size is a class. The outline level is the <code>level</code> prop. Changing
        one never changes the other.
      </p>
    </div>
  )
}

Source

What the CLI copies into your project, byte for byte.

components/ui/heading.tsx
/**
 * Heading — renders `<h1>`–`<h6>` from a numeric level.
 *
 * Written as an exhaustive switch over literal JSX rather than the obvious
 * `const Tag = \`h${level}\`; <Tag />`. The dynamic-tag form is what
 * `react-hooks/static-components` exists to catch, and suppressing that rule in
 * every section would blunt it everywhere else. This is a few lines longer and
 * needs no suppression at all.
 *
 * a11y: the whole point of the atom. Sections take a `headingLevel` prop and
 * pass it here, so a hero dropped inside an existing page can be `<h2>` while
 * the same component on its own landing page is `<h1>` — without the visual
 * size changing, which `className` controls independently.
 *
 * Dependencies: react, @/lib/types.
 */

import type { ComponentPropsWithoutRef } from "react"

import type { HeadingLevel } from "@/lib/types"

export interface HeadingProps extends ComponentPropsWithoutRef<"h2"> {
  /** 1–6. Reflects the document outline, never the visual size. */
  level: HeadingLevel
}

export function Heading({ level, children, ...props }: HeadingProps) {
  switch (level) {
    case 1:
      return <h1 {...props}>{children}</h1>
    case 2:
      return <h2 {...props}>{children}</h2>
    case 3:
      return <h3 {...props}>{children}</h3>
    case 4:
      return <h4 {...props}>{children}</h4>
    case 5:
      return <h5 {...props}>{children}</h5>
    case 6:
      return <h6 {...props}>{children}</h6>
  }
}

Props

HeadingProps

Extends ComponentPropsWithoutRef<"h2">.

Props for HeadingProps
PropTypeDefault
level*Required1–6. Reflects the document outline, never the visual size.HeadingLevel