> Markdown version of [Grid](https://vaadin.com/docs/v14/ds/components/grid). Section index: [llms.txt](https://vaadin.com/docs/v14/ds/llms.txt)

# Grid

Grid is a component for showing tabular data.

`GridBasic.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.addColumn(Person::getProfession).setHeader("Profession");

List<Person> people = DataService.getPeople();
grid.setItems(people);
```

`Person.java`

```java
public class Person {

  private String firstName;

  private String lastName;

  private String email;

  private Date birthday;

  private Integer id;

  private boolean subscriber;

  private String membership;

  private String pictureUrl;

  private String profession;

  private Address address;

  private Integer managerId;

  private boolean manager;

  private String status;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public String getFullName() {
    return firstName + " " + lastName;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public Date getBirthday() {
    return birthday;
  }

  public void setBirthday(Date birthday) {
    this.birthday = birthday;
  }

  public boolean isSubscriber() {
    return subscriber;
  }

  public void setSubscriber(boolean subscriber) {
    this.subscriber = subscriber;
  }

  public String getMembership() {
    return membership;
  }

  public void setMembership(String membership) {
    this.membership = membership;
  }

  public String getPictureUrl() {
    return pictureUrl;
  }

  public void setPictureUrl(String pictureUrl) {
    this.pictureUrl = pictureUrl;
  }

  public String getProfession() {
    return profession;
  }

  public void setProfession(String profession) {
    this.profession = profession;
  }

  public Address getAddress() {
    return address;
  }

  public void setAddress(Address address) {
    this.address = address;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  @Override
  public int hashCode() {
    return id;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof Person)) {
      return false;
    }
    Person other = (Person) obj;
    return id == other.id;
  }

  public Integer getManagerId() {
    return managerId;
  }

  public void setManagerId(Integer managerId) {
    this.managerId = managerId;
  }

  public boolean isManager() {
    return manager;
  }

  public void setManager(boolean manager) {
    this.manager = manager;
  }

  public String getStatus() {
    return status;
  }

  public void setStatus(String status) {
    this.status = status;
  }
}
```

## <a id="content"></a>Content

A basic Grid uses plain text to display information in rows and columns. Rich content can be used to provide additional information in a more legible fashion. Components such as [input fields](https://vaadin.com/docs/v14/ds/components/input-fields.md) and [Button](https://vaadin.com/docs/v14/ds/components/button.md) are also supported.

`GridContent.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.setSelectionMode(Grid.SelectionMode.MULTI);
grid.addColumn(createEmployeeTemplateRenderer()).setHeader("Employee")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getProfession).setHeader("Profession")
        .setAutoWidth(true);
grid.addColumn(createStatusComponentRenderer()).setHeader("Status")
        .setAutoWidth(true);

...

private static TemplateRenderer<Person> createEmployeeTemplateRenderer() {
    return TemplateRenderer.<Person>of(
            "<vaadin-horizontal-layout style=\"align-items: center;\" theme=\"spacing\">"
                    + "<vaadin-avatar img=\"[[item.pictureUrl]]\" name=\"[[item.fullName]]\" alt=\"User avatar\"></vaadin-avatar>"
                    + "  <vaadin-vertical-layout style=\"line-height: var(--lumo-line-height-m);\">"
                    + "    <span> [[item.fullName]] </span>"
                    + "    <span style=\"font-size: var(--lumo-font-size-s); color: var(--lumo-secondary-text-color);\">"
                    + "      [[item.email]]" + "    </span>"
                    + "  </vaadin-vertical-layout>"
                    + "</vaadin-horizontal-layout>")
            .withProperty("pictureUrl", Person::getPictureUrl)
            .withProperty("fullName", Person::getFullName)
            .withProperty("email", Person::getEmail);
}

private static final SerializableBiConsumer<Span, Person> statusComponentUpdater = (span, person) -> {
    boolean isAvailable = "Available".equals(person.getStatus());
    String theme = String
            .format("badge %s", isAvailable ? "success" : "error");
    span.getElement().setAttribute("theme", theme);
    span.setText(person.getStatus());
};

private static ComponentRenderer<Span, Person> createStatusComponentRenderer() {
    return new ComponentRenderer<>(Span::new, statusComponentUpdater);
}
```

### <a id="component-renderer-vs-template-renderer"></a>Component Renderer vs Template Renderer

As demonstrated in the example above, custom content can be rendered using component renderers or template renderers.

#### <a id="component-renderer"></a>Component Renderer

Component renderers are easy to build but slow to render. For a given column, they generate a component for each item in the dataset. The rendered components are fully controllable on the server side.

For each rendered cell, Grid creates a corresponding component instance on the server side. A dataset of 100 items and 10 columns using component renderer adds up to 1000 components that need to be managed. The more components used in a component renderer, the more it will negatively affect performance.

Component renderers are very flexible and easy to use, but should be used with caution. They are better suited as editors since only a single row can be edited at a time. They can also be used for detail rows.

#### <a id="template-renderer"></a>Template Renderer

Template renderers render quickly but require writing HTML. To use components with template renderers, you must use their HTML format. Templates are immutable, meaning the state of the components cannot be managed on the server side. The template can however have different representations depending on the state of the item.

The only data sent from the server, other than the template itself (which is only sent once), is the extra name property of each item.

Templates still enable event handling on the server side but you cannot, for example, disable or change the text of a button from the event handler. For those types of situations, use editors instead.

With template renderers, the server does not keep track of the components in each cell. It only manages the state of the item in each row. The client side does not need to wait for the server to send missing information about what needs to be rendered: it can use the template and stamp away all the information it needs.

## <a id="dynamic-height"></a>Dynamic Height

Grid has a default height of 400 pixels. It becomes scrollable when its items overflow the allocated space.

In addition to setting any fixed or relative value, the height of a grid can be set by the number of items in the dataset, meaning that the grid will grow and shrink based on the row count.

Please note that this disables scrolling and should not be used for large data sets to avoid performance issues.

`GridDynamicHeight.java`

```java
grid = new Grid<>(Person.class, false);
grid.setAllRowsVisible(true);
```

## <a id="selection"></a>Selection

Selection is enabled by default. Grid supports single and multi-select. The former allows the user to select exactly one item while the latter enables multiple items to be selected.

### <a id="single-selection-mode"></a>Single Selection Mode

In single selection mode, the user can select and deselect rows by clicking anywhere on the row.

`GridSingleSelectionMode.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");

List<Person> people = DataService.getPeople();
grid.setItems(people);

grid.addSelectionListener(selection -> {
    Optional<Person> optionalPerson = selection.getFirstSelectedItem();
    if (optionalPerson.isPresent()) {
        // System.out.printf("Selected person: %s%n", optionalPerson.get().getFullName());
    }
});
```

### <a id="multi-select-mode"></a>Multi-Select Mode

In multi-select mode, the user can use a checkbox column to select and deselect rows.

`GridMultiSelectionMode.java`

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

List<Person> people = DataService.getPeople();
grid.setItems(people);

grid.addSelectionListener(selection -> {
    // System.out.printf("Number of selected people: %s%n", selection.getAllSelectedItems().size());
});
```

## <a id="columns"></a>Columns

Column alignment, freezing (fixed position), grouping, headers & footers, visibility, and width can be configured. Users can be allowed to resize and reorder columns.

### <a id="column-alignment"></a>Column Alignment

Three different column alignments are supported: left (default), center and right.

Right align is useful when comparing numeric values as it helps with readability and scannability. Tabular numbers (if the font offers them) or a monospace font could be used to further improve digit alignment.

`GridColumnAlignment.java`

```java
grid.addColumn(GridColumnAlignment::generateRandomAmountText)
        .setHeader("Amount").setTextAlign(ColumnTextAlign.END);
```

### <a id="column-freezing"></a>Column Freezing

Columns and column groups can be frozen to exclude them from scrolling a grid horizontally. This can be useful for keeping the most important columns always visible in a grid with a large number of columns.

`GridColumnFreezing.java`

```java
grid.addColumn(createPersonRenderer()).setHeader("Name").setFrozen(true)
        .setAutoWidth(true).setFlexGrow(0);
```

> **Note: Freeze leftmost columns only**
>
> While it’s technically possible to freeze any column, this feature should primarily be used to freeze a certain number of columns starting with the leftmost one, leaving all remaining columns to the right unfrozen.

### <a id="column-grouping"></a>Column Grouping

It is possible to group columns together. Grouped columns share a common header and footer. Use this feature to better visualise and organise related or hierarchical data.

`GridColumnGrouping.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name");
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name");
Grid.Column<Person> streetColumn = grid
        .addColumn(person -> person.getAddress().getStreet())
        .setHeader("Street");
Grid.Column<Person> cityColumn = grid
        .addColumn(person -> person.getAddress().getCity())
        .setHeader("City");
Grid.Column<Person> zipColumn = grid
        .addColumn(person -> person.getAddress().getZip())
        .setHeader("Zip");
Grid.Column<Person> stateColumn = grid
        .addColumn(person -> person.getAddress().getState())
        .setHeader("State");

HeaderRow headerRow = grid.prependHeaderRow();
headerRow.join(firstNameColumn, lastNameColumn).setText("Name");
headerRow.join(streetColumn, cityColumn, zipColumn, stateColumn)
        .setText("Address");
```

### <a id="column-headers-footers"></a>Column Headers & Footers

Each column has its own customizable header and footer. A basic column header shows the name in plain text. Footers are empty and thus hidden by default. Both can contain rich content and components.

`GridColumnHeaderFooter.java`

```java
List<Person> people = DataService.getPeople();

Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFullName).setHeader("Name")
        .setFooter(String.format("%s total members", people.size()));
grid.addColumn(person -> person.isSubscriber() ? "Yes" : "No")
        .setHeader(createSubscriberHeader())
        .setFooter(createSubscriberFooterText(people));
grid.addColumn(Person::getMembership)
        .setHeader(createMembershipHeader())
        .setFooter(createMembershipFooterText(people));

...

private static Component createSubscriberHeader() {
    Span span = new Span("Subscriber");
    Icon icon = VaadinIcon.INFO_CIRCLE.create();
    icon.getElement()
            .setAttribute("title", "Subscribers are paying customers");
    icon.getStyle().set("height", "var(--lumo-font-size-m)")
            .set("color", "var(--lumo-contrast-70pct)");

    HorizontalLayout layout = new HorizontalLayout(span, icon);
    layout.setAlignItems(FlexComponent.Alignment.CENTER);
    layout.setSpacing(false);

    return layout;
}

private static String createSubscriberFooterText(List<Person> people) {
    long subscriberCount = people.stream().filter(Person::isSubscriber)
            .count();

    return String.format("%s subscribers", subscriberCount);
}

private static Component createMembershipHeader() {
    Span span = new Span("Membership");
    Icon icon = VaadinIcon.INFO_CIRCLE.create();
    icon.getElement().setAttribute("title",
            "Membership levels determines which features a client has access to");
    icon.getStyle().set("height", "var(--lumo-font-size-m)")
            .set("color", "var(--lumo-contrast-70pct)");

    HorizontalLayout layout = new HorizontalLayout(span, icon);
    layout.setAlignItems(FlexComponent.Alignment.CENTER);
    layout.setSpacing(false);

    return layout;
}

private static String createMembershipFooterText(List<Person> people) {
    long regularCount = people.stream()
            .filter(person -> "Regular".equals(person.getMembership()))
            .count();
    long premiumCount = people.stream()
            .filter(person -> "Premium".equals(person.getMembership()))
            .count();
    long vipCount = people.stream()
            .filter(person -> "VIP".equals(person.getMembership())).count();

    return String.format("%s regular, %s premium, %s VIP", regularCount,
            premiumCount, vipCount);
}
```

### <a id="column-visibility"></a>Column Visibility

Columns and column groups can be hidden. You can provide the user with a menu for toggling column visibilities, for example, using Menu Bar.

Allowing the user to hide columns is useful when only a subset of the columns are relevant to their task, and if there is a large number of columns.

`GridColumnVisibility.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
Grid.Column<Person> firstNameColumn = grid.addColumn(Person::getFirstName)
        .setHeader("First name");
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name");
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail)
        .setHeader("Email");
