> For the complete documentation index, see [llms.txt](https://docs.fact-finder.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fact-finder.com/webcomponents-overview/api/more-features/url-manipulation.md).

# URL Manipulation

Configure how Web Components reads search parameters from URLs and writes them back. Customize both directions whenever you change the parameter format.

## Documentation

The relevant two API contact points are:

* `factfinder.routing.setUrlParamOptionsListener(() => UrlParamMappingOptions)`
* `factfinder.utils.env.searchParamsFromUrl(parsingOptions)`

### Working with defaults

If you work with the default conversion, the only thing you will likely need is a method to translate the URL to *SearchParams* to be able to initiate a page's initial search request.

For this, there is the `factfinder.utils.env.searchParamsFromUrl()` helper that returns a valid *SearchParams* object. The translation from *SearchParams* to URL params is handled automatically by Web Components.

```js
document.addEventListener(`ffCoreReady`, ({ factfinder, init, initialSearch }) => {
    init({ ... });

    const searchParams = factfinder.utils.env.searchParamsFromUrl();

    initialSearch(searchParams);
});
```

#### `customParameters` are not processed by default

The FactFinder *SearchParams* object has a `customParameters` field. As these parameters are usually irrelevant to FactFinder and they are too generic to reasonably auto-translate to URL parameters, Web Components ignores this field by default.

If you must have `customParameters`, you can always manually add them to the *SearchParams* as well as you can customize the URL generation process to include them.

#### Preserving URL parameters unrelated to FactFinder

Web Components' default URL handling only considers parameters that are relevant to FactFinder. Unrelated parameters are ignored and discarded.

For an example on how to preserve these parameters, see `postStringifier` in the *UrlParamMappingOptions* section later on this page.

## SearchParams to URL params

When the response pipeline of a **search** or **navigation** request reaches the point where Web Components is about to write the latest *SearchParams* to the URL, the `setUrlParamOptionsListener` [pipeline hook](/webcomponents-overview/api/core-api/request-pipelines.md) is called. The listener's return value is a *UrlParamMappingOptions* object with various options to manipulate the resulting URL.

Registering this listener is **optional**. Typically, it is registered once during app initialization. All options are also **optional**. Specifying an option overrides the default value rather than appending to it.

```js
document.addEventListener(`ffCoreReady`, ({ factfinder, init, initialSearch }) => {
    init({ ... });

    // Registering the listener with its default options. This has the same effect as not registering it at all.
    factfinder.routing.setUrlParamOptionsListener(() => ({
        allow: [],
        block: [`activeAbTests`, `followSearch`, `purchaserId`, `showMarkets`, `showPermutedSearchParams`],
        blockFilters: [],
        categoryInfo: undefined,
        keyMapping: {},
        order: [`query`, `filters`, `sortItems`],
        postStringifier: undefined,
        stringifiers: {},
    }));
});
```

### UrlParamMappingOptions

The options are listed in the order they are applied to the incoming *SearchParams* object.

#### `allow`

`allow` specifies a list of search parameter names, as they appear on the *SearchParams* object used for the current request pipeline, that shall be considered for stringification. Search parameters that are not listed are discarded and will not appear in the URL.

If the list is empty, all parameters are considered.

#### `block`

`block` is a list of parameter names, as they appear on a *SearchParams* object, that shall not appear in the URL.

Blocked by default parameters are:

```js
[`activeAbTests`, `followSearch`, `purchaserId`, `showMarkets`, `showPermutedSearchParams`]
```

Specifying your own list of blocked parameters **removes** the default list. You therefore might want to add those parameters to your list.

#### `order`

`order` is a list of *SearchParams* keys that specifies which parameters shall appear **first** and in which order in the URL. Parameters that are not specified will appear in alphabetical order after the specified parameters.

If you do not specify an order, the default is:

```js
[`query`, `filters`, `sortItems`]
```

```js
// searchParams:
// { page: 2, query: "chisel" }

factfinder.routing.setUrlParamOptionsListener(() => ({ order: [`query`] }));

// URL: ?query=chisel&page=2
```

#### `blockFilters`

`blockFilters` works like `block` but targets individual filters from the `filters` search parameter. It takes a list of **filter names**.

This option can be useful if your URL path already contains a part of the filters. Frequently, this is the category filter. In this case, you don't want the category filter to appear again in the URL's query string.

```js
// URL with category filter: https://yourshop.com/powertools/woodworking?filter=input:230V

factfinder.routing.setUrlParamOptionsListener(() => ({
    // Assuming your category filter's field name is 'Category'.
    blockFilters: [`Category`],
}));
```

#### `categoryInfo`

`categoryInfo` enables you to further customize the blocking of the category filter. While `blockFilters` would block the entire category filter, `categoryInfo` allows you to only **partially** block the category filter's path.

{% hint style="info" %}
**Use case**

This option is only relevant in one specific scenario: on a category page that allows users to filter deeper into the page's sub-categories.

If your category page does not offer further category filtering, this option is unnecessary, and you better use the simpler `blockFilters` option for your category filter.
{% endhint %}

This option is only relevant for category pages where a part of the category filter is already encoded in the URL's path. For example:

```
www.your.shop/Clothing/Outdoor?filter=Category:Outdoor+jackets
```

Here, the fixed part is `['Clothing', 'Outdoor']` while the deeper, user selected part is `['Outdoor jackets']`. This should result in the complete category filter path of `['Clothing', 'Outdoor', 'Outdoor jackets']`.

The option's format is:

```js
{ fieldName: String, fixedPath: [String] }
```

* `fieldName` is the category's field name from your data feed.
* `fixedPath` is an array of strings representing the page's category path that the user cannot deselect.

Both values are the same that you specify during category page setup at `appConfig.categoryPage`.

**Example**

When you use this option, its setup always requires **three parts**:

* `appConfig.categoryPage`
* `factfinder.routing.setUrlParamOptionsListener`
* `factfinder.utils.env.searchParamsFromUrl`

Note how all three contact points use `categoryFieldName` and `fixedCategoryPath`.

**Always provide the same values as shown below!**

```js
document.addEventListener(`ffCoreReady`, ({ factfinder, init, initialSearch }) => {

    const categoryFieldName = `Your_category_field_name`;
    const fixedCategoryPath = [`The page's`, `category path`];

    const initOptions = {
        // ...
        appConfig: {
            // Setting the Web Components application to category page mode.
            categoryPage: factfinder.utils.filterBuilders.categoryFilter(categoryFieldName, fixedCategoryPath)
        },
    };

    // Setup for writing TO the URL.
    factfinder.routing.setUrlParamOptionsListener(() => ({
        // Not using `blockFilters` as it would block the entire category filter path.
        // Here, we only want to prevent the page's FIXED category path from appearing in the URL's query string.
        // blockFilters: [categoryFieldName],
        categoryInfo: { fieldName: categoryFieldName, fixedPath: fixedCategoryPath },
    }));

    // Reading FROM the URL.
    const searchParams = factfinder.utils.env.searchParamsFromUrl({
        categoryFieldName,
        fixedCategoryPath,
    });

    // Initialize the Web Components application.
    init(initOptions);

    // Trigger the initial search with the parameters that we just read from the URL.
    initialSearch(searchParams);
});
```

#### `stringifiers`

`stringifiers` allows you to define custom stringifiers. It is the counterpart to `searchParamsFromUrl`'s `preparsers`. It takes an object with *SearchParams* keys and the stringifier functions.

In addition to the parameter **values**, stringifier functions also define the parameter **names** that shall appear in the URL. Although these names can be manipulated again in later steps of the process.

When you don't define stringifiers, Web Components' built-in stringifiers will produce the parameter names that are defined in the REST API's parameter list of the related GET request of `/search` or `/navigation`. Apart from a few exceptions, these are identical to the keys in the *SearchParams* object.

The following example shows how one could merge the two-component `location` parameter into one. Instead of producing two URL parameters, `latitude` and `longitude`, it will only output one `loc` parameter.

The section on `searchParamsFromUrl` contains an example of the reverse action.

```js
// searchParams:
// { query: "atlantis" , location: { latitude: 30.8, longitude: -42.8 } }

