| layout | docs |
|---|---|
| docsOrder | 30 |
| handlebars | false |
A page combines source content with a layout, variables, and optional browser assets. This guide explains how to arrange those files and how DOMStack turns them into HTML at matching URLs.
[[toc]]
Pages are named directories inside src with one of the following page files:
mdpages are CommonMark markdown pages, with an optional YAML front-matter block.htmlpages are an inner HTML fragment that get inserted into the page layout.tspages are TypeScript files that export a default function that resolves into an inner HTML fragment inserted into the page layout.
Note
A source-backed page is discovered directly from a page file in src, rather than created by a *.pages.ts module.
Source-backed pages exist before global.data.ts and Generated pages run.
Variables are available in all pages.
md and html pages support variable access via handlebars template blocks.
ts pages receive variables as part of the argument passed to them.
See the Variables section for more info.
Pages can define a special variable called layout that determines which layout the page is rendered into.
Because pages are just directories, they nest and structure naturally as a filesystem router.
Directories in the src folder that lack one of these special page files can exist alongside page directories and can be used to store co-located code or static assets without conflict.
A md page looks like this on the filesystem:
src/page-name/page.md
# or
src/page-name/README.md
# or
src/page-name/loose-md.mdmdpages have three types: apage.md, aREADME.md, or a loosewhatever-name-you-want.mdfile.page.mdandREADME.mdfiles transform to anindex.htmlat the same path. When both exist in the same directory,page.mdtakes precedence overREADME.md.whatever-name-you-want.mdloose markdown files transform intowhatever-name-you-want.htmlfiles at the same path in thedestdirectory.mdpages can have YAML frontmatter, with variables that are accessible to the page layout and handlebars template blocks when building.- You can include HTML in markdown files, so long as you adhere to the allowable markdown syntax around html tags.
mdpages support handlebars template placeholders.- You can disable
mdpage handlebars processing by setting thehandlebarsvariable tofalse. mdpages support many github flavored markdown features.
An example of a md page:
---
title: A title for a markdown page
favoriteColor: 'Blue'
---
Just writing about web development.
## Favorite colors
My favorite color is {{ vars.favoriteColor }}.A html page looks like this:
src/page-name/page.htmlhtmlpages are namedpage.htmlinside an associated page folder.htmlpages are the simplest page type indomstack. They let you build with raw html for when you don't want that page to have access to markdown features. Some pages are better off with just rawhtml, and the rules with buildinghtmlin a realhtmlfile are much more flexible than inside of amdfile.htmlpage variables can only be set in apage.vars.tsfile inside the page directory.htmlpages support handlebars template placeholders.- You can disable
htmlpage handlebars processing by setting thehandlebarsvariable tofalse.
An example html page:
<h2>Favorite frameworks</h2>
<ul>
<li>React</li>
<li>Vue</li>
<li>Svelte</li>
<!-- favoriteFramework defined in page.vars.ts -->
<li>{{ vars.favoriteFramework }}</li>
</ul>A ts page looks like this:
src/page-name/page.tsNote
Wherever you see .ts being used, you can also use .js.
Type checking is supported in both file types.
See Supported file types for all available extensions.
tspages consist of a named directory with apage.tsfile that exports a default function returning the contents of the inner page.- A
tspage needs toexport defaulta function (async or sync) that accepts a variables argument and returns a string of the inner HTML of the page, or any other type that your layout can accept. - You can specify the return type using
PageFunction<T, U, D>whereTis the variables type,Uis the return type (defaults toany), andDis the declared global-data shape. - A
tspage can export avarsvariable provider that takes highest variable precedence when rendering the page.export varsis similar to amdpage's front matter. - A
tspage receives the standarddomstackVariables set. - There is no built-in Handlebars support in
tspages; however, you are free to use any template library that you can import. tspages run in a Node.js context only.
An example TypeScript page:
import type { PageFunction } from '@domstack/static/types.js'
export const vars = {
favoriteCookie: 'Chocolate Chip with Sea Salt'
}
const page: PageFunction<typeof vars> = async ({
vars
}) => {
return /* html */`<div>
<p>This is just some html.</p>
<p>My favorite cookie: ${vars.favoriteCookie}</p>
</div>`
}
export default pageIt is recommended to use some level of template processing over raw string templates so that HTML is well-formed and variable values are properly escaped.
DOMStack's default layout uses fragtml, a safe-by-default HTML tagged template library.
Here is a more realistic TypeScript example that uses fragtml and an explicit global-data subscription.
import { html } from 'fragtml'
import type { HtmlResult } from 'fragtml/types.js'
import type { PageFunction } from '@domstack/static/types.js'
type BlogVars = {
favoriteCake: string
}
type BlogData = {
blogYears: number[]
}
export const vars = {
favoriteCake: 'Chocolate Cloud Cake',
dataDeps: ['blogYears'],
}
const blogIndex: PageFunction<BlogVars, HtmlResult, BlogData> = async ({
vars: { favoriteCake },
data,
}) => {
return html`<div>
<p>I love ${favoriteCake}!!</p>
<ul>
${data.blogYears.map(year => html`
<li>
<a href="/blog/${year}/">
${year}
</a>
</li>
`)}
</ul>
</div>`
}
export default blogIndexYou can create a style.css file in any page folder.
Page styles are loaded on just that one page.
You can import common use styles into a style.css page style using css @import statements to re-use common css.
You can @import paths to other css files, or out of npm modules you have installed in your projects node_modues folder.
css page bundles are bundled using esbuild.
An example of a page style.css file:
/* /some-page/style.css */
@import "some-npm-module/style.css";
@import "../common-styles/button.css";
.some-page-class {
color: blue;
& .button {
color: purple;
}
}You can create a client.ts file in any page folder.
Page bundles are client-side JavaScript bundles that are loaded on that one page only.
You can import common code and modules from relative paths, or npm modules out of node_modules.
Page client bundles are bundle-split with every other client-side entry point, so shared code is loaded efficiently.
Page bundles run in a browser context only; however, they can share carefully crafted code that also runs in a Node.js or layout context.
Page bundles are built using esbuild.
An example of a page client.ts file:
/* /some-page/client.ts */
import { funnyLibrary } from 'funny-library'
import { someHelper } from '../helpers/foo.ts'
await someHelper()
await funnyLibrary()Client bundles support .tsx through esbuild's JSX transform.
Note
Wherever you see .tsx being used for a client bundle, you can also use .jsx.
Type checking is supported in both file types.
See Supported file types for all available extensions.
Important
.tsx and .jsx are supported only in client bundles.
JSX syntax is unavailable in page files, layouts, templates, settings, and anything else that runs in the Node.js context.
DOMStack does not include a JSX runtime by default.
Install the runtime you want and configure it with esbuild.settings.
Preact is the recommended JSX runtime for DomStack because it is small, browser-focused, and works well with page-scoped client bundles.
See the preact-isomorphic and react examples for complete projects.
To use Preact in browser TSX bundles, add it to your project and opt into Preact's automatic JSX runtime:
npm install preact// src/esbuild.settings.ts
export default async function esbuildSettingsOverride (esbuildSettings) {
esbuildSettings.jsx = 'automatic'
esbuildSettings.jsxImportSource = 'preact'
return esbuildSettings
}If a dependency expects React, you can often swap React for @preact/compat with an npm package alias.
This installs @preact/compat into node_modules/react.
See Simple TanStack Query in Preact for more details.
{
"dependencies": {
"react": "npm:@preact/compat@^18.3.1"
}
}React also works if your project needs React-specific APIs or ecosystem packages. To use React in browser TSX bundles, add React to your project and opt into React's automatic JSX runtime:
npm install react react-dom// src/esbuild.settings.ts
export default async function esbuildSettingsOverride (esbuildSettings) {
esbuildSettings.jsx = 'automatic'
esbuildSettings.jsxImportSource = 'react'
return esbuildSettings
}Each page can also have an adjacent page.vars.ts file that default-exports a variable provider containing page-specific variables.
// export an object
export default {
my: 'vars'
}
// OR export a default function
export default () => {
return { my: 'vars' }
}
// OR export a default async function
export default async () => {
return { my: 'vars' }
}Page variable files have higher precedence than global.vars.ts variables, but lower precedence than frontmatter or vars exports from ts pages.
See Variables for the full variable cascade.
A complete draft page can use the same colocated files as a published page:
src/
└── blog/
└── unpublished-post/
├── page.draft.md # Draft page content
├── page.vars.ts # Page-specific variables
├── client.ts # Page-specific browser code
└── style.css # Page-specific styles
If you add a .draft.{md,html,ts} suffix to any page type, the page is considered a draft page.
Draft pages are not built by default.
If you pass the --drafts flag when building or watching, the draft pages will be built.
When draft pages are omitted, they are completely ignored.
Draft pages can be detected in layouts using the page.draft === true or pages[n].draft === true variable.
It is a good idea to display something indicating the page is a draft in your templates so you don't get confused when working with the --drafts flag.
Note
Static assets colocated with draft pages are still copied when drafts are excluded because static assets are processed independently from pages.
Draft pages let you work on pages before they are ready and easily omit them from a build when deploying pages that are ready.
Variables combine site-wide defaults with layout and page overrides. The precedence is page/frontmatter vars, page variable files, inner-to-outer layout vars, global vars, then DOMStack defaults. See Settings for global defaults and Layouts for layout defaults.
DOMStack accepts variable providers anywhere variables can be supplied. A variable provider is an object or a sync/async function that returns an object.
Object provider:
// src/global.vars.ts
export default {
siteName: 'My site'
}Synchronous function provider:
// src/global.vars.ts
export default function vars () {
return {
siteName: 'My site'
}
}Asynchronous function provider:
// src/global.vars.ts
export default async function vars () {
return {
siteName: 'My site'
}
}Pages and layouts receive an object with the following parameters:
vars: An object with the variables ofglobal.vars.ts,page.vars.ts, layout vars, and any frontmatter orvarsexports from the page merged together.data: Only the top-level values selected fromglobal.data.tsby this renderer's owndataDepsdeclarations.page: The current page'sPageInfometadata.
Template files receive a similar set of variables:
vars: An object with the variables fromglobal.vars.ts.data: Only the top-level values selected fromglobal.data.tsby the template'sdataDepsnamed export.template: Information about the current template file.