Creating a writable svelte store from loaded page data in sveltekit

700 views Asked by At

Using sveltekit with svelte 4, There's data required to server side render pages that is loaded in the root +layout.server.js. The data is then used both on the client and server side, however, I need a way to edit the data after the page is loaded and the $page store is readonly. If I import a store and .set() it inside the root +layout.svelte, then all users/requests will trigger updates to the store on the server side, which is bad.

So how do I go about creating a store, or simple object, that is instantiated in the root server layout, which can be edited on the client side later?

I tried to use depends('data:rates') but that makes all the nested routes rerun their load functions and it still doesn't let me have fine-grained control on the data and timing from the client.

Note that a store limited to one file would work, but this store/object needs to be imported by dozens of locations across the site.

Thanks.

1

There are 1 answers

1
Oscar Hermoso On BEST ANSWER

Assuming your +layout.server.js looks something like this:

export const load = async ({ fetch }) => {
  const response = await fetch(`/api/rates`);
  const rates = await response.json();
  
  return { rates: rates }
}

Two ways to solve this problem in my experience:

1. Create a store in +layout.js

In an adjacent client-side +layout.js, return a writable:

import { writable } from 'svelte/store';

export const load = async ({ parent, data }) => {
    return {
        ...(await parent()),
        ...data,
        rates: writable(data.rates),
    };
};

Child pages can use the store with data.rates

<script>
  // +page.svelte
  export let data;

  $: rates = data.rates;

  // or; in other components
  // import { page } from '$app/stores'

  // $: rates = $page.data.rates;

  async function refreshRates = () => {
    $rates = await fetch(`/api/rates`);
  }
</script>

<button on:click={refreshRates}>Refresh</button>

2. Create a store in +layout.svelte

This example is also given in the Svelte docs:

<script>
  import { onMount, setContext } from 'svelte';
  import { writable } from 'svelte/store';

  const rates = writable(null);
  $: setContext('rates', rates);
  $: rates.set(data.session);
</script>

<slot/>

Then, in a child page/component:

<script>
  import { setContext } from 'svelte';

  const rates = getContext('rates', rates);

  async function refreshRates = () => {
    $rates = await fetch(`/api/rates`);
  }
</script>

<button on:click={refreshRates}>Refresh</button>