# Route Utils

*Category:* Utilities
*Section:* Helper
*Source:* https://uikit.developers.rio.cloud/#/utilities/routeUtils
*Captured:* 2026-04-27T14:59:28.978Z

Utility helpers for parsing and serializing URL state. These are low-level building blocks for service-specific mapping.

## Route Utils

For end-to-end URL sync (URL - store + store - URL), prefer the useUrlState hook. Use the route utils on this page when you need custom parse/serialize logic for specific values.

For history behavior guidance (pushState vs. replaceState), see the State in URL guideline .

If you use UrlConfigItem with useUrlState, set an explicit defaultValue for every key so removed params can reliably reset state.

## Route utils overview

| Export | Good for |
| --- | --- |
| getSortDirShort | Serialize sort direction into compact empty / - prefixes. |
| serializeSorting | Serialize sort state into a compact URL value like -name. |
| parseSorting | Parse +name / -name into sortBy and sortDir values. |
| mapColumnsSettingsToStrings | Serialize table column order, visibility, and widths into compact strings. |
| parseColumnsSettingsStrings | Parse compact column strings back into column settings with defaults. |
| serializeValue | Serialize primitive/array/object values to URL-compatible strings. |
| deserializeValue | Parse URL strings into typed values (number, boolean, arrays, object). |
| toQueryString | Build readable query strings (keeps commas/colons readable for complex values). |
| isEqual | Deep compare helper re-exported for state diffing in URL sync code. |
| UrlTypeMap, UrlValueType, UrlConfigItem | Core typing primitives for typed URL config definitions, including mandatory defaults and history behavior. |

## Simple state in URL

Example for manual parse/stringify helpers in a service file.

### Example: Example 1

import { deserializeValue , serializeValue } from '@rio-cloud/rio-uikit/routeUtils' ; // Adapt to your configuration const BASE_PATH = 'my-service' ; const VIEW_OPTIONS = [ 'list' , 'grid' ] as const ;

type ViewType = ( typeof VIEW_OPTIONS ) [ number ] ; const DEFAULT_VIEW : ViewType = 'list' ; const DEFAULT_TAB_ID = 1 ;

type RouterLike = { location ? : Location } ;
type RouteState = { query : string ; view : ViewType ; tabId : number ; tags : string [ ] ; showArchived : boolean ; } ; const isViewType = ( value : string ) : value is ViewType => ( VIEW_OPTIONS as readonly string [ ] ) . includes ( value ) ; /**
* Reads URL state from a router-like object.
* Useful when you want a lightweight parser without a hook.
*
* Note: Optional service helper when you only need a one-off parse.
* For reactive state sync, use the `useUrlState` hook instead.
*
* @example
* const state = parseRoute({ location: window.location });
*/ export const parseRoute = ( router : RouterLike ) : RouteState => { const location = ( router ?. location ?? { } ) as Location ; const params = new URLSearchParams ( location . search || '' ) ; const parsedView = deserializeValue ( params . get ( 'view' ) , 'string' ) ; const parsedTabId = deserializeValue ( params . get ( 'tabId' ) , 'number' ) ; const parsedTags = deserializeValue ( params . get ( 'tags' ) , 'array' ) ; const parsedArchived = deserializeValue ( params . get ( 'archived' ) , 'boolean' ) ; return { query : deserializeValue ( params . get ( 'q' ) , 'string' ) ?? '' , view : parsedView && isViewType ( parsedView ) ? parsedView : DEFAULT_VIEW , tabId : parsedTabId ?? DEFAULT_TAB_ID , tags : parsedTags ?? [ ] , showArchived : parsedArchived ?? false , } ; } ; /**
* Builds a deep-link URL for a given state.
* Useful for linking to a screen with preselected filters.
*
* Note: Optional service helper when you only need to build a link.
* For reactive state sync, use the `useUrlState` hook instead.
*
* @example
* const url = makeRoute({ query: 'rio', view: 'list', tabId: 2, tags: ['banana'], showArchived: false });
*/ export const makeRoute = ( state : RouteState ) => { const params = new URLSearchParams ( ) ; const query = serializeValue ( state . query , 'string' ) ; if ( query ) {
params . set ( 'q' , query ) ; } if ( state . view !== DEFAULT_VIEW ) { const view = serializeValue ( state . view , 'string' ) ; if ( view ) {
params . set ( 'view' , view ) ; } } if ( state . tabId !== DEFAULT_TAB_ID ) { const tabId = serializeValue ( state . tabId , 'number' ) ; if ( tabId ) {
params . set ( 'tabId' , tabId ) ; } } const tags = serializeValue ( state . tags , 'array' ) ; if ( tags ) {
params . set ( 'tags' , tags ) ; } if ( state . showArchived ) { const archived = serializeValue ( state . showArchived , 'boolean' ) ; if ( archived ) {
params . set ( 'archived' , archived ) ; } } const queryString = params . toString ( ) ; return ` ${ BASE_PATH } / ${ queryString ? ` ? ${ queryString } ` : '' } ` ; } ;