Grid.Column<Person> phoneColumn = grid
        .addColumn(person -> person.getAddress().getPhone())
        .setHeader("Phone");
Grid.Column<Person> professionColumn = grid.addColumn(Person::getProfession)
        .setHeader("Profession");

Button menuButton = new Button("Show/Hide Columns");
menuButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
ColumnToggleContextMenu columnToggleContextMenu = new ColumnToggleContextMenu(
        menuButton);
columnToggleContextMenu.addColumnToggleItem("First name", firstNameColumn);
columnToggleContextMenu.addColumnToggleItem("Last name", lastNameColumn);
columnToggleContextMenu.addColumnToggleItem("Email", emailColumn);
columnToggleContextMenu.addColumnToggleItem("Phone", phoneColumn);
columnToggleContextMenu.addColumnToggleItem("Profession", professionColumn);

...

void addColumnToggleItem(String label, Grid.Column<Person> column) {
    MenuItem menuItem = this.addItem(label, e -> {
        column.setVisible(e.getSource().isChecked());
    });
    menuItem.setCheckable(true);
    menuItem.setChecked(column.isVisible());
}
```

### <a id="column-reordering-resizing"></a>Column Reordering & Resizing

Enabling the user to reorder columns is useful when they wish to compare data that is not adjacent by default. Grouped columns can only be reordered within their group.

Resizing is helpful when a column’s content does not fit and gets cut off or varies in length.

`GridColumnReorderingResizing.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.setColumnReorderingAllowed(true);
Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name")
        .setResizable(true);
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name").setResizable(true);
Grid.Column<Person> streetColumn = grid
        .addColumn(person -> person.getAddress().getStreet())
        .setHeader("Street").setResizable(true);
