> Markdown version of [Styling](https://vaadin.com/docs/next/components/grid/styling). Section index: [llms.txt](https://vaadin.com/docs/next/components/llms.txt)

# Grid Styling

The look and feel of the Grid component can be customized in several ways: a set of built-in theme variants, customizable style properties and CSS selectors, and several different APIs for applying part names to header, footer and body cells.

## <a id="style-variants"></a>Style Variants

Grid supports the following style variants:

| Variant             | Description                                                            | Supported by |
| ------------------- | ---------------------------------------------------------------------- | ------------ |
| `wrap-cell-content` | Makes cell content wrap instead of being truncated                     | Aura, Lumo   |
| `no-border`         | Removes the outer border of the grid                                   | Aura, Lumo   |
| `no-row-borders`    | Removes the horizontal borders between rows                            | Aura, Lumo   |
| `column-borders`    | Adds vertical borders between columns                                  | Aura, Lumo   |
| `row-stripes`       | Adds a background color to every other row                             | Aura, Lumo   |
| `compact`           | Reduces the row and header heights and spacing to make the grid denser | Lumo         |

### <a id="wrap-cell-content"></a>Wrap Cell Content

Cell content that overflows is normally clipped or truncated. However, the `wrap-cell-content` variant makes the content wrap instead.

Notice in the example here that the text in the *Address* cells is wrapped. If you click on the gray icon at the top right corner of the example, it opens the table in a separate browser tab. When you do that, can see the addresses on one line. You can also resize that window to see how it wraps and unwraps the text.

**Lit** — `grid-wrap-cell-content.ts`

```typescript
@customElement('grid-wrap-cell-content')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  @state()
  private items: Person[] = [];

  protected override async firstUpdated() {
    const { people } = await getPeople();
    this.items = people;
  }

  protected override render() {
    return html`
      <vaadin-grid .items="${this.items}" theme="wrap-cell-content">
        <vaadin-grid-column
          header="Image"
          flex-grow="0"
          auto-width
          ${columnBodyRenderer(this.avatarRenderer, [])}
        ></vaadin-grid-column>
        <vaadin-grid-column path="firstName"></vaadin-grid-column>
        <vaadin-grid-column path="lastName"></vaadin-grid-column>
        <vaadin-grid-column
          header="Address"
          ${columnBodyRenderer(this.addressRenderer, [])}
        ></vaadin-grid-column>
      </vaadin-grid>
    `;
  }

  private avatarRenderer: GridColumnBodyLitRenderer<Person> = (person) => html`
    <vaadin-avatar
      img="${person.pictureUrl}"
      name="${person.firstName} ${person.lastName}"
    ></vaadin-avatar>
  `;

  private addressRenderer: GridColumnBodyLitRenderer<Person> = ({ address }) => html`
    <span>${address.street} ${address.city} ${address.zip} ${address.state}</span>
  `;
}
```

**Flow** — `GridWrapCellContent.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createAvatarRenderer()).setHeader("Image")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(GridWrapCellContent::formatAddress).setHeader("Address");

grid.addThemeVariants(GridVariant.WRAP_CELL_CONTENT);
```

**React** — `grid-wrap-cell-content.tsx`

```tsx
<Grid items={items.value} theme="wrap-cell-content">
  <GridColumn header="Image" flexGrow={0} autoWidth renderer={avatarRenderer} />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn header="Address" renderer={addressRenderer} />
</Grid>
```

[Tooltips](https://vaadin.com/docs/next/components/grid.md#tooltips) can also be used to display content that doesn’t fit into the cell.

Wrapped cell content is vertically centered by default. To align it to the top of the row instead, set the `align-items` property on the body cell part:

```css
vaadin-grid::part(body-cell) {
  align-items: start;
}
```

### <a id="no-border"></a>No Border

The `no-border` theme variant removes the outer border of the grid. Compare the example here with the previous one. Notice that the outer border, surrounding all of the rows is missing here.

**Lit** — `grid-no-border.ts`

```typescript
<vaadin-grid .items="${this.items}" theme="no-border">
  <vaadin-grid-column
    header="Image"
    flex-grow="0"
    auto-width
    ${columnBodyRenderer(this.avatarRenderer, [])}
  ></vaadin-grid-column>
  <vaadin-grid-column path="firstName"></vaadin-grid-column>
  <vaadin-grid-column path="lastName"></vaadin-grid-column>
  <vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
```

**Flow** — `GridNoBorder.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createAvatarRenderer()).setHeader("Image")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

grid.addThemeVariants(GridVariant.NO_BORDER);
```

**React** — `grid-no-border.tsx`

```tsx
<Grid items={items.value} theme="no-border">
  <GridColumn header="Image" flexGrow={0} autoWidth renderer={avatarRenderer} />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

### <a id="no-row-border"></a>No Row Border

This theme variant removes the horizontal row borders. This is best suited for small datasets. Viewing larger datasets may be difficult unless paired with the `row-stripes` theme variant. You can see this in the example here. There’s no border between the rows. With so much space, notice how it’s a little difficult to be sure you’re reading the email address of a particular person — and not the email address of a different row. It would be worse with a wider table containing many columns of data.

**Lit** — `grid-no-row-border.ts`

```typescript
<vaadin-grid .items="${this.items}" theme="no-row-borders">
  <vaadin-grid-column
    header="Image"
    flex-grow="0"
    auto-width
    ${columnBodyRenderer(this.avatarRenderer, [])}
  ></vaadin-grid-column>
  <vaadin-grid-column path="firstName"></vaadin-grid-column>
  <vaadin-grid-column path="lastName"></vaadin-grid-column>
  <vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
```

**Flow** — `GridNoRowBorder.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createAvatarRenderer()).setHeader("Image")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

grid.addThemeVariants(GridVariant.NO_ROW_BORDERS);
```

**React** — `grid-no-row-border.tsx`

```tsx
<Grid items={items.value} theme="no-row-borders">
  <GridColumn header="Image" flexGrow={0} autoWidth renderer={avatarRenderer} />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

### <a id="column-borders"></a>Column Borders

You can add vertical borders between columns by using the `column-borders` theme variant. Datasets with a lot of cramped columns, or where content is truncated, can benefit from the extra separation that vertical borders bring. Compare the table here to previous ones. You can see that this one has a border between each column. While this can sometimes make reading the data easier, it can be aesthetically displeasing — look too rigid.

**Lit** — `grid-column-borders.ts`

```typescript
<vaadin-grid .items="${this.items}" theme="column-borders">
  <vaadin-grid-column
    header="Image"
    flex-grow="0"
    auto-width
    ${columnBodyRenderer(this.avatarRenderer, [])}
  ></vaadin-grid-column>
  <vaadin-grid-column path="firstName"></vaadin-grid-column>
  <vaadin-grid-column path="lastName"></vaadin-grid-column>
  <vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
```

**Flow** — `GridColumnBorders.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createAvatarRenderer()).setHeader("Image")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

grid.addThemeVariants(GridVariant.COLUMN_BORDERS);
```

**React** — `grid-column-borders.tsx`

```tsx
<Grid items={items.value} theme="column-borders">
  <GridColumn header="Image" flexGrow={0} autoWidth renderer={avatarRenderer} />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

### <a id="row-stripes"></a>Row Stripes

The `row-stripes` theme variant produces a background color for every other row. This can make scanning the rows of data easier. You can see in the example here that the odd rows have a light gray background, while the even ones have none or a white background. This is particularly useful with very wide tables, with many columns of data.

**Lit** — `grid-row-stripes.ts`

```typescript
<vaadin-grid .items="${this.items}" theme="row-stripes">
  <vaadin-grid-column
    header="Image"
    flex-grow="0"
    auto-width
    ${columnBodyRenderer(this.avatarRenderer, [])}
  ></vaadin-grid-column>
  <vaadin-grid-column path="firstName"></vaadin-grid-column>
  <vaadin-grid-column path="lastName"></vaadin-grid-column>
  <vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
```

**Flow** — `GridRowStripes.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createAvatarRenderer()).setHeader("Image")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

