> Markdown version of [Data Binding](https://vaadin.com/docs/latest/components/grid/data-binding). Section index: [llms.txt](https://vaadin.com/docs/latest/components/llms.txt)

# Binding Data to Grid Flow

Grid supports connecting to various types of data sources through data providers. A data provider is a class that implements the `DataProvider` interface and encapsulates the logic for fetching items from a specific data source, with support for pagination, sorting, and filtering. For common use cases, Grid provides convenience methods so that you don’t have to deal with the full data provider setup, and it also supports custom data providers where more flexibility is needed.

## <a id="binding-items-via-setitems"></a>Binding Items via `setItems`

The most straightforward way to bind data to a Grid is to use the `setItems` convenience method. It allows you to bind an in-memory collection or provide callbacks for lazy loading from a backend service without the need to explicitly create a data provider.

The example below shows how to display, filter and sort a static list of `Person` records:

PeopleView\.java

`BindingInMemoryList.java`

```java
Grid<Person> grid = new Grid<>(Person.class);

// Binding
GridListDataView<Person> dataView = grid.setItems(
        new Person("Michael Chen", "Engineering"),
        new Person("Sarah Johnson", "Engineering"),
        new Person("David Rodriguez", "Marketing"),
        new Person("Emma Wilson", "HR"));

// Filtering
TextField searchInput = new TextField("Search", event -> {
    if (!event.getValue().isEmpty()) {
        // Set a filter to show only people whose name contains the
        // search term
        dataView.setFilter(person -> person.name().toLowerCase()
                .contains(event.getValue().toLowerCase()));
    } else {
        // Clear the filter if the search term is empty
        dataView.removeFilters();
    }
});
searchInput.setValueChangeMode(ValueChangeMode.EAGER);

// Sorting
Select<String> sortBySelect = new Select<>("Sort by", event -> {
    switch (event.getValue()) {
    case "Name (A-Z)" ->
        // Set sort order to ascending by name
        dataView.setSortOrder(Person::name, SortDirection.ASCENDING);
    case "Name (Z-A)" ->
        // Set sort order to descending by name
        dataView.setSortOrder(Person::name, SortDirection.DESCENDING);
    default ->
        // Clear sorting
        dataView.removeSorting();
    }
});
sortBySelect.setItems("", "Name (A-Z)", "Name (Z-A)");

add(searchInput, sortBySelect, grid);
```

`Person.java`

```java
public record Person(String name, String department) {

}
```

The next example shows how to display, filter and sort data from a backend service with lazy loading by providing fetch and count callbacks to the `setItems` method:

PeopleView\.java

`BindingLazyLoadedList.java`

```java
Grid<Person> grid = new Grid<>(Person.class);

// Filtering
TextField searchInput = new TextField("Search", event -> {
    grid.getLazyDataView().refreshAll();
});
searchInput.setValueChangeMode(ValueChangeMode.EAGER);

// Sorting
Select<String> sortBySelect = new Select<>("Sort by", event -> {
    grid.getLazyDataView().refreshAll();
});
sortBySelect.setItems("", "Name (A-Z)", "Name (Z-A)");

// Binding
grid.setItems((query) -> PersonService.fetch(searchInput.getValue(),
        sortBySelect.getValue(), query.getOffset(), query.getLimit()),
        (query) -> PersonService.count(searchInput.getValue()));

add(searchInput, sortBySelect, grid);
```

`PersonService.java`

```java
public class PersonService {
    private static final List<Person> PEOPLE = Arrays.asList(
            new Person("Michael Chen", "Engineering"),
            new Person("Sarah Johnson", "Engineering"),
            new Person("David Rodriguez", "Marketing"),
            new Person("Emma Wilson", "HR"));

    public static Stream<Person> fetch(String searchTerm, String sortOrder,
            int offset, int limit) {
        return PEOPLE.stream().filter(createPredicate(searchTerm))
                .sorted(createComparator(sortOrder)).skip(offset).limit(limit);
    }

    public static int count(String searchTerm) {
        return (int) PEOPLE.stream().filter(createPredicate(searchTerm))
                .count();
    }

    private static Predicate<Person> createPredicate(String searchTerm) {
        return person -> person.name().toLowerCase()
                .contains(searchTerm != null ? searchTerm.toLowerCase() : "");
    }

    private static Comparator<Person> createComparator(String sortOrder) {
        return switch (sortOrder) {
        case "Name (A-Z)" -> Comparator.comparing(Person::name);
        case "Name (Z-A)" -> Comparator.comparing(Person::name).reversed();
        default -> (p1, p2) -> 0;
        };
    }
}
```

`Person.java`

```java
public record Person(String name, String department) {

}
```

More documentation and examples for using the `setItems` method are available in the [Binding Items To Components](https://vaadin.com/docs/latest/flow/binding-data/data-provider.md) article.

## <a id="custom-data-providers"></a>Custom Data Providers

A more advanced way to bind data to a Grid is to supply a custom data provider implementation via the `setDataProvider(DataProvider<T, F>)` method.

The example below shows a Grid backed by a custom data provider that imitates fetching data from a database, with support for filtering and sorting:

`PersonDataProvider.java`

```java
public class PersonDataProvider
        extends AbstractBackEndDataProvider<Person, PersonFilter> {
    // In a real application, data would come from a real database or backend
    // service. This example uses a static list for demonstration purposes only.
    private static final List<Person> DATABASE = Arrays.asList(
            new Person("Michael Chen", "Engineering"),
            new Person("Sarah Johnson", "Engineering"),
            new Person("David Rodriguez", "Marketing"),
            new Person("Emma Wilson", "HR"));

    @Override
    protected Stream<Person> fetchFromBackEnd(
            Query<Person, PersonFilter> query) {
        // SQL equivalent: SELECT ... FROM ...
        Stream<Person> stream = DATABASE.stream();

        // SQL equivalent: WHERE ...
        stream = stream.filter(createPredicate(query.getFilter()));

        // SQL equivalent: ORDER BY ...
        stream = stream.sorted(createComparator(query.getSortOrders()));

        // SQL equivalent: OFFSET ... LIMIT ...
        stream = stream.skip(query.getOffset()).limit(query.getLimit());

        return stream;
    }

    @Override
    protected int sizeInBackEnd(Query<Person, PersonFilter> query) {
        // SQL equivalent: SELECT COUNT(*) FROM ...
        Stream<Person> stream = DATABASE.stream();

        // SQL equivalent: WHERE ...
        stream = stream.filter(createPredicate(query.getFilter()));

        return (int) stream.count();
    }

    /**
     * Creates a filter function (predicate) based on the filter object from the
     * Grid.
     *
     * This is for demonstration purposes only. In a real application, you
     * should do filtering in the database query e.g. by using SQL WHERE if you
     * are using a relational database.
     */
    private Predicate<Person> createPredicate(Optional<PersonFilter> filter) {
        return filter.map(f -> (Predicate<Person>) f::test).orElse(p -> true);
    }

    /**
     * Creates a sorting function (comparator) based on the sort orders from the
     * Grid.
     *
     * This is for demonstration purposes only. In a real application, you
     * should do sorting in the database query e.g. by using SQL ORDER BY if you
     * are using a relational database.
     */
    private Comparator<Person> createComparator(
            List<QuerySortOrder> sortOrders) {
        return sortOrders.stream().map(sortOrder -> {
            Comparator<Person> comparator = switch (sortOrder.getSorted()) {
            case "name" -> Comparator.comparing(Person::name);
            case "department" -> Comparator.comparing(Person::department);
            default -> (p0, p1) -> 0;
            };

            return sortOrder.getDirection().equals(SortDirection.ASCENDING)
                    ? comparator
                    : comparator.reversed();
        }).reduce((p0, p1) -> 0, Comparator::thenComparing);
    }
}
```

`PersonFilter.java`

```java
public record PersonFilter(String nameFilter, String departmentFilter) {
    public boolean test(Person person) {
        boolean matches = true;
        if (nameFilter != null && !nameFilter.isEmpty()) {
            matches &= person.name().toLowerCase()
                    .contains(nameFilter.toLowerCase());
        }
        if (departmentFilter != null && !departmentFilter.isEmpty()) {
            matches &= person.department().equals(departmentFilter);
        }
        return matches;
    }
}
```

`Person.java`

```java
public record Person(String name, String department) {

}
```

PeopleView\.java

`BindingDataProvider.java`

```java
public class BindingDataProvider extends VerticalLayout {
    private TextField nameFilter;
    private Select<String> departmentFilter;

    public BindingDataProvider() {
        Grid<Person> grid = new Grid<>(Person.class);

        // Enable sorting for the columns. When the user sorts the columns,
        // the updated sort orders are passed to the data provider and are
        // available through Query#getSortOrders().
        grid.setSortableColumns("name", "department");

        // Create a data provider instance with a configurable filter allowing
        // the filter value to be set programmatically via setFilter(). The
        // filter value is passed to the data provider on change and is
        // available through Query#getFilter().
        ConfigurableFilterDataProvider<Person, Void, PersonFilter> dataProvider = new PersonDataProvider()
                .withConfigurableFilter();
        grid.setDataProvider(dataProvider);

        // Create filter components
        nameFilter = new TextField("Search by name", event -> {
            dataProvider.setFilter(createFilterObject());
        });
        nameFilter.setValueChangeMode(ValueChangeMode.EAGER);

        departmentFilter = new Select<>("Department", event -> {
            dataProvider.setFilter(createFilterObject());
        });
        departmentFilter.setItems("", "Engineering", "Marketing", "HR");

        add(nameFilter, departmentFilter, grid);
    }

    private PersonFilter createFilterObject() {
        return new PersonFilter(nameFilter.getValue(),
                departmentFilter.getValue());
    }
}
```

More examples of custom data providers are available in the [Binding Items To Components](https://vaadin.com/docs/latest/flow/binding-data/data-provider.md#recycling-data-binding-logic) article.
