Learning goal: by the end of this tutorial you will have Gilbert installed, understand its default project structure, and be able to build a site both from the command line and programmatically.
- Node.js 24+
- Git
- Familiarity with the command line
The fastest way to see Gilbert in action is through the example project in the repository:
# Clone the repository
git clone git@github.com:tforster/gilbert.git
cd gilbert
# Install dependencies
npm install
# Try the example project
cd examples/getting-started
npx gilbert
# Review the generated output
ls -la dist/For your own projects, install Gilbert as a development dependency:
npm install @tforster/gilbert --save-devQuick Example: Basic programmatic usage
import Gilbert from "@tforster/gilbert";
import GilbertFS from "@tforster/gilbert-fs";
// Create adapter instances
const dataAdapter = new GilbertFS({ base: "./src/data" });
const templatesAdapter = new GilbertFS({ base: "./src/templates" });
const staticAdapter = new GilbertFS({ base: "./src" });
const outputAdapter = new GilbertFS();
// Configure Gilbert with streams-first constructor
const gilbert = new Gilbert(
{
templates: templatesAdapter.read("**/*.hbs"),
data: { source: dataAdapter.read("**/*.json") },
scripts: ["./src/scripts/main.js"],
stylesheets: ["./src/stylesheets/main.css"],
staticFiles: staticAdapter.read("images/**/*"),
},
{
debug: true,
}
);
// compile() returns ReadableStream directly
await gilbert.start().pipeTo(outputAdapter.write("./dist"));Gilbert follows a conventional project structure that separates content, templates, assets, and output:
. (your project root)
├── src/ # Source directory (configurable)
│ ├── data/
│ │ └── data.json # Page data and content
│ ├── theme/
│ │ ├── common/ # Shared template components
│ │ └── templates/ # Page templates (.hbs files)
│ ├── scripts/
│ │ └── main.js # JavaScript entry points
│ ├── stylesheets/
│ │ └── main.css # CSS entry points
│ ├── images/ # Static assets
│ └── fonts/ # Font files
└── dist/ # Generated output (configurable)
├── index.html
├── about.html
├── main.js
├── main.css
└── images/Key directories:
src/data/— JSON files defining pages and content. The main data file uses theurisproperty to map URL paths to page data.src/theme/— Handlebars templates (.hbsfiles). Template names correspond towebProducerKeyvalues in your data.src/scripts/— JavaScript entry points processed by esbuild for bundling and optimisation.src/stylesheets/— CSS entry points processed by PostCSS with optional autoprefixing.src/images/,src/fonts/— Static assets copied to the output directory.
# Basic operation with defaults
npx gilbert
# View all available options
npx gilbert --help
# Customise paths and options
npx gilbert \
--data ./content/data.json \
--theme ./templates/**/*.hbs \
--scripts ./js/main.js \
--css ./styles/main.css \
--files ./assets/**/* \
--out ./buildCommon CLI options:
| Option | Default | Description |
|---|---|---|
--data [path] |
./src/data/data.json |
Path to JSON data file |
--theme [glob] |
./src/theme/**/*.hbs |
Glob pattern for Handlebars templates |
--scripts [entry] |
./src/scripts/main.js |
JavaScript entry points |
--css [entry] |
./src/stylesheets/main.css |
CSS entry points |
--files [glob] |
./src/images/**/* |
Static file patterns |
--out [dir] |
./dist |
Output directory |
import Gilbert from "@tforster/gilbert";
import GilbertFS from "@tforster/gilbert-fs";
const dataAdapter = new GilbertFS({ base: "./src/data" });
const templatesAdapter = new GilbertFS({ base: "./src/templates" });
const staticAdapter = new GilbertFS({ base: "./src" });
const outputAdapter = new GilbertFS();
const gilbert = new Gilbert(
{
templates: templatesAdapter.read("**/*.hbs"),
data: { source: dataAdapter.read("**/*.json") },
scripts: ["./src/scripts/main.js"],
stylesheets: ["./src/stylesheets/main.css"],
staticFiles: staticAdapter.read("images/**/*"),
},
{ debug: true }
);
await gilbert.start().pipeTo(outputAdapter.write("./dist"));Gilbert supports selective pipeline execution for faster iteration. Omit pipelines you do not need:
// Only templates and static files (no scripts or stylesheets)
const gilbert = new Gilbert({
templates: templatesAdapter.read("**/*.hbs"),
data: { source: dataAdapter.read("**/*.json") },
staticFiles: staticAdapter.read("images/**/*"),
// scripts and stylesheets omitted
});
// Templates only — fastest for content-only updates
const gilbert = new Gilbert({
templates: templatesAdapter.read("**/*.hbs"),
data: { source: dataAdapter.read("**/*.json") },
});Your data.json defines pages using the uris property:
{
"uris": {
"/index": {
"webProducerKey": "homepage",
"title": "Welcome",
"content": "Welcome to our site"
},
"/about": {
"webProducerKey": "page",
"title": "About Us",
"content": "Learn about our company"
}
}
}The webProducerKey maps to template files (homepage.hbs, page.hbs) in your theme directory.
- How to install and run Gilbert from the CLI
- Gilbert's default project structure and what each directory contains
- How to use the programmatic API with adapter instances
- How to omit pipelines for faster focused builds
- Architecture — understand how Gilbert's streams-based engine works
- Packages Reference — full details on every gilbert-* package
- API Reference — complete API documentation
- Set Up Local Development — configure watch mode and hot-reload