grid.addThemeVariants(GridVariant.ROW_STRIPES);
```

**React** — `grid-row-stripes.tsx`

```tsx
<Grid items={items.value} theme="row-stripes">
  <GridColumn header="Image" flexGrow={0} autoWidth renderer={avatarRenderer} />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

### <a id="compact-lumo-only"></a>Compact (Lumo-only)

The `compact` Lumo theme variant makes a grid denser by reducing the header and row heights, as well as the spacing between columns.

This is useful for displaying more information on-screen without having to scroll. It can also help improve scannability and comparability between rows. Notice that there are more rows displayed in the example here, compared to the number of rows visible in the same space in the earlier example.

**Lit** — `grid-compact.ts`

```typescript
<vaadin-grid .items="${this.items}" theme="compact">
  <vaadin-grid-column path="firstName"></vaadin-grid-column>
  <vaadin-grid-column path="lastName"></vaadin-grid-column>
  <vaadin-grid-column path="email"></vaadin-grid-column>
</vaadin-grid>
```

**Flow** — `GridCompact.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

grid.addThemeVariants(GridVariant.LUMO_COMPACT);
```

**React** — `grid-compact.tsx`

```tsx
<Grid items={items.value} theme="compact">
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

## <a id="style-properties"></a>Style Properties

The following style properties can be used in CSS stylesheets to customize the appearance of this component.

To apply values to these properties globally in your application UI, place them in a CSS block using the `html {…​}` selector. See [Component Style Properties](https://vaadin.com/docs/next/styling/styling-components.md#component-style-properties) for more information on style properties.

| Property                                      | Supported By |
| --------------------------------------------- | ------------ |
| `--vaadin-grid-background`                    | Aura         |
| `--vaadin-grid-border-width`                  | Aura         |
| `--vaadin-grid-border-color`                  | Aura         |
| `--vaadin-grid-border-radius`                 | Aura         |
| `--vaadin-grid-row-border-width`              | Aura         |
| `--vaadin-grid-column-border-width`           | Aura         |
| `--vaadin-grid-header-font-size`              | Aura         |
| `--vaadin-grid-header-font-weight`            | Aura         |
| `--vaadin-grid-header-text-color`             | Aura         |
| `--vaadin-grid-row-background-color`          | Aura         |
| `--vaadin-grid-row-selected-background-color` | Aura         |
| `--vaadin-grid-row-hover-background-color`    | Aura         |
| `--vaadin-grid-row-odd-background-color`      | Aura         |
| `--vaadin-grid-cell-background-color`         | Aura         |
| `--vaadin-grid-cell-background`               | Lumo         |
| `--vaadin-grid-cell-padding`                  | Aura, Lumo   |
| `--vaadin-grid-cell-text-overflow`            | Aura, Lumo   |
| `--vaadin-grid-column-resize-handle-color`    | Aura         |

## <a id="css-selectors"></a>CSS Selectors

The following CSS selectors can be used in stylesheets to target the various parts and states of the component. See the [Styling documentation](https://vaadin.com/docs/next/styling.md) for more details on how to style components.

> **Important: Not for Shadow DOM**
>
> These selectors should be used in `styles.css` or another stylesheet loaded with the `@StyleSheet` annotation or the `@import` CSS rule. **They do not work in the shadow DOM of Vaadin components**, such as in stylesheets in the `components` sub-folder or loaded with the `@CssImport` annotation’s `themeFor` property.

---

- Root element

  `vaadin-grid`

### <a id="rows"></a>Rows

- Row (any)

  `vaadin-grid::part(row)`

- First row

  `vaadin-grid::part(first-row)`

- Last row

  `vaadin-grid::part(last-row)`

- Even row

  `vaadin-grid::part(even-row)`

- Odd row

  `vaadin-grid::part(odd-row)`

- Selected row

  `vaadin-grid::part(selected-row)`

- Hovered row

  `vaadin-grid::part(row):hover`

Note: To set background colors based on row selectors, use the `--vaadin-grid-cell-background` style property.

### <a id="cells"></a>Cells

- Cell (any)

  `vaadin-grid::part(cell)`

- Header row cell

  `vaadin-grid::part(header-cell)`

- Body cell

  `vaadin-grid::part(body-cell)`

- Footer row cell

  `vaadin-grid::part(footer-cell)`

- Focused cell

  `vaadin-grid::part(focused-cell)`

- Cell content wrapper

  `vaadin-grid-cell-content`

- Cell in first column

  `vaadin-grid::part(first-column-cell)`

- Cell in last column

  `vaadin-grid::part(last-column-cell)`

- Cell in first row

  `vaadin-grid::part(first-row-cell)`

- Cell in last row

  `vaadin-grid::part(last-row-cell)`

- Cell in even row

  `vaadin-grid::part(even-row-cell)`

- Cell in odd row

  `vaadin-grid::part(odd-row-cell)`

- Cell in selected row

  `vaadin-grid::part(selected-row-cell)`

- Cell in first header row

  `vaadin-grid::part(first-header-row-cell)`

- Cell in last header row

  `vaadin-grid::part(last-header-row-cell)`

- Cell focus ring

  `vaadin-grid::part(cell)::before`

- Row focus ring

  `vaadin-grid::part(row)::before`

- Collapsed cell

  `vaadin-grid::part(collapsed-row-cell)`

- Expanded cell

  `vaadin-grid::part(expanded-row-cell)`

- Cell in a column frozen to the start

  `vaadin-grid::part(frozen-cell)`

- Cell in a column frozen to the end

  `vaadin-grid::part(frozen-to-end-cell)`

- Cell in the last column frozen to the start

  `vaadin-grid::part(last-frozen-cell)`

- Cell in the first column frozen to the end

  `vaadin-grid::part(first-frozen-to-end-cell)`

You can [style individual cells, rows, and columns](#part-name-generator) by applying custom part names to them. Cell padding can be configured using the `--vaadin-grid-cell-padding` style property.

### <a id="selection-checkboxes"></a>Selection Checkboxes

- Row selection checkbox

  `vaadin-grid > vaadin-checkbox`

- Select All checkbox

  `vaadin-grid > #selectAllCheckbox`