Grid.Column<Person> cityColumn = grid
        .addColumn(person -> person.getAddress().getCity())
        .setHeader("City").setResizable(true);
Grid.Column<Person> zipColumn = grid
        .addColumn(person -> person.getAddress().getZip())
        .setHeader("Zip").setResizable(true);
Grid.Column<Person> stateColumn = grid
        .addColumn(person -> person.getAddress().getState())
        .setHeader("State").setResizable(true);

HeaderRow headerRow = grid.prependHeaderRow();
headerRow.join(firstNameColumn, lastNameColumn).setText("Name");
headerRow.join(streetColumn, cityColumn, zipColumn, stateColumn)
        .setText("Address");
```

### <a id="column-width"></a>Column Width

All columns are the same width by default. You can set a specific width for any column, or allow the Grid to automatically set the width based on the contents.

Column widths can be fixed or non-fixed (default). Fixed width columns do not grow or shrink as the available space changes, while non-fixed width columns do.

In the following example, the first and last columns have fixed widths. The second column’s width is to set to be based on the content, while the third column takes up the remaining space.

`GridColumnWidth.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.setSelectionMode(Grid.SelectionMode.MULTI);
grid.addColumn(Person::getFirstName).setHeader("First name")
        .setWidth("7em").setFlexGrow(0);
