data
Optional provider. Page path → JSON file: the build writes each page's load() result, spa fetches it on navigation.
What it does
Optional provider. data is the transport of the SSG → DATA → SPA pattern. It owns one
contract: a page path maps to one JSON file. On Node, the build calls write(entries) with the
load result of every rendered page. In the browser, spa calls at(path) on navigation and
passes the JSON to the route's render as ctx.data.
It is exported from both entries. Compose it on both sides for data navigation.
How it works
data declares no dependencies. The link to the other plugins is a call they make:
buildchecksctx.has("data")in its pages phase. Whendatais there androuter.mode()is not"ssg", it writes one sidecar per page that has arender.spachecksctx.has("data")while the browser app is created. Whendatais there and the mode is not"ssg", navigation tries the data path before HTML-over-fetch.
urlFor and fileFor come from one function, so the written file is the fetched URL:
| Page path | fileFor | urlFor |
|---|---|---|
/ | _data/index.json | /_data/index.json |
/en/hello/ | _data/en/hello/index.json | /_data/en/hello/index.json |
/en/hello | _data/en/hello/index.json | /_data/en/hello/index.json |
write stores each entry's data with JSON.stringify, at most 8 files at a time, through a
lazily loaded node:fs writer. The browser bundle gets no Node code from it.
Config
pluginConfigs.data.
| Option | Type | Default | Description |
|---|---|---|---|
outputDir | string | "_data" | Write side: the folder under the build outDir. |
baseUrl | string | "/_data/" | Read side: the URL the browser fetches from. Must start with /. Keep it equal to "/" + outputDir + "/". |
API
Mounted at app.data, reachable with ctx.require(dataPlugin).
| Method | Signature | Description |
|---|---|---|
write | (entries: readonly DataEntry[], options?: { outDir?: string }) => Promise<DataWriteSummary> | Node. One file per entry. outDir defaults to ./dist. Resolves to { fileCount, bytes, files }. |
at | (path: string) => Promise<unknown | null> | Browser. Fetches and caches a page's JSON. null when the fetch or the parse fails. |
urlFor | (path: string) => string | The fetch URL of a page. |
fileFor | (path: string) => string | The outDir-relative file of a page. |
DataEntry is { path: string; data: unknown }.
Events
Emits
None.
Listens to
None.
Depends on
Nothing. build and spa find it by name when it is composed.
Example
import { createApp, dataPlugin } from "@moku-labs/web";
const app = createApp({
plugins: [dataPlugin],
pluginConfigs: {
site: { name: "My Blog", url: "https://blog.dev", author: "Ada", description: "Notes." },
data: { outputDir: "_page", baseUrl: "/_page/" }
}
});
const summary = await app.data.write(
[
{ path: "/", data: { title: "Home" } },
{ path: "/about/", data: { title: "About" } }
],
{ outDir: "dist" }
);
console.log(summary.files); // ["_page/index.json", "_page/about/index.json"]
console.log(app.data.urlFor("/about/")); // "/_page/about/index.json"- Change both together: one is a folder, the other its URL.
You rarely call write yourself: the build does it for every page. Call it for pages the build
does not render, for example a JSON payload made by another script.
- Both sides or neither. Node only: the files are written but never read. Browser only: every fetch misses and navigation falls back to HTML.
ssgmode turns it off. The build writes no sidecars andspaskips the data path.atreturns the JSON as it is. No validation. The route'srendergets it directly asctx.data.baseUrlmust start with/. OtherwisecreateAppthrows[web] data.baseUrl: must be a site-root-relative URL path starting with "/" (e.g. "/_data/").
Related
- Data: the guide.
- How it works: the build-to-SPA picture.
- collection plugin: the same idea keyed by collection and shard.