factfinder.routing.setUrlParamOptionsListener(() => ({

    keyMapping: { latitude: `loc` },

    stringifiers: {
        location: (addParam, location) => {
            // All stringifiers receive two arguments.
            // An `addParam` function and the SearchParams value that is to be stringified.
            // - `addParam(value, key?)` is a function that takes the stringified value and an optional output key.
            // - `location` is the value from the _SearchParams_ object that is to be stringified.

            const value = `${location.latitude}:${location.longitude}`;

            // Using `latitude` as a temporary output. It will later be renamed by the `keyMapping`.
            // Attention: While it is possible to immediately specify the final key `loc` here and skip the `keyMapping`,
            // during URL parsing you will have to map the custom key back to a known key such as `latitude`.
            // See later sections about `keyMapping` and URL parsing.
            const key = `latitude`;

            // `key` is optional. If you don't specify it, the related _SearchParams_ key is used.
            // Here it would be "location".
            addParam(value, key);

            // You can call `addParam` as many times as you require.

            // If you wanted to produce the same result as the default `location` stringifier,
            // you would call `addParam` once for each location component.

            // addParam(location.latitude, `latitude`);
            // addParam(location.longitude, `longitude`);
        },
    },
}));

// URL: ?query=atlantis&loc=30.8:-42.8
```

#### `postStringifier`

`postStringifier` takes a function that receives an array of ordered and stringified key/value pairs and returns an array of the same format.

Each key/value pair the post-stringifier receives represents one URL parameter as it is about to be written to the browser's URL.

```js
// query: jacket
// filters: size=L, features=waterproof

