Data loading
Export a data() function and its result appears as ctx.data in your render function. It runs at build time, and per-request in the dev server.
export async function data({ params, dev }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
return await res.json()
}
export default ({ data }) => `
<html><body>
<h1>${data.title}</h1>
${data.body}
</body></html>
`import { html, body, h1 } from 'javascript-to-html'
export async function data({ params, dev }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
return await res.json()
}
export default ({ data }) =>
html(
body(
h1(data.title),
data.body,
),
)export async function data({ params, dev }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
return await res.json()
}
export default function Post({ data }) {
return (
<html>
<body>
<h1>{data.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.body }} />
</body>
</html>
)
}fetchWithCache
Building many pages against the same API? Import fetchWithCache from sitelo:
import { fetchWithCache } from 'sitelo'
export async function data({ params }) {
const res = await fetchWithCache(
`https://api.example.com/posts/${params.slug}`,
{ /* standard fetch options */ },
{ maxAge: 3600 }
)
return { post: await res.json() }
}Options
maxAge— cache TTL in seconds (default3600)cacheKey— custom key (default: hash of URL + method + headers + body)forceRefresh— bypass the cachecache—'auto'|'memory'|'fs'|'none'
Cache modes
auto(default) — memory in dev, filesystem in production buildsmemory— in-process, cleared when the process exitsfs— persisted undernode_modules/.cache/none— always fetch
Only GET requests are cached by default (pass a cacheKey to cache other methods). Error responses are never cached.
Local JSON files
No API? Keep the content in the repo as JSON and read it with sitelo/data.
data/
├─ site.json
└─ posts/
├─ hello-world.json
└─ why-static.jsonimport { readJsonCollection } from 'sitelo/data'
const posts = () => readJsonCollection('data/posts', { sort: '-date' })
export async function generateStaticParams() {
return (await posts()).map((post) => ({ slug: post.slug }))
}
export async function data({ params }) {
return (await posts()).find((post) => post.slug === params.slug)
}
export default ({ data }) => `
<html><body>
<h1>${data.title}</h1>
<time datetime="${data.date}">${data.date}</time>
${data.body}
</body></html>
`Relative paths resolve from the project root, so data/posts means the same thing wherever you run the CLI from. readJson returns one parsed file; readJsonCollection returns an array of entries, each with a slug — from a directory of .json files (one per entry, slug from the filename), or from a single file holding an array of entries or an object keyed by slug.
// data/posts/hello-world.json -> { slug: 'hello-world', ... }
await readJsonCollection('data/posts')
// data/posts.json: [{ "slug": "hello-world", ... }]
// data/posts.json: { "hello-world": { ... } }
await readJsonCollection('data/posts.json')
// data/site.json
await readJson('data/site.json')Collection options
slug— field name or function; defaults to the filename, the object key, or the entry’s ownslug/idsort— field name ('date'ascending,'-date'descending) or a compare functionrecursive— include.jsonfiles in subdirectories, slugged by their pathroot— directory relative paths resolve fromcache—'auto'|'memory'|'none'
Reads are memoized per file, so a 500-page build parses each file once. The dev server revalidates against mtime instead, and reloads the browser when a JSON file a page read changes. Duplicate slugs, missing files, and malformed JSON fail the build, each named by path.