---
title: LayerHeader
description: The header of a Layer — a centered heading between up to two round buttons
---

```tsx
'use client';

import { LayerHeader } from '@/ui/LayerHeader';

export function LayerHeaderDemo() {
  return (
    <div className="border-lightGrey w-full border">
      <LayerHeader
        hLevel={2}
        leftButton={{ label: 'Back', icon: 'ArrowDefaultLeft', onClick: () => {} }}
        rightButton={{ label: 'Close', icon: 'CrossDefault', onClick: () => {} }}
      >
        Search filters
      </LayerHeader>
    </div>
  );
}
```

## Installation

Using the CLI:

```bash
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/layer-header.json
```

Or manually, copy the following code into `/ui/LayerHeader.tsx`:

```tsx
import clsx from 'clsx';
import type { ComponentProps, FunctionComponent, MouseEventHandler } from 'react';

import { Button } from './buttons/Button';
import type { ButtonProps as Btn } from './buttons/Button.type';
import { Heading, type HeadingProps } from './heading/Heading';

export interface LayerHeaderButtonProps
  extends Omit<ComponentProps<'button'>, 'color' | 'onClick'>,
    Omit<Btn, 'variant' | 'theme'> {
  /**
   * Icon displayed in the round button
   */
  icon: Exclude<Btn['icon'], undefined>;
  /**
   * Accessible name of the button (rendered as `aria-label`)
   */
  label: string;
  onClick: MouseEventHandler<HTMLButtonElement>;
}

export interface LayerHeaderProps extends ComponentProps<'header'> {
  /**
   * Overrides the level announced to assistive technologies, without changing the tag
   */
  hAriaLevel?: number;
  /**
   * Level of the heading tag (h1 to h6)
   */
  hLevel?: HeadingProps['level'];
  /**
   * Round button pinned to the inline start of the header (e.g. a "back" arrow)
   */
  leftButton?: LayerHeaderButtonProps;
  /**
   * Round button pinned to the inline end of the header (e.g. a "close" cross)
   */
  rightButton?: LayerHeaderButtonProps;
}

export const LayerHeader: FunctionComponent<LayerHeaderProps> = ({
  children,
  className,
  hAriaLevel,
  hLevel = 1,
  id,
  leftButton,
  rightButton,
  ...attrs
}) => {
  const { label: leftLabel, ...leftBtn } = leftButton || {};
  const { label: rightLabel, ...rightBtn } = rightButton || {};

  return (
    <header
      {...attrs}
      className={clsx(
        'relative flex min-h-64 items-center justify-between gap-8 overscroll-contain bg-white p-8 lg:px-20',
        className,
      )}
    >
      {leftButton && (
        <Button
          aria-describedby={id}
          {...leftBtn}
          aria-label={leftLabel}
          className="absolute start-0 m-8 lg:mx-20"
          color="black"
          size="medium"
          theme="outline"
          variant="circle"
        />
      )}
      <Heading
        aria-level={hAriaLevel}
        className="text-b3 lg:text-b2 flex-1 px-56 text-center font-semibold"
        id={id}
        level={hLevel}
      >
        {children}
      </Heading>
      {rightButton && (
        <Button
          aria-describedby={id}
          {...rightBtn}
          aria-label={rightLabel}
          className="absolute end-0 m-8 lg:mx-20"
          color="black"
          size="medium"
          theme="outline"
          variant="circle"
        />
      )}
    </header>
  );
};
```

## Usage

`<Layer />` renders its own `LayerHeader`. Use it directly to build a layer-like panel, like a sidebar or a bottom sheet.

```tsx showLineNumbers
import { LayerHeader } from '@/ui/LayerHeader';

export function Component() {
  return (
    <LayerHeader
      hLevel={2}
      leftButton={{ label: 'Back', icon: 'ArrowDefaultLeft', onClick: goBack }}
      rightButton={{ label: 'Close', icon: 'CrossDefault', onClick: close }}
    >
      Search filters
    </LayerHeader>
  );
}
```

## API Reference

### LayerHeader

Extra props are spread on the `<header>` element.

| Prop          | Type                         | Default     | Description                                                                         |
| ------------- | ---------------------------- | ----------- | ----------------------------------------------------------------------------------- |
| `children`    | `ReactNode`                  | `undefined` | Content of the heading.                                                             |
| `hLevel`      | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `1`         | Level of the heading tag.                                                           |
| `hAriaLevel`  | `number`                     | `undefined` | Level announced to assistive technologies (`aria-level`), without changing the tag. |
| `id`          | `string`                     | `undefined` | Put on the heading, and used as `aria-describedby` of the buttons.                  |
| `leftButton`  | `LayerHeaderButtonProps`     | `undefined` | Round button pinned to the inline start.                                            |
| `rightButton` | `LayerHeaderButtonProps`     | `undefined` | Round button pinned to the inline end.                                              |
| `className`   | `string`                     | `undefined` | Classes merged on the `<header>`.                                                   |

### LayerHeaderButtonProps

Any `<button>` attribute and `Button` prop is forwarded, except `variant` and `theme`.

| Prop      | Type                                   | Default    | Description                                   |
| --------- | -------------------------------------- | ---------- | --------------------------------------------- |
| `icon`    | `IconicNames`                          | (required) | Icon of the round button.                     |
| `label`   | `string`                               | (required) | Accessible name of the button (`aria-label`). |
| `onClick` | `MouseEventHandler<HTMLButtonElement>` | (required) | Click handler.                                |

## Notes

- **Centered heading**: the buttons are absolutely positioned and the heading keeps a 56px inline padding, so it stays centered whether there are zero, one or two buttons, and long headings wrap instead of running under them.
- **RTL**: the buttons use logical positions (`start` / `end`), so they swap sides in right-to-left layouts.
- **Accessible description**: give the header an `id` to have a screen reader announce the heading after a button name, like "Close, Search filters".