factfinder.routing.setUrlParamOptionsListener(() => ({
    postStringifier: pairs => {
        // Current contents of `pairs`:
        // [
        //   { key: "query", value: "jacket" },
        //   { key: "filter", value: "size:L" },
        //   { key: "filter", value: "features:waterproof" },
        // ]

        // Manipulate `pairs` as required. E.g. filter, reorder, add additional pairs, etc.
        pairs.unshift({ key: `custom`, value: `pole-position` });

        // Return the manipulated `pairs` array or a new array with the same format.
        return pairs;
    },
}));

// URL: ?custom=pole-position&query=jacket&filter=size:L&filter=features:waterproof
```

**Example of how to preserve non-FactFinder URL parameters**

As parameters unknown to FactFinder are discarded by default, you can use the `postStringifier` to re-add them before Web Components overwrites the URL.

```js
factfinder.routing.setUrlParamOptionsListener(() => ({
    postStringifier: pairs => {
        const additionalPairs = [];
        new URLSearchParams(window.location.search).entries().forEach(([key, value]) => {
            if (!pairs.find(p => p.key === key)) {
                additionalPairs.push({ key, value });
            }
        });
        return [...pairs, ...additionalPairs];
    },
}));
```

#### `keyMapping`

`keyMapping` is an object with which you can specify how URL parameters shall be renamed.

The *keys* in the object are the parameter names that were produced by the stringifiers and, potentially, modified by the post-stringifier.

The *values* are the parameter names as you want them to **actually appear** in the URL.

{% tabs %}
{% tab title="Without mapping" %}

```js
// SearchParams
// { query: "atlantis" , location: { latitude: 30.8, longitude: -42.8 } }

factfinder.routing.setUrlParamOptionsListener(() => ({
    keyMapping: {},
}));

// URL: ?query=atlantis&latitude=30.8&longitude=-42.8
```

{% endtab %}

{% tab title="With mapping" %}

```js
// SearchParams
// { query: "atlantis" , location: { latitude: 30.8, longitude: -42.8 } }

factfinder.routing.setUrlParamOptionsListener(() => ({
    keyMapping: {
        query: `q`,
        latitude: `lat`,
        longitude: `lon`,
    },
}));

// URL: ?q=atlantis&lat=30.8&lon=-42.8
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Important**

The `keyMapping` here is the same as in the *parsing options* of the counterpart `factfinder.utils.env.searchParamsFromUrl(parsingOptions)`.

Always use the same mapping object!
{% endhint %}

```js
const keyMapping = { query: `q` };

factfinder.routing.setUrlParamOptionsListener(() => ({ keyMapping }));

initialSearch(factfinder.utils.env.searchParamsFromUrl({ keyMapping }));
```

#### Flowchart

Below is a flowchart of how the options are applied while the *SearchParams* object is processed into a URL string. It also highlights the relations between the various parameters that appear in the whole chain.

It might look intimidating as a whole. Look at it step by step while reading the related descriptions above.