grid.addColumn(Person::getProfession).setHeader("Profession")
        .setAutoWidth(true).setFlexGrow(0);
grid.addColumn(Person::getEmail).setHeader("Email");
grid.addColumn(person -> person.isSubscriber() ? "Yes" : "No")
        .setHeader("Has Sub").setWidth("6em").setFlexGrow(0);
```

## <a id="sorting"></a>Sorting

Any column can be made sortable. Enable sorting to allow the user to sort items alphabetically, numerically, by date, etc.

`GridSorting.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getId).setHeader("Id").setSortable(true);
grid.addColumn(Person::getFullName).setHeader("Name").setSortable(true);
grid.addColumn(Person::getEmail).setHeader("Email").setSortable(true);
grid.addColumn(Person::getProfession).setHeader("Profession")
        .setSortable(true);
grid.addColumn(new LocalDateRenderer<>(GridSorting::getPersonBirthday,
        "yyyy-MM-dd")).setHeader("Birthday").setSortable(true)
        .setComparator(Person::getBirthday);
```

You can also sort columns that contain rich and/or custom content by defining which property to sort by. For example, you can have a column containing a person’s profile picture, name and email sorted by the person’s last name.

`GridRichContentSorting.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createEmployeeRenderer()).setHeader("Employee")
        .setAutoWidth(true).setFlexGrow(0)
        .setComparator(Person::getLastName);
grid.addColumn(createBirthdayRenderer()).setHeader("Birthdate")
        .setComparator(Person::getBirthday);
```

Sorting helps users find and analyze the data, so it’s generally recommended to enable it for all applicable columns, except in cases where the order of items is an essential part of the data itself (such as prioritized lists).

## <a id="filtering"></a>Filtering

Filtering allows the user to quickly find a specific item or subset of items. You can add filters to Grid columns or use external filter fields.

`GridColumnFiltering.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
Grid.Column<Person> nameColumn = grid.addColumn(createPersonRenderer())
        .setWidth("230px").setFlexGrow(0);
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail);
Grid.Column<Person> professionColumn = grid
        .addColumn(Person::getProfession);

List<Person> people = DataService.getPeople();
ListDataProvider<Person> dataProvider = new ListDataProvider<>(people);
grid.setDataProvider(dataProvider);
PersonFilter personFilter = new PersonFilter(dataProvider);

grid.getHeaderRows().clear();
HeaderRow headerRow = grid.appendHeaderRow();

headerRow.getCell(nameColumn).setComponent(
        createFilterHeader("Name", personFilter::setFullName));
headerRow.getCell(emailColumn).setComponent(
        createFilterHeader("Email", personFilter::setEmail));
headerRow.getCell(professionColumn).setComponent(
        createFilterHeader("Profession", personFilter::setProfession));

...

private static Component createFilterHeader(String labelText,
        Consumer<String> filterChangeConsumer) {
    Label label = new Label(labelText);
    label.getStyle().set("padding-top", "var(--lumo-space-m)")
            .set("font-size", "var(--lumo-font-size-xs)");
    TextField textField = new TextField();
    textField.setValueChangeMode(ValueChangeMode.EAGER);
    textField.setClearButtonVisible(true);
    textField.addThemeVariants(TextFieldVariant.LUMO_SMALL);
    textField.setWidthFull();
    textField.getStyle().set("max-width", "100%");
    textField.addValueChangeListener(
            e -> filterChangeConsumer.accept(e.getValue()));
    VerticalLayout layout = new VerticalLayout(label, textField);
    layout.getThemeList().clear();
    layout.getThemeList().add("spacing-xs");

    return layout;
}

private static class PersonFilter {
    private final ListDataProvider<Person> dataProvider;

    private String fullName;
    private String email;
    private String profession;

    public PersonFilter(ListDataProvider<Person> dataProvider) {
        this.dataProvider = dataProvider;
        this.dataProvider.addFilter(this::test);
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
        this.dataProvider.refreshAll();
    }

    public void setEmail(String email) {
        this.email = email;
        this.dataProvider.refreshAll();
    }

    public void setProfession(String profession) {
        this.profession = profession;
        this.dataProvider.refreshAll();
    }

    public boolean test(Person person) {
        boolean matchesFullName = matches(person.getFullName(), fullName);
        boolean matchesEmail = matches(person.getEmail(), email);
        boolean matchesProfession = matches(person.getProfession(),
                profession);

        return matchesFullName && matchesEmail && matchesProfession;
    }

