# Complex Headers

Grid supports up to three header rows. Header cells can span multiple columns or rows using `colSpan` and `rowSpan` attributes, similar to HTML tables.

```tsx
import { createModel } from "cx/data";
import { Controller } from "cx/ui";
import { Grid } from "cx/widgets";

interface Person {
  id: number;
  fullName: string;
  phone: string;
  email: string;
  city: string;
  country: string;
}

interface PageModel {
  records: Person[];
  $record: Person;
}

const m = createModel<PageModel>();

class PageController extends Controller {
  onInit() {
    this.store.set(m.records, [
      { id: 1, fullName: "Alice Johnson", phone: "555-1001", email: "alice@example.com", city: "New York", country: "USA" },
      { id: 2, fullName: "Bob Smith", phone: "555-1002", email: "bob@example.com", city: "Los Angeles", country: "USA" },
      { id: 3, fullName: "Carol White", phone: "555-1003", email: "carol@example.com", city: "Chicago", country: "Canada" },
      { id: 4, fullName: "David Brown", phone: "555-1004", email: "david@example.com", city: "Houston", country: "Mexico" },
      { id: 5, fullName: "Eva Green", phone: "555-1005", email: "eva@example.com", city: "Phoenix", country: "USA" },
    ]);
  }
}

export default (
  <Grid
    controller={PageController}
    records={m.records}
    style="width: 100%"
    border
    vlines
    columns={[
      {
        header1: { text: "Name", rowSpan: 2 },
        field: "fullName",
        sortable: true,
      },
      {
        align: "center",
        header1: { text: "Contact", colSpan: 2 },
        header2: "Phone",
        field: "phone",
      },
      {
        header2: "Email",
        field: "email",
        sortable: true,
      },
      {
        header1: { text: "Address", colSpan: 2, align: "center" },
        header2: "City",
        field: "city",
        sortable: true,
      },
      {
        header2: "Country",
        field: "country",
        sortable: true,
      },
    ]}
  />
);

```

## Multiple Header Rows

Use `header1`, `header2`, and `header3` to define content for each header row:

```tsx
columns={[
  {
    header1: { text: "Name", rowSpan: 2 },
    field: "fullName",
  },
  {
    header1: { text: "Contact", colSpan: 2 },
    header2: "Phone",
    field: "phone",
  },
  {
    header2: "Email",
    field: "email",
  },
]}
```

## Header Configuration

Each header can be a simple string or an object with these properties:

| Property | Type | Description |
| -------- | ---- | ----------- |
| `text` | `string` | Header text content. |
| `colSpan` | `number` | Number of columns to span. |
| `rowSpan` | `number` | Number of rows to span. |
| `align` | `string` | Text alignment: `left`, `center`, or `right`. |
| `allowSorting` | `boolean` | Set to `false` to disable sorting when clicking this header cell. |
| `style` | `object` | Custom CSS styles for the header cell. |
| `className` | `string` | Custom CSS class for the header cell. |
| `items` | `any` | Custom content to render inside the header cell. |

## Tips

- When using `colSpan`, subsequent columns should not define that header row (e.g., if `header1` spans 2 columns, the next column should only define `header2`).
- Use `rowSpan: 2` on a header to make it span both `header1` and `header2` rows.
- Add `vlines` prop to the Grid for vertical lines between columns, which helps visualize complex headers.

See also: [Grid](/docs/tables/grid), [Header Menu](/docs/tables/header-menu)