On the left, there is an example *SearchParams* object. On the right are the available options with example values. These values are chosen to demonstrate their effect. They don't necessarily make much sense in a real world scenario.

Below each step, a URL is shown that would be the final result if you had only specified the options up until that point:

* No options
* Only `allow`
* `allow` and `block`
* `allow`, `block`, and `order`
* etc.

Remember that no option is required, and any combination of options is allowed.

Also note that even though subsequent options are considered unspecified at each station, Web Components still has default values that will be applied in this case.

## URL params to SearchParams

Converting URL params to *SearchParams* is done on page load in order to produce the initial search request based on the current URL params. It must be done manually in the `ffCoreReady` event handler.

```js
document.addEventListener(`ffCoreReady`, ({ factfinder, init, initialSearch }) => {
    const searchParams = factfinder.utils.env.searchParamsFromUrl(parsingOptions);

    initialSearch(searchParams);
});
```

### Parsing options

`searchParamsFromUrl` converts the query string from the current URL to a valid *SearchParams* object that you can use to issue a **search** request.

{% hint style="info" %}
If you need a *NavigationParams* object, you can use the `factfinder.utils.toNavigationParams(searchParams)` conversion function.
{% endhint %}

All options to `searchParamsFromUrl` are **optional**.

```js
const searchParams = factfinder.utils.env.searchParamsFromUrl({
    block,              // array of strings
    categoryFieldName,  // string
    fixedCategoryPath,  // array of strings
    keyMapping,         // object
    preparsers,         // object
});
```

#### `block`

`block` is an array of strings where you can specify search parameters that shall not appear in the *SearchParams* object.

Note that these are the parameter names as they appear in the resulting *SearchParams* object and not as they appear in the URL.

```js
// URL: ?query=atlantis&latitude=30.8&longitude=-42.8

factfinder.utils.env.searchParamsFromUrl();
// { query: "atlantis" , location: { latitude: 30.8, longitude: -42.8 } }

factfinder.utils.env.searchParamsFromUrl({ block: [`location`] });
// { query: "atlantis" }
```

#### `categoryFieldName`

`categoryFieldName` is the field name of your category facet. While technically optional, **you should always specify it** because the category filter must be constructed in a different format than regular filters.

Web Components can't discern from URL parameters alone which filter is a category filter and which is not.

You can find the correct field name in your data feed or, in a search response, in the category facet object's `associatedFieldName` field.

```js
factfinder.utils.env.searchParamsFromUrl({ categoryFieldName: `Category` });
```

#### `fixedCategoryPath`

`fixedCategoryPath` can be used when you are implementing a **category page** that allows users to filter deeper into the page's sub-categories. It takes an array of strings that is the page's fixed category path.

It is the *same array* that you use to set up the category page during app configuration and also provide to `factfinder.routing.setUrlParamOptionsListener`.

If you specify this option, you must also specify `categoryFieldName`.

See the related `categoryInfo` section in *SearchParams to URL params* for details and examples.

```js
const searchParams = factfinder.utils.env.searchParamsFromUrl({
    categoryFieldName: `Category`,
    fixedCategoryPath: [`Clothing`, `Outdoor`],
});
```

#### `keyMapping`

`keyMapping` is an object with which you can specify how URL parameters have been renamed.

The *keys* in the object are the **default names** as Web Components would write them to the URL. The *values* are the parameter names as they **actually appear** in the URL.

```js
// URL: ?q=atlantis&lat=30.8&lon=-42.8

factfinder.utils.env.searchParamsFromUrl({ keyMapping: { query: `q`, latitude: `lat`, longitude: `lon` } });
// { query: "atlantis" , location: { latitude: 30.8, longitude: -42.8 } }
```

{% hint style="warning" %}
**Important**

Always use the same mapping object that you do for `factfinder.routing.setUrlParamOptionsListener`.
{% endhint %}

If you do not specify `keyMapping`, the following default mapping is used. All other parameters appear as they are in the *SearchParams* object.

```js
{
    activeAbTests: [`activeAbTest`],
    filters: [`filter`, `substringFilter`],
    location: [`latitude`, `longitude`],
    marketIds: [`marketId`],
    sortItems: [`sort`],
}
```

#### `preparsers`

`preparsers` takes an object that lets you define functions to process URL parameters before they are converted to *SearchParams*.

