Skip to content

Project structure

This is what create-staticbolt writes, and what the rest of the docs assume:

my-site/
.staticbolt.ts the build config: every plugin, in the order it runs
tsconfig.json path aliases, plus the compiler options the editor uses
package.json dev and build scripts
eslint.config.mjs
.stylelintrc.mjs
.prettierrc
.gitignore
.vscode/ recommended extensions and workspace settings
pages/
index.html every file here becomes a page
sources/
layouts/main.layout.html
parts/feature-card.part.html
scripts/main.ts
scripts/global.d.ts
styles/global.css
styles/showcase.css
assets/favicon.svg
assets/apple-touch-icon.png

Nothing about it is fixed. The directories exist because the config's globs and the aliases point at them. Move one and both follow.

pages

Everything under pages becomes a route. pages/about.html is served at /about/, pages/index.html at /. Markdown files work the same way.

The load sources plugin decides which files are picked up:

plugins.loadSourcesPlugin({ include: ["./pages/**/*.{html,md}"] });

sources

Everything a page pulls in, and nothing that is a route on its own.

Directory What lives there
layouts Page wrappers, plain HTML with <slot> elements where the page's content goes.
parts Reusable pieces a page or layout drops in with <part src="...">.
scripts TypeScript, compiled and bundled by the build.
styles CSS, run through PostCSS and bundled by the build.
assets Files copied through, with images converted where the config asks for it.

Aliases

tsconfig.json declares the aliases, so the build, the editor, and the language server all resolve paths the same way:

tsconfig.json
{
"compilerOptions": {
"paths": {
"~/*": ["./pages/*"],
"@/*": ["./sources/*"],
"@assets/*": ["./sources/assets/*"],
"@scripts/*": ["./sources/scripts/*"],
"@styles/*": ["./sources/styles/*"],
"@layouts/*": ["./sources/layouts/*"],
"@parts/*": ["./sources/parts/*"],
"@components/*": ["./sources/components/*"],
"@types": ["./sources/types"]
}
}
}

Adding a directory means adding its alias here, and nothing else has to change.

~/about/ in an href resolves to the built page relative to whatever file the link ends up in, so the same markup works at the site root and under a sub-path.

public

Files that go to the site root as they are, with no processing: public/favicon.ico is /favicon.ico. The dev server serves them from there, the copy assets plugin copies them to the root of dist, and the analyze output plugin never reports them as unused. publicDir in the config moves it:

export default defineConfig({
publicDir: "./static",
plugins: [],
});

dist

The build writes here, and writeFilesPlugin with clean: true empties it first. analyzeOutputPlugin then removes anything nothing references and reports files that grew too large.