    private boolean matches(String value, String searchTerm) {
        return searchTerm == null || searchTerm.isEmpty() || value
                .toLowerCase().contains(searchTerm.toLowerCase());
    }
}
```

Place filters outside the grid when:

- The filter is based on multiple columns.

- A bigger field or more complex filter UI is needed, which wouldn’t comfortably fit in a column.

`GridExternalFiltering.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(createPersonRenderer()).setHeader("Name").setFlexGrow(0)
        .setWidth("230px");
grid.addColumn(Person::getEmail).setHeader("Email");
grid.addColumn(Person::getProfession).setHeader("Profession");

List<Person> people = DataService.getPeople();
ListDataProvider<Person> dataProvider = new ListDataProvider<>(people);
grid.setDataProvider(dataProvider);

TextField searchField = new TextField();
searchField.setWidth("50%");
searchField.setPlaceholder("Search");
searchField.setPrefixComponent(new Icon(VaadinIcon.SEARCH));
searchField.setValueChangeMode(ValueChangeMode.EAGER);
searchField.addValueChangeListener(e -> dataProvider.refreshAll());

dataProvider.addFilter(person -> {
    String searchTerm = searchField.getValue().trim();

    if (searchTerm.isEmpty())
        return true;

    boolean matchesFullName = matchesTerm(person.getFullName(),
            searchTerm);
    boolean matchesEmail = matchesTerm(person.getEmail(), searchTerm);
    boolean matchesProfession = matchesTerm(person.getProfession(),
            searchTerm);

    return matchesFullName || matchesEmail || matchesProfession;
});
```

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

Item details are expandable content areas that can be displayed below the regular content of a row, used to display more information about an item. By default, an item’s details are toggled by clicking on the item’s row.

`GridItemDetails.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFullName).setHeader("Name");
grid.addColumn(Person::getProfession).setHeader("Profession");

grid.setItemDetailsRenderer(createPersonDetailsRenderer());

...

private static ComponentRenderer<PersonDetailsFormLayout, Person> createPersonDetailsRenderer() {
    return new ComponentRenderer<>(
            PersonDetailsFormLayout::new,
            PersonDetailsFormLayout::setPerson);
}

private static class PersonDetailsFormLayout extends FormLayout {
    private final TextField emailField = new TextField("Email address");
    private final TextField phoneField = new TextField("Phone number");
    private final TextField streetField = new TextField("Street address");
    private final TextField zipField = new TextField("ZIP code");
    private final TextField cityField = new TextField("City");
    private final TextField stateField = new TextField("State");

    public PersonDetailsFormLayout() {
        Stream.of(emailField, phoneField, streetField, zipField, cityField,
                stateField).forEach(field -> {
            field.setReadOnly(true);
            add(field);
        });

        setResponsiveSteps(new ResponsiveStep("0", 3));
        setColspan(emailField, 3);
        setColspan(phoneField, 3);
        setColspan(streetField, 3);
    }

    public void setPerson(Person person) {
        emailField.setValue(person.getEmail());
        phoneField.setValue(person.getAddress().getPhone());
        streetField.setValue(person.getAddress().getStreet());
        zipField.setValue(person.getAddress().getZip());
        cityField.setValue(person.getAddress().getCity());
        stateField.setValue(person.getAddress().getState());
    }
}
```

The default toggle behavior can be replaced by programmatically toggling the details visibility, for example, from a button click.

`GridItemDetailsToggle.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFullName).setHeader("Name");
grid.addColumn(Person::getProfession).setHeader("Profession");
grid.addColumn(createToggleDetailsRenderer(grid));

grid.setDetailsVisibleOnClick(false);
grid.setItemDetailsRenderer(createPersonDetailsRenderer());

...

private static TemplateRenderer<Person> createToggleDetailsRenderer(
        Grid<Person> grid) {
    return TemplateRenderer.<Person>of(
            "<vaadin-button theme=\"tertiary\" on-click=\"handleClick\">Toggle details</vaadin-button>")
            .withEventHandler("handleClick", person -> grid
                    .setDetailsVisible(person,
                                       !grid.isDetailsVisible(person)));
}
```

## <a id="context-menu"></a>Context Menu

You can use Context Menu to provide shortcuts to the user. It appears on right (default) or left click. In a mobile browser, a long press opens the menu.

Please note that using a context menu should not be the only way of accomplishing a task. The same functionality needs to be accessible elsewhere in the UI as well.

See [Context Menu](https://vaadin.com/docs/v14/ds/components/context-menu.md) for more information.

`GridContextMenuExample.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.addColumn(Person::getProfession).setHeader("Profession");

PersonContextMenu contextMenu = new PersonContextMenu(grid);

add(grid, contextMenu);

...

