Grid
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.
Theme Variants
Theme variants are built-in styling variations that can be toggled on separately or in combination.
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.
new tab
@customElement('grid-wrap-cell-content')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
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>
`;
}
Tooltips can also be used to display content that doesn’t fit into the cell.
Compact
The compact
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.
new tab
<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>
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.
new tab
<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>
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.
new tab
<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>
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.
new tab
<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>
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.
new tab
<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>
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 Lumo Style Properties for more information on style properties.
Feature | Property | Default Value |
---|---|---|
Row/cell background |
|
|
Row/cell padding |
|
|
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 for more details on how to style components.
- Root element
-
vaadin-grid
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.
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)
You can style individual cells, rows, and columns by applying custom part names to them. Cell padding can be configured using the --vaadin-grid-cell-padding
style property.
Selection Checkboxes
- Row selection checkbox
-
vaadin-grid
> vaadin-checkbox - Select All checkbox
-
vaadin-grid
> #selectAllCheckbox
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)
Item Details
- Item details cell (spans entire row)
-
vaadin-grid
::part(details-cell) - Cell in row with open details
-
vaadin-grid
::part(details-open-row-cell) - Row with open details
-
vaadin-grid
::part(details-open-row)
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)
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)
).
new tab
interface PersonWithRating extends Person {
customerRating: number;
}
@customElement('grid-styling')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
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.item;
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;
}
}
Styling Header & Footer Cells
Header and footer cells can be similarly styled through their own custom part names.
new tab
interface PersonWithRating extends Person {
customerRating: number;
}
@customElement('grid-header-footer-styling')
export class Example extends LitElement {
protected override createRenderRoot() {
const root = super.createRenderRoot();
// Apply custom theme (only supported if your app uses one)
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>
`;
}