Warning Since this project Sveltekit has proper support for the defer api. This package is hereby deprecated. See the official documentation to know how to stream in Sveltekit.
⚠ This is extremely WIP
This package allows you to transfare Promises over the wire in sveltekit. It uses SSE and stores to let you write code as close as possible to "vanilla sveltekit" while giving you the opportunity (with a simple await) to choose what data you want available as soon as the page load and what data it's not that important to you.
Here's an example of what you would write;
hooks.server.ts
import { defer_handle } from 'sveltekit-defer';
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = defer_handle;
if you have other handles you can chain them with the utility function sequence
provided by sveltekit.
hooks.server.ts
import { defer_handle } from 'sveltekit-defer';
import { sequence } from '@sveltejs/kit/hooks';
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = sequence(defer_handle, your_handle);
you can than define a +page.server.ts
wrapping the load function inside the defer function
import type { ServerLoadEvent } from '@sveltejs/kit';
import { defer } from 'sveltekit-defer';
import type { PageServerLoad } from './$types';
async function get_blog() {
await new Promise((r) => setTimeout(r, 7000));
return "A long blog post that it's very critical so the user need to see it right away";
}
async function get_comments() {
await new Promise((r) => setTimeout(r, 10000));
return [
{ author: 'Antonio', comment: 'Very cool' },
{ author: 'Oskar', comment: "Yeah it's wonderful" }
];
}
/**
* Wrap you actual load function inside our defer function to unlock the defer functionality
*/
export const load: PageServerLoad = defer(async (event: ServerLoadEvent) => {
// start the fetch for the comments right away without awaiting
const comments = get_comments();
// await the blog post given that is critical content
const blog = await get_blog();
//return the promise (comments) and the blog post
return {
blog,
comments
};
});
then on the client side you can access the data via the store provided by sveltekit-defer
<script lang="ts">
import { get_data } from 'sveltekit-defer';
import type { PageData } from './$types';
const data = get_data<PageData>();
</script>
<main>
<section>
<!--The blog will be available right away-->
{$data.blog}
</section>
<section>
<ul>
<!--await the comments because they are a Promise-->
{#await $data.comments}
Loading...
{:then comments}
{#each comments as comment}
<li>{comment.author} - {comment.comment}</li>
{/each}
{/await}
</ul>
</section>
</main>
sveltekit-defer
makes use of apis that require to choose a name for them (e.g we need to create a couple of endpoints, an event name, a field to store the deferred promises etc etc). We tryed to chose unique enaugh names so that they should never collide with your applications but you know what they say and the internet is a vast enaugh place to encounter the weirdest circumstances. To avoid this sveltekit-defer
provide a custom vite plugin to override those names.
import { sveltekit } from '@sveltejs/kit/vite';
import { sveltekit_defer } from 'sveltekit-defer/vite';
import type { UserConfig } from 'vite';
const config: UserConfig = {
plugins: [
sveltekit_defer({
cookie_name: 'your_cookie_name',
stream_event: 'your_stream_event',
stream_pathname: '/your_pathname', //this should start with a / but don't worry, if you don't we take care of it
promise_field: 'your_promise_field'
}),
sveltekit()
]
};
export default config;
Make sure to put this plugin before the sveltekit
one.
Here's how it works:
Here's a simple flow diagram to depict this
There's a lot to do, but the first thing is to make sure that this doesn't create some super-huge security/performance issue and generally if there are things that can be done better.
Feel free to open an issue if you found something that is not working or if you have some ideas on how to improve.