private static class PersonContextMenu extends GridContextMenu<Person> {
    public PersonContextMenu(Grid<Person> target) {
        super(target);

        addItem("Edit", e -> e.getItem().ifPresent(person -> {
            // System.out.printf("Edit: %s%n", person.getFullName());
        }));
        addItem("Delete", e -> e.getItem().ifPresent(person -> {
            // System.out.printf("Delete: %s%n", person.getFullName());
        }));

        add(new Hr());

        GridMenuItem<Person> emailItem = addItem("Email",
                e -> e.getItem().ifPresent(person -> {
                    // System.out.printf("Email: %s%n", person.getFullName());
                }));
        GridMenuItem<Person> phoneItem = addItem("Call",
                e -> e.getItem().ifPresent(person -> {
                    // System.out.printf("Phone: %s%n", person.getFullName());
                }));

        setDynamicContentHandler(person -> {
            // Do not show context menu when header is clicked
            if (person == null)
                return false;
            emailItem.setText(
                    String.format("Email: %s", person.getEmail()));
            phoneItem.setText(String.format("Call: %s",
                    person.getAddress().getPhone()));
            return true;
        });
    }
}
```

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

Grid supports drag and drop, for example to reorder rows and to drag rows between grids.

### <a id="drop-mode"></a>Drop Mode

The drop mode of a grid determines where a drop can happen. Vaadin offers 4 different drop modes:

| Drop Mode                           | Description                                                                                                                                                                    |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [On Grid](#drag-rows-between-grids) | Drops can occur on the grid as a whole, not on top of or between individual rows. Use this option when the order is unimportant.                                               |
| [Between](#row-reordering)          | Drops can happen between rows. Use this mode when the order is important.                                                                                                      |
| [On Top](#drag-and-drop-filters)    | Drops can take place on top of rows. This is useful when creating relationships between items or moving an item into another item, for example placing a file inside a folder. |
| On Top or Between                   | Drops can occur on top of or between rows.                                                                                                                                     |

### <a id="row-reordering"></a>Row Reordering

You can use drag and drop to reorder rows.

`GridRowReordering.java`

```java
Grid<Person> grid = setupGrid();

// Modifying the data view requires a mutable collection
List<Person> people = new ArrayList<>(DataService.getPeople());
ListDataProvider<Person> dataProvider = new ListDataProvider<>(people);
grid.setDataProvider(dataProvider);

grid.setDropMode(GridDropMode.BETWEEN);
grid.setRowsDraggable(true);

grid.addDragStartListener(
        e -> draggedItem = e.getDraggedItems().get(0));

grid.addDropListener(e -> {
    Person targetPerson = e.getDropTargetItem().orElse(null);
    GridDropLocation dropLocation = e.getDropLocation();

    boolean personWasDroppedOntoItself = draggedItem
            .equals(targetPerson);

    if (targetPerson == null || personWasDroppedOntoItself)
        return;

    people.remove(draggedItem);

    if (dropLocation == GridDropLocation.BELOW) {
        people.add(people.indexOf(targetPerson) + 1, draggedItem);
    } else {
        people.add(people.indexOf(targetPerson), draggedItem);
    }
    dataProvider.refreshAll();
});

grid.addDragEndListener(e -> draggedItem = null);
```

### <a id="drag-rows-between-grids"></a>Drag Rows Between Grids

Rows can be dragged from one grid to another, for example to move, copy or link items from different datasets.

`GridDragRowsBetweenGrids.java`

```java
Grid<Person> grid1 = setupGrid();
Grid<Person> grid2 = setupGrid();

ListDataProvider<Person> dataProvider1 = new ListDataProvider<>(people1);
ListDataProvider<Person> dataProvider2 = new ListDataProvider<>(people2);

grid1.setDataProvider(dataProvider1);
grid2.setDataProvider(dataProvider2);

grid1.setDropMode(GridDropMode.ON_GRID);
grid1.setRowsDraggable(true);
grid1.addDragStartListener(this::handleDragStart);
grid1.addDropListener(e -> {
    if (dataProvider1.getItems().contains(draggedItem)) {
        return;
    }
    dataProvider2.getItems().remove(draggedItem);
    dataProvider2.refreshAll();
    dataProvider1.getItems().add(draggedItem);
    dataProvider1.refreshAll();
});
grid1.addDragEndListener(this::handleDragEnd);

grid2.setDropMode(GridDropMode.ON_GRID);
grid2.setRowsDraggable(true);
grid2.addDragStartListener(this::handleDragStart);
grid2.addDropListener(e -> {
    if (dataProvider2.getItems().contains(draggedItem)) {
        return;
    }
    dataProvider1.getItems().remove(draggedItem);
    dataProvider1.refreshAll();
    dataProvider2.getItems().add(draggedItem);
    dataProvider2.refreshAll();
});
grid2.addDragEndListener(this::handleDragEnd);
```

### <a id="drag-and-drop-filters"></a>Drag and Drop Filters

Drag and drop filters determine which rows are draggable and which rows are valid drop targets, respectively. The filters function on a per row basis.

`GridDragDropFilters.java`

```java
TreeGrid<Person> treeGrid = setupTreeGrid();

TreeData<Person> treeData = new TreeData<>();
treeData.addItems(managers, this::getStaff);
TreeDataProvider<Person> treeDataProvider = new TreeDataProvider<>(
        treeData);