### <a id="sorters"></a>Sorters

- Element

  `vaadin-grid-sorter`

- Active sorter

  `vaadin-grid-sorter[direction]`

- Column header content

  `vaadin-grid-sorter::part(content)`

- Sort indicators

  `vaadin-grid-sorter::part(indicators)`

- Sort indicator icons

  `vaadin-grid-sorter::part(indicators)::before`

- Sort order indicator

  `vaadin-grid-sorter::part(order)`

### <a id="item-details"></a>Item Details

- Item details cell (spans entire row)

  `vaadin-grid::part(details-cell)`

- Cell in row with open details

  `vaadin-grid::part(details-opened-row-cell)`

- Row with open details

  `vaadin-grid::part(details-opened-row)`

### <a id="drag-drop"></a>Drag & Drop

The following terminology is used in the description of these part names:

- **Ghost**: the semi-transparent copy of the row that is dragged around by the pointer.

- **Drag-source**: the non-moving rows that are being dragged.

- **Drop-target**: a row or a gap between rows that the ghost can be dropped onto.

* The cells in the “ghost” row

  `vaadin-grid::part(dragstart-row-cell)`

* The cells in the drag source row

  `vaadin-grid::part(drag-source-row-cell)`

* The cells of a row when the drop target is between this row and the row before it (i.e., above this row)

  `vaadin-grid::part(dragover-above-row-cell)`

