Using query strings the SvelteKit way

A common scenario where the query string paramers should be saved in when you have a page with tabs, for instance a settings page.

In such a scenario, the selected tab should be saved in the query string so that, when the user presses the back / forward buttons of the browser, they go to the corresponding tab.

Traditionally, you would use the popstate event to listen to changes in the browser URL and then parse the query string using URLSearchParams.  SvelteKit makes this easier through the use of stores:

  1. Setting a query string parameter is done using $page.url.searchParams.set
  2. Updating the query string in the browser is do using the standard goto functions from $app/navigation
  3. When the query string changes (e.g., the user presses Back), the application can be notified by subscribing to the page store.




Full code below:

<script lang="ts">
    import { goto } from '$app/navigation';
    import { page } from '$app/stores';
    import { onDestroy, onMount } from 'svelte';
    import type { Unsubscriber } from 'svelte/store';
 
    let unsubscriber: Unsubscriber;

    onMount(() => {
        unsubscriber = page.subscribe(onPageChanged);
    });

    onDestroy(() => {
        unsubscriber && unsubscriber();
    });

    function onPageChanged(v: any) {    
        console.log('Page changed', $page.url.searchParams.get('tab'));
    }

    function setTab(tab: number) {
        $page.url.searchParams.set('tab', `${tab}`);
        goto(`?${$page.url.searchParams.toString()}`);
    }
</script>

<ul>
    <li class:selected={$page.url.searchParams.get('tab') == "1"} on:click={() => setTab(1)} on:keydown={() => setTab(1)}>
        Tab 1
    </li>
    <li class:selected={$page.url.searchParams.get('tab') == "2"} on:click={() => setTab(2)} on:keydown={() => setTab(2)}>
        Tab 2
    </li>
    <li class:selected={$page.url.searchParams.get('tab') == "3"} on:click={() => setTab(3)} on:keydown={() => setTab(3)}>
        Tab 3
    </li>
</ul>

<style>
    .selected {
        color: red;
    }
</style>

Comments