treeGrid.setDataProvider(treeDataProvider);

treeGrid.setRowsDraggable(true);
treeGrid.setDropMode(GridDropMode.ON_TOP);
// Only allow dragging staff
treeGrid.setDragFilter(person -> !person.isManager());
// Only allow dropping on managers
treeGrid.setDropFilter(person -> person.isManager());

treeGrid.addDragStartListener(
        e -> draggedItem = e.getDraggedItems().get(0));

treeGrid.addDropListener(e -> {
    Person newManager = e.getDropTargetItem().orElse(null);
    boolean isSameManager = newManager != null && newManager.getId()
            .equals(draggedItem.getManagerId());

    if (newManager == null || isSameManager)
        return;

    draggedItem.setManagerId(newManager.getId());
    treeData.removeItem(draggedItem);
    treeData.addItem(newManager, draggedItem);

    treeDataProvider.refreshAll();
});

treeGrid.addDragEndListener(e -> draggedItem = null);
```

## <a id="inline-editing"></a>Inline Editing

Grid can be configured to allow inline editing. Editing can be either buffered and non-buffered. Buffered means changes must be explicitly committed, while non-buffered automatically commit changes on blur (when a field loses focus).

### <a id="buffered"></a>Buffered

`GridBufferedInlineEditor.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
Editor<Person> editor = grid.getEditor();

Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name")
        .setWidth("120px").setFlexGrow(0);
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name").setWidth("120px").setFlexGrow(0);
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail)
        .setHeader("Email");
Grid.Column<Person> editColumn = grid.addComponentColumn(person -> {
    Button editButton = new Button("Edit");
    editButton.addClickListener(e -> {
        if (editor.isOpen())
            editor.cancel();
        grid.getEditor().editItem(person);
    });
    return editButton;
}).setWidth("150px").setFlexGrow(0);

Binder<Person> binder = new Binder<>(Person.class);
editor.setBinder(binder);
editor.setBuffered(true);

TextField firstNameField = new TextField();
firstNameField.setWidthFull();
binder.forField(firstNameField)
        .asRequired("First name must not be empty")
        .withStatusLabel(firstNameValidationMessage)
        .bind(Person::getFirstName, Person::setFirstName);
firstNameColumn.setEditorComponent(firstNameField);

TextField lastNameField = new TextField();
lastNameField.setWidthFull();
binder.forField(lastNameField).asRequired("Last name must not be empty")
        .withStatusLabel(lastNameValidationMessage)
        .bind(Person::getLastName, Person::setLastName);
lastNameColumn.setEditorComponent(lastNameField);

EmailField emailField = new EmailField();
emailField.setWidthFull();
binder.forField(emailField).asRequired("Email must not be empty")
        .withValidator(new EmailValidator(
                "Please enter a valid email address"))
        .withStatusLabel(emailValidationMessage)
        .bind(Person::getEmail, Person::setEmail);
emailColumn.setEditorComponent(emailField);

Button saveButton = new Button("Save", e -> editor.save());
Button cancelButton = new Button(VaadinIcon.CLOSE.create(),
        e -> editor.cancel());
cancelButton.addThemeVariants(ButtonVariant.LUMO_ICON,
        ButtonVariant.LUMO_ERROR);
HorizontalLayout actions = new HorizontalLayout(saveButton,
        cancelButton);
actions.setPadding(false);
editColumn.setEditorComponent(actions);
```

**Java** — `ValidationMessage.java`

```java
package com.vaadin.demo.component.grid;

import com.vaadin.flow.component.HasText;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;

class ValidationMessage extends HorizontalLayout implements HasText {

    private final Span span = new Span();

    public ValidationMessage() {
        setVisible(false);
        setAlignItems(Alignment.CENTER);
        getStyle().set("color", "var(--lumo-error-text-color)");
        getThemeList().clear();
        getThemeList().add("spacing-s");

        Icon icon = VaadinIcon.EXCLAMATION_CIRCLE_O.create();
        icon.setSize("16px");
        add(icon, span);
    }

    @Override
    public String getText() {
        return span.getText();
    }

    @Override
    public void setText(String text) {
        span.setText(text);
        this.setVisible(text != null && !text.isEmpty());
    }
}
```

### <a id="non-buffered"></a>Non-Buffered

In the example below, double-click a row to start editing. Press `Escape`, or click on a different row to stop editing.

`GridUnbufferedInlineEditor.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
Grid.Column<Person> firstNameColumn = grid
        .addColumn(Person::getFirstName).setHeader("First name")
        .setWidth("120px").setFlexGrow(0);
Grid.Column<Person> lastNameColumn = grid.addColumn(Person::getLastName)
        .setHeader("Last name").setWidth("120px").setFlexGrow(0);
Grid.Column<Person> emailColumn = grid.addColumn(Person::getEmail)
        .setHeader("Email");

Binder<Person> binder = new Binder<>(Person.class);
Editor<Person> editor = grid.getEditor();
editor.setBinder(binder);