#### Code (text)

```text
import { deserializeValue, serializeValue } from '@rio-cloud/rio-uikit/routeUtils';

// Adapt to your configuration
const BASE_PATH = 'my-service';
const VIEW_OPTIONS = ['list', 'grid'] as const;

type ViewType = (typeof VIEW_OPTIONS)[number];
const DEFAULT_VIEW: ViewType = 'list';
const DEFAULT_TAB_ID = 1;

type RouterLike = { location?: Location };
type RouteState = {
    query: string;
    view: ViewType;
    tabId: number;
    tags: string[];
    showArchived: boolean;
};

const isViewType = (value: string): value is ViewType => (VIEW_OPTIONS as readonly string[]).includes(value);

/**
 * Reads URL state from a router-like object.
 * Useful when you want a lightweight parser without a hook.
 *
 * Note: Optional service helper when you only need a one-off parse.
 * For reactive state sync, use the `useUrlState` hook instead.
 *
 * @example
 * const state = parseRoute({ location: window.location });
 */
export const parseRoute = (router: RouterLike): RouteState => {
    const location = (router?.location ?? {}) as Location;
    const params = new URLSearchParams(location.search || '');

    const parsedView = deserializeValue(params.get('view'), 'string');
    const parsedTabId = deserializeValue(params.get('tabId'), 'number');
    const parsedTags = deserializeValue(params.get('tags'), 'array');
    const parsedArchived = deserializeValue(params.get('archived'), 'boolean');

    return {
        query: deserializeValue(params.get('q'), 'string') ?? '',
        view: parsedView && isViewType(parsedView) ? parsedView : DEFAULT_VIEW,
        tabId: parsedTabId ?? DEFAULT_TAB_ID,
        tags: parsedTags ?? [],
        showArchived: parsedArchived ?? false,
    };
};

/**
 * Builds a deep-link URL for a given state.
 * Useful for linking to a screen with preselected filters.
 *
 * Note: Optional service helper when you only need to build a link.
 * For reactive state sync, use the `useUrlState` hook instead.
 *
 * @example
 * const url = makeRoute({ query: 'rio', view: 'list', tabId: 2, tags: ['banana'], showArchived: false });
 */
export const makeRoute = (state: RouteState) => {
    const params = new URLSearchParams();

    const query = serializeValue(state.query, 'string');
    if (query) {
        params.set('q', query);
    }

    if (state.view !== DEFAULT_VIEW) {
        const view = serializeValue(state.view, 'string');
        if (view) {
            params.set('view', view);
        }
    }

    if (state.tabId !== DEFAULT_TAB_ID) {
        const tabId = serializeValue(state.tabId, 'number');
        if (tabId) {
            params.set('tabId', tabId);
        }
    }

    const tags = serializeValue(state.tags, 'array');
    if (tags) {
        params.set('tags', tags);
    }

    if (state.showArchived) {
        const archived = serializeValue(state.showArchived, 'boolean');
        if (archived) {
            params.set('archived', archived);
        }
    }

    const queryString = params.toString();
    return `${BASE_PATH}/${queryString ? `?${queryString}` : ''}`;
};
```

## TableSettingsDialog state

Example for compact sort + table settings mapping with dedicated helpers.

Example: http://my-url/#demoService/table?sort=userNickName&columns=itemId,userImageStatus,userNickName:278,userFirstName,userLastName,userActivity,userJobTitle,userEmail,userPhoneNumber

### Example: Example 2

import {
deserializeValue ,
mapColumnsSettingsToStrings ,
parseColumnsSettingsStrings ,
parseSorting ,
serializeSorting ,
serializeValue , } from '@rio-cloud/rio-uikit/routeUtils' ; import type { SortDirectionType } from '@rio-cloud/rio-uikit/SortUtils' ; import type { TableColumnsSettings } from '@rio-cloud/rio-uikit/TableSettingsDialog' ; // TableSettingsDialog state example. // Adapt to your configuration const BASE_PATH = 'my-service' ; const defaultColumnsDetails : Record < string , { width : number ; defaultWidth ? : number } > = { name : { width : 250 , defaultWidth : 250 , } , } ; // Sample code for parsing the route just for illustration how to use the utils functions // Feel free to modify this to your needs. // Note: the "parseRoute" and "makeRoute" functions may contain more props which are left out in this demo
type RouterLike = { location ? : Location } ; /**
* Reads table settings state from a router-like object.
*
* Note: Optional service helper when you only need a one-off parse.
* For reactive state sync, use the `useUrlState` hook instead.
*
* @example
* const state = parseRoute({ location: window.location });
*/ export const parseRoute = ( router : RouterLike ) => { const location = ( router ?. location ?? { } ) as Location ; const params = new URLSearchParams ( location . search || '' ) ; const sortValue = deserializeValue ( params . get ( 'sort' ) , 'string' ) ; const sorting = sortValue ? parseSorting ( sortValue ) : undefined ; const columnsList = deserializeValue ( params . get ( 'columns' ) , 'array' ) ; const columns = columnsList ? parseColumnsSettingsStrings ( columnsList , defaultColumnsDetails ) : undefined ; return { sortBy : sorting ?. sortBy , sortDir : sorting ?. sortDir ,
columns , } ; } ;

