Passing data from layouts to pages in SvelteKit
I’m prototyping Basil, the free and open hosting client that’s going to power small-web.org, in SvelteKit and one thing I want to ensure from the outset is that the app is not hardcoded for our use so that anyone can easily set up a Small Web host simply by installing and configuring it.
Now, ideally, I want that configuration data to be loaded and rendered on the server (SSR/SSG) once.
Given that you cannot use Node APIs in layouts/pages even when they’re rendered on the server, the way to do this is to use an endpoint and consume it on the server in your main layout and then pass the data to any pages the layout has slotted into it using the context.
To cut a long story short, I struggled a bit to get this running initially as there are two separate “context” concepts in SvelteKit1 and I couldn’t find any code samples when I was starting out with it. So here’s the code sample I wish I had.
Remember that SvelteKit is in beta and this might all change. There’s also the chance that, while this works, I’m still “holding it wrong” and there might be an even more straightforward way to achieve this that I do not know about.
The endpoint
The rest of this post assumes that you’ll be working with the following endpoint, defined in src/routes/config.js
:
import os from 'os'
import fs from 'fs'
import path from 'path'
// Load the configuration from ~/.small-tech.org/basil/config.json
const config = JSON.parse(fs.readFileSync(path.join(os.homedir(), '.small-tech.org', 'basil', 'config.json'), 'utf-8'))
// Return the configuration in response to a GET request on /config
export async function get() {
return {
body: config
}
}
Layout: load the data
You load the data in the load() handler.
For example, your layout in src/routes/$layout.svelte
might resemble the following:
<script context='module'>
import '../app.css'
import Nav from '../lib/Nav.svelte'
export async function load({ page, fetch, session, context }) {
const url = '/config'
const res = await fetch(url)
if (res.ok) {
const config = await res.json()
return { context: { config } }
}
return {
status: res.status,
error: new Error(`Could not load ${url}`)
};
}
</script>
<Nav/>
<slot/>
Two key things to note here:
- This is a context=‘module’ script, not a regular script tag.
- We’re returning an object with a
context
property from theload()
handler.
So what’s this context property?
According to the docs, it is exactly what we need:
This will be merged with any existing context and passed to the load functions of subsequent layout and page components.
This only applies to layout components, not page components.
The important thing to note is that it is not the same as the context you manipulate using the setContext()
and getContext()
methods. In fact, if you try to access those methods from context='module'
scripts, you will get an error.
Page: load the data from the context
Now, let’s say you have a page at src/routes/index.svelte
that gets slotted into your layout.
To use the configuration data, you just need to get it from the context you returned in your layout:
<script context='module'>
let config
export async function load({ page, fetch, session, context }) {
config = context.config
return true
}
</script>
<main>
<h1>{config.name}</h1>
<h2>About this Small Web host</h2>
<p>{config.description}</p>
</main>
<style>
/* etc. */
</style>
That’s it!
Hope it helps in case you were stuck trying to do the same thing :)
Like this? Fund us!
Small Technology Foundation is a tiny, independent not-for-profit.
We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.
-
You can actually also use the other type of context in Svelte/SvelteKit for this also but it is a far more convoluted process that involves returning a prop from the
load()
handler in your layout and storing it in the context usingsetContext()
from a separate script tag (not yourcontext='module'
script tag) in the layout, and, finally, usinggetContext()
to retrieve it from the script tag (not yourcontext='module'
script tag) of your page. Whew! ↩︎