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.pngNothing 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:
{ "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.