type mapRouteProps = { sortBy : string ; sortDir : SortDirectionType ; columns : TableColumnsSettings ; } ; /**
* Builds a deep-link URL for a given table settings state.
*
* Note: Optional service helper when you only need to build a link.
* For reactive state sync, use the `useUrlState` hook instead.
*
* @example
* const url = makeRoute({ sortBy: 'name', sortDir: 'asc', columns });
*/ export const makeRoute = ( { sortBy , sortDir , columns } : mapRouteProps ) => { const params = new URLSearchParams ( ) ; if ( sortBy ) { // Use serializeSorting for compact empty / - prefix behavior. const serializedSort = serializeSorting ( { sortBy , sortDir } ) ; if ( serializedSort ) {
params . set ( 'sort' , serializedSort ) ; } } const mappedColumns = mapColumnsSettingsToStrings ( columns ) ; const serializedColumns = serializeValue ( mappedColumns , 'array' ) ; if ( serializedColumns ) {
params . set ( 'columns' , serializedColumns ) ; } const queryString = params . toString ( ) ; const searchFragment = queryString ? ` ? ${ queryString } ` : '' ; return ` ${ BASE_PATH } / ${ searchFragment } ` ; } ;

#### Code (text)

```text
import {
    deserializeValue,
    mapColumnsSettingsToStrings,
    parseColumnsSettingsStrings,
    parseSorting,
    serializeSorting,
    serializeValue,
} from '@rio-cloud/rio-uikit/routeUtils';
import type { SortDirectionType } from '@rio-cloud/rio-uikit/SortUtils';
import type { TableColumnsSettings } from '@rio-cloud/rio-uikit/TableSettingsDialog';

// TableSettingsDialog state example.
// Adapt to your configuration
const BASE_PATH = 'my-service';
const defaultColumnsDetails: Record<string, { width: number; defaultWidth?: number }> = {
    name: {
        width: 250,
        defaultWidth: 250,
    },
};

// Sample code for parsing the route just for illustration how to use the utils functions
// Feel free to modify this to your needs.
// Note: the "parseRoute" and "makeRoute" functions may contain more props which are left out in this demo
type RouterLike = { location?: Location };

/**
 * Reads table settings state from a router-like object.
 *
 * Note: Optional service helper when you only need a one-off parse.
 * For reactive state sync, use the `useUrlState` hook instead.
 *
 * @example
 * const state = parseRoute({ location: window.location });
 */
export const parseRoute = (router: RouterLike) => {
    const location = (router?.location ?? {}) as Location;
    const params = new URLSearchParams(location.search || '');

    const sortValue = deserializeValue(params.get('sort'), 'string');
    const sorting = sortValue ? parseSorting(sortValue) : undefined;

    const columnsList = deserializeValue(params.get('columns'), 'array');
    const columns = columnsList ? parseColumnsSettingsStrings(columnsList, defaultColumnsDetails) : undefined;

    return {
        sortBy: sorting?.sortBy,
        sortDir: sorting?.sortDir,
        columns,
    };
};

type mapRouteProps = {
    sortBy: string;
    sortDir: SortDirectionType;
    columns: TableColumnsSettings;
};

/**
 * Builds a deep-link URL for a given table settings state.
 *
 * Note: Optional service helper when you only need to build a link.
 * For reactive state sync, use the `useUrlState` hook instead.
 *
 * @example
 * const url = makeRoute({ sortBy: 'name', sortDir: 'asc', columns });
 */
export const makeRoute = ({ sortBy, sortDir, columns }: mapRouteProps) => {
    const params = new URLSearchParams();

    if (sortBy) {
        // Use serializeSorting for compact empty / - prefix behavior.
        const serializedSort = serializeSorting({ sortBy, sortDir });
        if (serializedSort) {
            params.set('sort', serializedSort);
        }
    }

    const mappedColumns = mapColumnsSettingsToStrings(columns);
    const serializedColumns = serializeValue(mappedColumns, 'array');
    if (serializedColumns) {
        params.set('columns', serializedColumns);
    }

    const queryString = params.toString();
    const searchFragment = queryString ? `?${queryString}` : '';

    return `${BASE_PATH}/${searchFragment}`;
};
```