This is necessary when you are using URL parameters in a format that Web Components doesn't understand. The preparsers must translate them back into the Web Components-compatible format.

The *keys* of the object that you pass to `preparsers` are the same as those in a *SearchParams* object. The *values* are each a function that receives a list of matched parameters.

Only the preparser for the `filter` parameter receives a second argument: the `categoryFieldName`. The function then returns an array of key/value pairs.

**Pre-parser examples**

Here are two examples of possible `preparsers`: a simple one (`articleNumberSearch`) and a more complex one (`location`).

You can find the reverse action of the `location` example in the `setUrlParamOptionsListener` section.

```js
// URL: ?ans=A&loc=30.8:-42.8

const searchParams = factfinder.utils.env.searchParamsFromUrl({

    // Translates 'ans' from the URL to 'articleNumberSearch'
    // and 'loc' to 'latitude'.
    keyMapping: { articleNumberSearch: `ans`, latitude: `loc` },

    preparsers: {
        // `matchedEntries` has the format: [{ key: "keyAfterMapping", values: ["value1", "value2"] }]
        articleNumberSearch: (matchedEntries) => {
            const singleEntry = matchedEntries[0];
            const val = singleEntry.values[0].toUpperCase();
            singleEntry.values[0] = val === `A` ? `ALWAYS` : val === `N` ? `NEVER` : `DETECT`;

            // Must return an array in the same format as `matchedEntries`.
            return [singleEntry];
        },

        location: (matchedEntries) => {
            const entry = matchedEntries.find(({ key }) => key === `latitude`);
            const [lat, lon] = entry.values[0].split(`:`);

            // The `location` parser expects both `latitude` and `longitude` parameters.
            return [
                { key: `latitude`, values: [lat] },
                { key: `longitude`, values: [lon] },
            ];
        },
    },
});

// searchParams:
// {
//   articleNumberSearch: "ALWAYS",
//   location: { latitude: 30.8, longitude: -42.8 }
// }
```

**Details of the `articleNumberSearch` example**

In the URL, the parameter appears as `ans=A`.

The presupposition is that the name `articleNumberSearch` is too long for a pleasing appearance in the URL and you shortened it. Likewise, the possible values defined by FactFinder (`ALWAYS`, `NEVER`, `DETECT`) are too long and are shortened, too.

As Web Components does not know the parameter `ans`, it has to be mapped to one of the known parameters. This happens in `keyMapping: { articleNumberSearch: 'ans' }`.

With the mapping defined, all URL parameters with the key `ans` will be passed to the `articleNumberSearch` preparser. The preparser then converts the customized value `A` to the FactFinder-defined `ALWAYS`.

```js
articleNumberSearch: (matchedEntries) => {
    // matchedEntries === [{ key: "articleNumberSearch", values: ["A"] }]

    // Translate values to expected format (see above).

    // Return modified `matchedEntries` or a new array with the same format (see above).
}
```

**Details of the `location` example**

In the URL, the parameter appears as `loc=30.8:-42.8`.

`location` normally appears as two parameters, `latitude` and `longitude`, in the URL. In this example, only a single parameter with a custom format is used.

The internal parser for `location` requires both `latitude` and `longitude` parameters in order to produce a valid `location` object. As there is only a single parameter in the URL, simple key mapping is insufficient, and the custom value must be split into its components.

First, `keyMapping: { latitude: 'loc' }` translates the custom `loc` to the known `latitude`, which causes the `location` parser to be invoked. However, this is incomplete data because the `longitude` parameter is missing. The `location` **preparser** must therefore convert the custom value and add the missing parameter.

```js
preparser: {
    location: (matchedEntries) => {
        // matchedEntries === [{ key: "latitude", values: ["30.8:-42.8"] } }]

        const entry = matchedEntries.find(entry => entry.key === `latitude`);
        // entry === { key: "latitude", values: ["30.8:-42.8"] } }

        // Splitting the custom value into its components.
        const [lat, lon] = entry.values[0].split(`:`);

        // Return an array with the entries required by Web Components' parser.
        return [
            { key: `latitude`, values: [lat] },
            { key: `longitude`, values: [lon] },
        ];
    },
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.fact-finder.com/webcomponents-overview/api/more-features/url-manipulation.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