TextField firstNameField = new TextField();
firstNameField.setWidthFull();
addCloseHandler(firstNameField, editor);
binder.forField(firstNameField)
        .asRequired("First name must not be empty")
        .withStatusLabel(firstNameValidationMessage)
        .bind(Person::getFirstName, Person::setFirstName);
firstNameColumn.setEditorComponent(firstNameField);

TextField lastNameField = new TextField();
lastNameField.setWidthFull();
addCloseHandler(lastNameField, editor);
binder.forField(lastNameField).asRequired("Last name must not be empty")
        .withStatusLabel(lastNameValidationMessage)
        .bind(Person::getLastName, Person::setLastName);
lastNameColumn.setEditorComponent(lastNameField);

EmailField emailField = new EmailField();
emailField.setWidthFull();
addCloseHandler(emailField, editor);
binder.forField(emailField).asRequired("Email must not be empty")
        .withValidator(new EmailValidator(
                "Please enter a valid email address"))
        .withStatusLabel(emailValidationMessage)
        .bind(Person::getEmail, Person::setEmail);
emailColumn.setEditorComponent(emailField);

grid.addItemDoubleClickListener(e -> {
    editor.editItem(e.getItem());
    Component editorComponent = e.getColumn().getEditorComponent();
    if (editorComponent instanceof Focusable) {
        ((Focusable) editorComponent).focus();
    }
});
```

**Java** — `ValidationMessage.java`

```java
package com.vaadin.demo.component.grid;

import com.vaadin.flow.component.HasText;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;

class ValidationMessage extends HorizontalLayout implements HasText {

    private final Span span = new Span();

    public ValidationMessage() {
        setVisible(false);
        setAlignItems(Alignment.CENTER);
        getStyle().set("color", "var(--lumo-error-text-color)");
        getThemeList().clear();
        getThemeList().add("spacing-s");

        Icon icon = VaadinIcon.EXCLAMATION_CIRCLE_O.create();
        icon.setSize("16px");
        add(icon, span);
    }

    @Override
    public String getText() {
        return span.getText();
    }

    @Override
    public void setText(String text) {
        span.setText(text);
        this.setVisible(text != null && !text.isEmpty());
    }
}
```

Alternatively, use [Grid Pro](https://vaadin.com/docs/v14/ds/components/grid-pro.md) for more streamlined inline-editing, or [CRUD](https://vaadin.com/docs/v14/ds/components/crud.md) for editing in a separate side panel or dialog.

## <a id="styling-rows-and-columns"></a>Styling Rows and Columns

You can style individual cells based on the data, for example, to highlight changes or important information.

The Java Flow API currently only allows to style the whole row, but not individual cells.

`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)");

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

`vaadin-grid-styling.css`

```css
/* frontend/theme/[my-theme]/components/vaadin-grid.css */

.high-rating {
    background-color: #b1ffb7;
}

.low-rating {
    background-color: #ffd9d9;
}

.font-weight-bold {
    font-weight: bold;
}
```

## <a id="theme-variants"></a>Theme Variants

Grid variants can reduce the white space inside the grid, adjust border and row highlight visibility, and control cell content overflow behavior.

Variants can be combined together freely.

### <a id="compact"></a>Compact

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

It is useful for displaying more information on-screen without having to scroll. It can also help improve scannability and comparability between rows.

`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);
```

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

The `no-border` theme variant removes the outer border of the grid.

`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.LUMO_NO_BORDER);
```

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

This theme variant removes the horizontal row borders. It is best suited for small datasets. Parsing larger sets may be difficult unless paired with the `row-stripes` theme variant.

`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.LUMO_NO_ROW_BORDERS);
```

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

You can add vertical borders between columns by using the `column-borders` theme variant. Data sets with a lot of columns packed tightly together, or where content gets truncated, can benefit from the additional separation that vertical borders bring.

`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.LUMO_COLUMN_BORDERS);
```

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

The `row-stripes` theme produces a background color for every other row. It can have a positive effect on scannability.

`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.LUMO_ROW_STRIPES);
```

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

Overflowing cell content is clipped or truncated by default. This variant makes the content wrap instead.

`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.LUMO_WRAP_CELL_CONTENT);
```

## <a id="related-components"></a>Related Components

| Component                                                           | Usage recommendations                                                   |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| [CRUD](https://vaadin.com/docs/v14/ds/components/crud.md)           | Component for creating, displaying, updating and deleting tabular data. |
| [Grid Pro](https://vaadin.com/docs/v14/ds/components/grid-pro.md)   | Component for showing and editing tabular data.                         |
| [Tree Grid](https://vaadin.com/docs/v14/ds/components/tree-grid.md) | Component for showing hierarchical tabular data.                        |
| [List Box](https://vaadin.com/docs/v14/ds/components/list-box.md)   | Lightweight component for lightweight, single-column lists.             |

`DCDFD246-69FE-41E3-A286-A09B4EAE2E6F`
