> Markdown version of [In-Memory Grid Filtering](https://vaadin.com/docs/next/flow/ui-state/usage-examples/grid-filtering). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Grid Filtering with Signals

This guide shows how to implement reactive client-side filtering for a data grid using signals. The grid automatically updates whenever users change any filter criteria - category, search term, or stock status, without manual coordination between components.

## <a id="the-use-case"></a>The Use Case

Data grids often need multiple filters that work together. For example, a product grid where users can filter by category, search by name or ID, and toggle to show only in-stock items. Each filter should immediately update the visible results, and all filters should work together seamlessly.

Signals simplify this by making the filtered dataset reactive. When a user changes any filter, a computed signal automatically recalculates the filtered results and updates the grid.

## <a id="the-solution"></a>The Solution

`GridFilteringExample.java`

```java
public GridFilteringExample() {
    // Create signals for filter inputs
    ValueSignal<String> categoryFilterSignal = new ValueSignal<>("All");
    ValueSignal<String> searchTermSignal = new ValueSignal<>("");
    ValueSignal<Boolean> inStockOnlySignal = new ValueSignal<>(false);

    // Load all products
    List<Product> allProducts = loadProducts();

    // Computed signal for filtered products
    Signal<List<Product>> filteredProductsSignal = Signal.computed(() -> {
        String category = categoryFilterSignal.get();
        String searchTerm = searchTermSignal.get().toLowerCase();
        boolean inStockOnly = inStockOnlySignal.get();

        return allProducts.stream()
                .filter(p -> category.equals("All")
                        || p.category().equals(category))
                .filter(p -> searchTerm.isEmpty()
                        || p.name().toLowerCase().contains(searchTerm)
                        || p.id().toLowerCase().contains(searchTerm))
                .filter(p -> !inStockOnly || p.stock() > 0).toList();
    });

    // Filter UI components
    ComboBox<String> categoryFilter = new ComboBox<>("Category", List.of(
            "All", "Electronics", "Clothing", "Books", "Home & Garden"));
    categoryFilter.bindValue(categoryFilterSignal,
            categoryFilterSignal::set);

    TextField searchField = new TextField("Search");
    searchField.setPlaceholder("Search by name or ID");
    searchField.bindValue(searchTermSignal, searchTermSignal::set);

    Checkbox inStockCheckbox = new Checkbox("Show in-stock items only");
    inStockCheckbox.bindValue(inStockOnlySignal, inStockOnlySignal::set);

    // Data grid
    Grid<Product> productGrid = new Grid<>(Product.class);
    productGrid.setColumns("id", "name", "category", "price", "stock");
    Signal.effect(productGrid, () -> {
        productGrid.setItems(Objects.requireNonNullElseGet(
                filteredProductsSignal.get(), List::of));
    });

    add(categoryFilter, searchField, inStockCheckbox, productGrid);
}
```

## <a id="how-it-works"></a>How It Works

### <a id="signals-for-filter-state"></a>Signals for Filter State

Create `ValueSignal` instances for each filter input:

```java
ValueSignal<String> categoryFilterSignal = new ValueSignal<>("All");
ValueSignal<String> searchTermSignal = new ValueSignal<>("");
ValueSignal<Boolean> inStockOnlySignal = new ValueSignal<>(false);
```

These signals track the current state of each filter. When any filter changes, its signal updates automatically, triggering dependent computations.

### <a id="two-way-binding-with-filter-components"></a>Two-Way Binding with Filter Components

Connect signals to filter components using `bindValue()`:

```java
categoryFilter.bindValue(categoryFilterSignal, categoryFilterSignal::set);
searchField.bindValue(searchTermSignal, searchTermSignal::set);
inStockCheckbox.bindValue(inStockOnlySignal, inStockOnlySignal::set);
```

This creates bidirectional connections:

- When users interact with the components, the write callback (`signal::set`) updates the signal

- When code updates the signals, the framework reads via `signal.get()` and updates the components

See [Two-Way Signal Mapping](https://vaadin.com/docs/next/flow/ui-state/local-signals.md#two-way-mapping) for more details about bidirectional bindings.

### <a id="computed-signal-for-filtered-data"></a>Computed Signal for Filtered Data

Create a computed signal that derives the filtered dataset from all filter signals:

```java
Signal<List<Product>> filteredProductsSignal = Signal.computed(() -> {
    String category = categoryFilterSignal.get();
    String searchTerm = searchTermSignal.get().toLowerCase();
    boolean inStockOnly = inStockOnlySignal.get();

    return allProducts.stream()
        .filter(p -> category.equals("All")
            || p.category().equals(category))
        .filter(p -> searchTerm.isEmpty()
            || p.name().toLowerCase().contains(searchTerm)
            || p.id().toLowerCase().contains(searchTerm))
        .filter(p -> !inStockOnly || p.stock() > 0)
        .toList();
});
```

This computed signal automatically recalculates whenever any of the three filter signals change. The filtering logic is centralized in one place, making it easy to understand and maintain. The signal reads all three filter values using `get()` and applies them sequentially using stream operations.

### <a id="binding-the-grid-to-filtered-data"></a>Binding the Grid to Filtered Data

Connect the grid to the computed signal using `Signal.effect()`:

```java
Grid<Product> productGrid = new Grid<>(Product.class);
productGrid.setColumns("id", "name", "category", "price", "stock");
Signal.effect(productGrid, () -> {
    productGrid.setItems(Objects.requireNonNullElseGet(
        filteredProductsSignal.get(), List::of));
});
```

The `Signal.effect()` method creates a component effect that runs whenever any signal accessed within it changes. The effect automatically tracks that it reads `filteredProductsSignal.get()` and reruns whenever the filtered products change. This establishes a reactive connection—whenever the computed signal recalculates (because any filter changed), the effect runs and updates the grid with the new filtered results.

## <a id="key-patterns"></a>Key Patterns

- **Signals for filter state**

  Use `ValueSignal` to track each independent filter criterion. This makes the filter state explicit and reactive.

- **Two-way binding with `bindValue()`**

  Connect filter components to signals using the pattern `component.bindValue(signal, signal::set)`. The first parameter is the signal (read using `signal.get()`), and the second is the write callback that is supposed to update the signal when the user changes the component, this callback can have a conditional logic that write the value if it’s valid, or does nothing otherwise.

- **Computed signals for derived data**

  Create a computed signal that reads all filter signals using `signal.get()` and produces the filtered dataset. This centralizes filtering logic and makes dependencies clear.

- **Component effects for grid updates**

  Use `Signal.effect()` to connect the grid to the filtered data signal. The effect automatically tracks signal access via `signal.get()` and reruns whenever those signals change, updating the grid’s items.

## <a id="when-to-use-this-pattern"></a>When to Use This Pattern

This pattern is ideal when:

- You have multiple filters that need to work together

- Filtering logic is moderately complex or involves multiple conditions

- You want client-side filtering for responsive interaction

- The dataset is small enough to filter in memory (typically under 10,000 items)

For larger datasets, consider server-side filtering with lazy loading. For single, simple filters, a direct event listener approach might be sufficient.

## <a id="related-topics"></a>Related Topics

- [Local Signals](https://vaadin.com/docs/next/flow/ui-state/local-signals.md) - Understanding ValueSignal and two-way binding

- [Effects and Computed Signals](https://vaadin.com/docs/next/flow/ui-state/effects-computed.md) - Creating derived values

- [Component Bindings](https://vaadin.com/docs/next/flow/ui-state/building-ui.md) - Binding signals to component properties

- [Grid](https://vaadin.com/docs/next/components/grid.md) - Reference documentation for Grid component
