Managing Filters In the URL in React: A Practical Guide - Part 2

Written by Lucie Zdeňková on 2025-12-04

reactjavascript

In our last post Managing Filters In the URL in React: A Practical Guide - Part 1, we shared how to synchronize filter values with the URL on the simpliest example with full-text filter. In today's post, we will explore, how to alter those function to handle multi-select or from-to filters values in the URL. Let's get into it.


Multi-select

Multi-select is useful in various cases, when you aim to allow the choice of several options. Like in full-text filter mentioned in previous blog, we are using similar logic in following functions - read from URL, write to URL and merge those to update the URL. Our multi-select value is dictionary such as {1: true, 2:false, 3:true}, where the key is id of the option and the value is boolean representing whether option is checked.

1. Extract function

In the case of a multi-select filter, the URL can contain multiple values under the same parameter name, for example: www.example.com/documents?status=2&status=3&status=4&status=5&status=6.

The purpose of the extraction function is to read all of these values and translate them into a dictionary format. The algorithm steps are:

  • collecting all occurrences of the parameter using params.getAll(key),
  • turning them into a Set for lookup,
  • iterating through all possible filter ids,
  • and producing an object where each id maps to a boolean (true is selected, false is not selected).

If no values are present in the URL, the function defaults to selecting all options.

function extract_field_from_params_multiselect(params, key, def_dict = "") {
    const selected = new Set(params.getAll(key));
    const allIds = Object.keys(def_dict);
    const val = Object.fromEntries(allIds.map((id) => [id, selected.size === 0 || selected.has(id)]));
    return val;
}

2. Update filter value in the URL

Update function first deletes all occurrences of the mentioned param status. Then it rewrites new value of the multi-select filter to the URL by looping through the dictionary of selected values and appending each active option back into the query string.

function set_field_to_params_multiselect(params, key, val) {
    const newParams = new URLSearchParams(params);
    newParams.delete(key);
    Object.entries(val).forEach(([id, checked]) => {
        if (checked) {
            newParams.append("status", id);
        }
    });
    return newParams;
}

3. Final update function

Final function that merges the previous ones follows the same logic as in the full-text filter case in previous blog. The function returns a [val, setter] pair that behaves similarly to React’s useState. Thanks to this, handling filters stored in the URL feels just like using React useState hook, but with the benefit that the URL remains the single source of truth.

export function use_params_field_multiselect(params, setParams, key, def_dict = "") {
    const val = extract_field_from_params_multiselect(params, key, def_dict);
    const setter = (val) => {
        setParams(set_field_to_params_multiselect(params, key, val));
    }
    return [val, setter];
}

From-to range filter

From-to range filters are very useful when it comes to numbers. We store the value in an array ["", ""].

1. Extract function

This helper retrieves a range filter (typically something like a date range or numeric interval) from the URL. Instead of storing both values under a single query parameter, the range is split into two keys following a consistent naming pattern: key_from and key_to.

The function builds the expected parameter names (e.g., price_from, price_to), it reads all occurrences of each key from the URL. If no values are found, it falls back to a default values ["", ""]. It returns a two-element array [fromValue, toValue] that you can directly use in your component state.

function extract_range_field_from_params(params, key, defval = ["", ""]) {
    const keyFrom = key + "_from";
    const keyTo = key + "_to";
    const valsFrom = params.getAll(keyFrom);
    const valsTo = params.getAll(keyTo);
    const valFrom = valsFrom.length === 0 ? defval[0] : valsFrom[0];
    const valTo = valsTo.length === 0 ? defval[1] : valsTo[0];
    return [valFrom, valTo];
}

2. Update filter value in the URL

This function updates the URL so that it reflects the current state of a range filter.

It generates the appropriate parameter names: key_from and key_to. A fresh copy of the current URL parameters is created, followed by deletion of any existingsting values for the range filter. The new range values are appended in the correct order: first the from value, then the to value. Finally, it returns an updated URLSearchParams instance ready to be pushed into the URL.

function set_range_field_to_params(params, key, val) {
    const keyFrom = key + "_from";
    const keyTo = key + "_to";
    const newParams = new URLSearchParams(params);
    newParams.delete(keyFrom);
    newParams.delete(keyTo);
    newParams.append(keyFrom, val[0]);
    newParams.append(keyTo, val[1]);
    return newParams;
}

3. Final update function

Final function to use has again same logic as previously, meanwhile being easy to use thanks to the [val, setter] pair.

export function use_params_range_field(params, setParams, key, defval = ["", ""]) {
    const val = extract_range_field_from_params(params, key, defval);
    const setter = (val) => {
        setParams(set_range_field_to_params(params, key, val));
    }
    return [val, setter];
}

Implementation in components

Simply call the function in your component and don't forget to use the values multiSelectStatus amountFilter to white a filter function for your actual data.

const [multiSelectStatus, setMultiSelectStatus] = use_params_field_multiselect(searchParams, setSearchParams, "status", {1: true, 2: true, 3: true});
const [amountFilter, setAmountFilter] = use_params_range_field(searchParams, setSearchParams, "amount");

In this blog post, we shared how to handle update of filter in URL for two very common filter use cases - multi-select and from-to range filter. We hope this blog was useful, happy coding and see ya next time!