Skip to content

Firestore Bundles

Firestore bundles let you pre-package query results on the server and serve them as static files. The client loads the bundle instead of making a live Firestore query on first render, dramatically reducing cold-start latency.

import { loadFirestoreBundle } from 'svelte-firekit';
// Fetch the bundle from your server or CDN
const response = await fetch('/bundles/featured-posts.bundle');
// Load it into the local Firestore cache
await loadFirestoreBundle(response.body!);

After loading, any query that matches data in the bundle will be served from the local cache without a round-trip to Firestore.

Bundles can include pre-built named queries. Use getNamedQuery to retrieve one:

import { getNamedQuery } from 'svelte-firekit';
import { getDocs } from 'firebase/firestore';
const query = await getNamedQuery<Post>('featured-posts');
if (query) {
const snapshot = await getDocs(query);
const posts = snapshot.docs.map(d => ({ id: d.id, ...d.data() }));
}

getNamedQuery returns null if the named query is not available in the cache (e.g. the bundle has not been loaded yet).

A common pattern is to load the bundle in a SvelteKit server load function and pipe it through to the client:

+page.server.ts
export async function load({ fetch }) {
const res = await fetch('/bundles/featured-posts.bundle');
return {
bundleData: await res.arrayBuffer(),
};
}
+page.svelte
<script lang="ts">
import { loadFirestoreBundle, getNamedQuery } from 'svelte-firekit';
import { getDocs } from 'firebase/firestore';
import { onMount } from 'svelte';
let { data } = $props();
let posts = $state<Post[]>([]);
onMount(async () => {
const stream = new Response(data.bundleData).body!;
await loadFirestoreBundle(stream);
const q = await getNamedQuery<Post>('featured-posts');
if (q) {
const snap = await getDocs(q);
posts = snap.docs.map(d => ({ id: d.id, ...d.data() as Post }));
}
});
</script>
{#each posts as post (post.id)}
<article><h2>{post.title}</h2></article>
{/each}