* The cells of a row when the drop target is between this row and the row after it (i.e., below this row)

  `vaadin-grid::part(dragover-below-row-cell)`

* The cells of a row when the drop target is the row itself (i.e., on top of this row)

  `vaadin-grid::part(dragover-on-top-row-cell)`

* The cells of a row that isn’t draggable

  `vaadin-grid::part(drag-disabled-row-cell)`

* The cells of a row that isn’t a drop target

  `vaadin-grid::part(drop-disabled-row-cell)`

* The dragged row (i.e., the “ghost”)

  `vaadin-grid::part(dragstart-row)`

* The row which is the drop target, and the target is between this row and the row before it (i.e., above this row)

  `vaadin-grid::part(dragover-above-row)`

* The row which is the drop target, and the target is between this row and the row after it (i.e., below this row)

  `vaadin-grid::part(dragover-below-row)`

* The row which is the drop target, and the target is the row itself (i.e., on top of this row)

  `vaadin-grid::part(dragover-on-top-row)`

* A row that isn’t draggable

  `vaadin-grid::part(drag-disabled-row)`

* A row that isn’t a drop target

  `vaadin-grid::part(drop-disabled-row)`

## <a id="part-name-generator"></a>Dynamically Styling Rows & Columns

Cells can be styled dynamically, based on application logic and the data in the grid, through custom part names. This can be used, for example, to highlight specific rows and apply custom styling to specific columns.

