@prerender
Use @prerender to fetch data at build time. Prerendered data is served as static
assets from a CDN, making navigation near-instant. Use this for content that changes only when
you redeploy.
Basic usage
from fluidkit import prerender
from pydantic import BaseModel
class Post(BaseModel):
slug: str
title: str
content: str
@prerender
async def get_posts() -> list[Post]:
return await db.get_all_posts()<script>
import { get_posts } from '$lib/content.remote';
</script>
{#each await get_posts() as post}
<a href="/blog/{post.slug}">{post.title}</a>
{/each}On the client, prerendered data is cached using the browser's Cache API. This cache survives page reloads and is cleared when the user first visits a new deployment.
Arguments
Like @query, prerender functions can accept arguments:
from fluidkit import prerender, error
@prerender
async def get_post(slug: str) -> Post:
post = await db.find(slug)
if not post:
raise error(404, "Not found")
return post<script>
import { get_post } from '$lib/content.remote';
let { params } = $props();
</script>
{#await get_post(params.slug) then post}
<h1>{post.title}</h1>
<div>{@html post.content}</div>
{/await}Any calls found by SvelteKit's crawler during prerendering are saved automatically. But you can
also specify which values to prerender using the inputs option.
Prerender inputs
Pass a list of arguments to prerender at build time:
@prerender(inputs=["hello-world", "about-fluidkit", "getting-started"])
async def get_post(slug: str) -> Post:
post = await db.find(slug)
if not post:
raise error(404, "Not found")
return postYou can also use a callable that returns the list:
@prerender(inputs=lambda: db.get_all_slugs())
async def get_post(slug: str) -> Post:
...Callable inputs are evaluated at build time but cannot be serialized into the generated
.remote.tsfile. Static lists are preferred when possible.
Dynamic fallback
By default, prerender functions are excluded from your server bundle — calling them with an
argument that wasn't prerendered will fail. Set dynamic=True to allow runtime
fallback for non-prerendered arguments:
@prerender(inputs=["hello-world", "about-fluidkit"], dynamic=True)
async def get_post(slug: str) -> Post:
post = await db.find(slug)
if not post:
raise error(404, "Not found")
return postWith dynamic=True, "hello-world" and "about-fluidkit" are prerendered at build time.
Any other slug is fetched from the server at runtime — slower on first load, but the function
still works.
No-argument prerender
For data with no arguments, @prerender is used bare with no options:
@prerender
async def get_site_config() -> SiteConfig:
return await db.get_config()This runs once at build time. Every page that calls get_site_config() gets the
cached result instantly.
When to use @prerender vs @query
| @prerender | @query | |
|---|---|---|
| Data fetched | At build time | At request time |
| Speed | Instant (static asset) | Network round-trip |
| Freshness | Stale until redeployment | Always current |
| Use case | Blog posts, docs, config | User data, dashboards, feeds |
Use @prerender with dynamic=True for a hybrid approach — prerender
known content for speed, fall back to the server for new content.
Limitations
- Prerender functions cannot set cookies (read-only, same as
@query) - Prerender functions do not support
.refresh()or.set()— data is static - Callable
inputswork at build time but won't appear in the generated.remote.ts