Site config
pagedeck.config.ts default-exports defineConfig(...). A minimal one names
its collections and, to build, its pages and components:
export default defineConfig({
collections: [pages],
build: {
pages: [fromCollection(pages, { layout: "article" })],
components: { article: "./components/article.tsx" },
},
});Everything else this page lists has a default. A site that declares a field gets exactly the value it declares, and the default plays no part.
| Field | Default |
|---|---|
store |
"./content.db" |
build.outDir |
"./site" |
locales in definePages |
one locale, en, labelled English, direction: "ltr" |
trailingSlash in definePages |
"always" |
route in fromCollection |
the entry's path, with a last index segment dropped |
The store and the output directory
store is the SQLite file pagedeck sync writes and every other verb reads.
build.outDir is the directory pagedeck build writes the site to. Both are
resolved against the directory that holds the config file, not the directory
you run pagedeck from.
build.outDir defaults only inside a build section. A config with no build
has no output directory, so pagedeck build, pagedeck dev and
pagedeck rollback refuse it and say to add one.
The page set
build.pages takes a page set from definePages, or a bare list of page
sources:
pages: [fromCollection(pages)],A bare list is the sources of a definePages call that declares nothing
else, so it gets the default locale and trailing slash. Moving between the two
is one wrap. Call definePages yourself to declare locales or a trailing
slash, or to keep the page set in a variable so a content callback can link
with its href:
const site = definePages({
trailingSlash: "never",
locales: defineLocales({
en: { label: "English", direction: "ltr" },
de: { label: "Deutsch", direction: "ltr" },
}),
sources: [fromCollection(pages)],
});The two settings default independently. A page set that declares locales
and no trailingSlash spells its routes "always", and one that declares
trailingSlash alone has the single en locale.
The markdown loader's locale has no default. It says which locale the
loader's entries are in, and the page set's default locale does not reach it.
An entry in a locale the page set does not declare fails the build.
The route of an entry
fromCollection with no route routes each entry at its path, split into
segments: legal/terms routes at /legal/terms. An entry whose last segment
is index routes at the directory that holds it:
| Entry path | Route |
|---|---|
index |
/ |
about |
/about |
docs/index |
/docs |
docs/install |
/docs/install |
docs/index routes at /docs because that is where a static site author
expects it. A static host answers /docs with docs/index.html, and a
markdown file at content/docs/index.md is the page for that directory. The
rule drops a last index segment only when a segment is left, and a lone
index routes at /, so it never makes an empty or dot segment for the route
table to refuse. An entry at docs and one at docs/index both route at
/docs, and the build fails naming both, as it does for any two entries that
claim one route.
Write a route when the URL is not the path. The function you write decides
every route of that source, index entries included.
Rendering a page
fromCollection(collection, { layout: "article" }) renders each entry of the
collection into the registered component article, and passes it the entry's
title and html as props. The markdown loader writes that shape, and any
loader whose entries carry a string title and a string html works the same
way.
A page places other components through a components list in its frontmatter:
---
components: [counter]
---
# Counter
The build appends each name to the layout's children, in the order listed,
with no props, and the layout renders them where it puts children. A
component with "use client" placed this way is an island like any other: its
JavaScript ships to the pages that name it and to no other page.
Each name must be a key of build.components. The build looks the name up
there and nowhere else, and never reads a frontmatter value as a module path.
Entry content, which may come from a CMS, can pick from the components the
config registered and cannot import anything. An unregistered name fails the
build, and so does an entry without a string title or html. The build names
every such entry in one run, lists the registered names, and exits 1,
because the content is at fault and the config is intact.
The layout itself must be a key of build.components too. pagedeck build,
pagedeck dev and pagedeck sync check it when they load the config, and
exit 2 naming every source whose layout is unregistered.
build.content renders every other page. The callback gets the page and the
content store, and returns the page's component tree. Each page has one
renderer, chosen by its source: a page whose source names a layout renders
into that layout, and the build never calls the callback for it. A page whose
source names no layout, such as one from paginate or fromTemplate, goes to
the callback. So the config needs build.content when any source names no
layout, and leaves it out when every source names one:
build: {
pages: [
fromCollection(pages, { layout: "article" }),
paginate({ pageSize: 10, lists: postLists }),
],
components: { article: "./components/article.tsx", list: "./components/list.tsx" },
content: (page) => ({ template: "list", props: { paging: page.paging } }),
},Write a callback for a collection too when its pages need another tree, or
props from fields other than title and html: drop that source's layout,
and the callback renders its pages.