In the example below, bold font weight is applied to the **Rating** column with a `font-weight-bold` part name, and the rows are colored red and green, based on their rating, with `low-rating` and `high-rating` part names, respectively. The styling itself is applied in a stylesheet with `vaadin-grid::part()` selectors (e.g., `vaadin-grid::part(high-rating)`).

**Lit** — `grid-styling.ts`

```typescript
interface PersonWithRating extends Person {
  customerRating: number;
}

@customElement('grid-styling')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  @state()
  private items: PersonWithRating[] = [];

  private ratingFormatter = new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });

  protected override async firstUpdated() {
    const { people } = await getPeople();
    this.items = people.map((person) => ({ ...person, customerRating: Math.random() * 10 }));
  }

  protected override render() {
    return html`
      <vaadin-grid .items="${this.items}" .cellPartNameGenerator="${this.cellPartNameGenerator}">
        <vaadin-grid-column path="lastName"></vaadin-grid-column>
        <vaadin-grid-column path="profession"></vaadin-grid-column>
        <vaadin-grid-column
          header="Customer rating (0-10)"
          ${columnBodyRenderer(this.ratingRenderer, [])}
        ></vaadin-grid-column>
      </vaadin-grid>
    `;
  }

  private ratingRenderer: GridColumnBodyLitRenderer<PersonWithRating> = (person) => html`
    <span>${this.ratingFormatter.format(person.customerRating)}</span>
  `;

  private cellPartNameGenerator(column: GridColumn, model: GridItemModel<PersonWithRating>) {
    const { item } = model;
    let parts = '';
    // Make the customer rating column bold
    if (column.header?.startsWith('Customer rating')) {
      parts += ' font-weight-bold';
    }
    // Add high-rating part to customer ratings of 8 or higher
    if (item.customerRating >= 8.0) {
      parts += ' high-rating';
      // Add low-rating part to customer ratings of 4 or lower
    } else if (item.customerRating <= 4.0) {
      parts += ' low-rating';
    }
    return parts;
  }
}
```

**Flow** — `GridStyling.java`

```java
Grid<PersonWithRating> grid = new Grid<>(PersonWithRating.class, false);
grid.addColumn(PersonWithRating::getFirstName).setHeader("First name");
grid.addColumn(PersonWithRating::getLastName).setHeader("Last name");
grid.addColumn(PersonWithRating::getProfession).setHeader("Profession");
grid.addColumn(PersonWithRating::getFormattedRating)
        .setHeader("Customer rating (0-10)")
        .setPartNameGenerator(person -> "font-weight-bold");

grid.setPartNameGenerator(person -> {
    if (person.getRating() >= 8)
        return "high-rating";
    if (person.getRating() <= 4)
        return "low-rating";
    return null;
});
```

**React** — `grid-styling.tsx`

```typescript
interface PersonWithRating extends Person {
  customerRating: number;
}

const cellPartNameGenerator: GridCellPartNameGenerator<PersonWithRating> = (column, model) => {
  const { item } = model;
  let parts = '';
  // Make the customer rating column bold
  if (column.header?.startsWith('Customer rating')) {
    parts += ' font-weight-bold';
  }
  // Add high-rating part to customer ratings of 8 or higher
  if (item.customerRating >= 8.0) {
    parts += ' high-rating';
    // Add low-rating part to customer ratings of 4 or lower
  } else if (item.customerRating <= 4.0) {
    parts += ' low-rating';
  }
  return parts;
};

const ratingFormatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

const ratingRenderer = ({ item: person }: { item: PersonWithRating }) => (
  <span>{ratingFormatter.format(person.customerRating)}</span>
);

function Example() {
  const items = useSignal<PersonWithRating[]>([]);

  useEffect(() => {
    getPeople().then(({ people }) => {
      const peopleWithRating = people.map((person) => ({
        ...person,
        customerRating: Math.random() * 10,
      }));
      items.value = peopleWithRating;
    });
  }, []);

  return (
    <Grid items={items.value} cellPartNameGenerator={cellPartNameGenerator}>
      <GridColumn path="firstName" />
      <GridColumn path="lastName" />
      <GridColumn path="profession" />
      <GridColumn header="Customer rating (0-10)" renderer={ratingRenderer} />
    </Grid>
  );
}
```

