Heading
SM · 950B gzip budgetHeadings that know their own level, so a reusable component never breaks the page outline.
- Category
- Accessibility & preferences
- Budget
- sm, single-purpose control
- External runtime
- React / React DOM only
axe fixtureSSR rendersource scanbundle budget
Preview
Live fixturePage title
Section inside it
Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.
Install
npx shadcn@latest add https://gear5-ui.vercel.app/r/heading-level.jsonCopies the source into your project. Pulls in 1 shared registry item: cn.
New here? Set up Tailwind and import aliases first →Source & props
Edit on GitHub ↗Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.
"use client";
import { createContext, useContext, useMemo } from "react";
import { cn } from "../lib/cn";
const HeadingLevelContext = createContext(1);
export interface HeadingSectionProps {
children: React.ReactNode;
}
/**
* Increments the heading level for everything inside it.
*
* Wrap a region whose headings sit one level deeper than the surrounding page.
*/
export function HeadingSection({ children }: HeadingSectionProps) {
const level = useContext(HeadingLevelContext);
const next = useMemo(() => Math.min(level + 1, 6), [level]);
return <HeadingLevelContext.Provider value={next}>{children}</HeadingLevelContext.Provider>;
}
export interface HeadingProps extends React.HTMLAttributes<HTMLHeadingElement> {
children: React.ReactNode;
className?: string;
}
/**
* A heading that knows its own level.
*
* A reusable card cannot know whether it is being rendered under an `h1` or an
* `h3`, so it guesses — and the result is a page whose heading outline skips
* levels or restarts. That outline is the primary way screen reader users
* navigate a page: they pull up a list of headings and jump. A broken outline
* is a broken table of contents for exactly the people who rely on it most.
*
* `Heading` renders whatever level its position in the tree implies, and
* `HeadingSection` deepens that level for its subtree — so a component is
* correct wherever it is placed, rather than correct where it was first
* written.
*/
export function Heading({ children, className, ...props }: HeadingProps) {
const level = useContext(HeadingLevelContext);
const Tag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
return (
<Tag {...props} className={cn("text-start font-semibold", className)}>
{children}
</Tag>
);
}
/** The heading level the current subtree would render at. */
export function useHeadingLevel(): number {
return useContext(HeadingLevelContext);
}
What CI checks
Quality contract- Bundled, minified and gzipped against its tier budget
- Audited by axe in the state previewed above
- Rendered through react-dom/server with no browser globals
- Scanned for network calls, dangerous sinks, and unguarded animation