`grid-styling.css`

```css
/* Add this to your global CSS, for example in: */
/* src/main/resources/META-INF/resources/styles.css */

vaadin-grid::part(high-rating) {
    background: rgba(122, 201, 122, 0.2);
}

vaadin-grid::part(low-rating) {
    background: rgba(255, 128, 114, 0.2);
}

vaadin-grid::part(font-weight-bold) {
    font-weight: bold;
}
```

## <a id="styling-header-footer-cells"></a>Styling Header & Footer Cells

Header and footer cells can be similarly styled through their own custom part names.

**Lit** — `grid-header-footer-styling.ts`

```typescript
interface PersonWithRating extends Person {
  customerRating: number;
}

@customElement('grid-header-footer-styling')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  @state()
  private items: PersonWithRating[] = [];

  private ratingFormatter = new Intl.NumberFormat('en-US', {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });

  protected override async firstUpdated() {
    const { people } = await getPeople();
    this.items = people.map((person) => ({ ...person, customerRating: Math.random() * 10 }));
  }

  protected override render() {
    return html`
      <vaadin-grid .items="${this.items}" class="styling-header-footer">
        <vaadin-grid-column path="firstName"></vaadin-grid-column>
        <vaadin-grid-column path="lastName"></vaadin-grid-column>
        <vaadin-grid-column path="profession"></vaadin-grid-column>
        <vaadin-grid-column
          header="Customer rating (0-10)"
          header-part-name="rating-header"
          footer-part-name="rating-footer"
          ${columnFooterRenderer(() => html`<span>Avg rating: 5.32</span>`, [])}
          ${columnBodyRenderer(this.ratingRenderer, [])}
        ></vaadin-grid-column>
      </vaadin-grid>
    `;
  }

  private ratingRenderer: GridColumnBodyLitRenderer<PersonWithRating> = (person) => html`
    <span>${this.ratingFormatter.format(person.customerRating)}</span>
  `;
}
```

**Flow** — `GridHeaderFooterStyling.java`

```java
Grid<PersonWithRating> grid = new Grid<>(PersonWithRating.class, false);
grid.addClassName("styling-header-footer");
grid.addColumn(PersonWithRating::getFirstName).setHeader("First name");
grid.addColumn(PersonWithRating::getLastName).setHeader("Last name");
grid.addColumn(PersonWithRating::getProfession).setHeader("Profession");
grid.addColumn(PersonWithRating::getFormattedRating)
        .setHeader("Customer rating (0-10)")
        .setHeaderPartName("rating-header")
        .setFooter("Avg rating: 5.32")
        .setFooterPartName("rating-footer");
```

**React** — `grid-header-footer-styling.tsx`

```typescript
interface PersonWithRating extends Person {
  customerRating: number;
}

const ratingFormatter = new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

const ratingRenderer = ({ item: person }: { item: PersonWithRating }) => (
  <span>{ratingFormatter.format(person.customerRating)}</span>
);

function Example() {
  const items = useSignal<PersonWithRating[]>([]);
  useEffect(() => {
    getPeople().then(({ people }) => {
      const peopleWithRating = people.map((person) => ({
        ...person,
        customerRating: Math.random() * 10,
      }));
      items.value = peopleWithRating;
    });
  }, []);

  return (
    <Grid items={items.value} className="styling-header-footer">
      <GridColumn path="firstName" />
      <GridColumn path="lastName" />
      <GridColumn path="profession" />
      <GridColumn
        header="Customer rating (0-10)"
        header-part-name="rating-header"
        footer-part-name="rating-footer"
        renderer={ratingRenderer}
        footer={<span>Avg rating: 5.32</span>}
      />
    </Grid>
  );
}
```

`grid-header-footer-styling.css`

```css
/* Add this to your global CSS, for example in: */
/* src/main/resources/META-INF/resources/styles.css */

vaadin-grid.styling-header-footer::part(rating-header) {
    font-size: 0.8125rem;
    white-space: unset;
    color: var(--blue-600);
}

vaadin-grid.styling-header-footer::part(rating-footer) {
    color: var(--blue-600);
}
```
