Simple Jinja-based static site generator
Project description
Stapler
A simple static site generator built with Jinja and Markdown.
Installation
Clone the repo:
git clone https://github.com/gijs6/stapler.git
cd stapler
Create a virtual environment (recommended):
python -m venv .venv
source .venv/bin/activate
Install:
pip install -e .
Or with dev dependencies:
pip install -e ".[dev]"
Quick start
- Create a
stapler.tomlin your project root:
[site]
url = "https://yoursite.com"
title = "Your site"
-
Put your content in a
site/directory (the default). Templates go insite/templates/. -
Run:
stapler serve # local dev server on port 8000
stapler build # production build
Configuration
Required
[site]
url = "https://yoursite.com"
title = "Your site"
Optional
Site metadata
[site]
description = "About your site"
base_path = "/blog" # Deploy to example.com/blog instead of the root
Author info
Used in RSS/Atom feeds.
[site.author]
name = "Your name"
email = "you@example.com"
Directories
All paths are relative to where you run stapler.
[directories]
site = "site" # Content directory (default: "site")
build = "build" # Production output (default: "build")
build_dev = "build-dev" # Dev server output (default: "build-dev")
templates = "templates" # Templates folder inside the site directory (default: "templates")
blog = "blog" # Blog posts folder inside the site directory (default: "blog")
Default template
The template used for HTML pages that have front matter but no template field.
[templates]
default = "base.html"
Blog
[features.blog]
enabled = true # Enable blog functionality (default: false)
template = "blog_post.html" # Template for individual posts
index_template = "blog_index.html" # Template for the blog index page
Sitemap and feeds
[features]
sitemap = true # Generate sitemap.xml (default: true)
[features.feeds]
rss = true # Generate rss.xml (default: true)
atom = true # Generate atom.xml (default: true)
Feeds are only generated when the blog feature is enabled.
Markdown extensions
[markdown]
extensions = ["meta", "tables", "fenced_code"] # Python-Markdown extensions
How it works
Pages
Any .html or .md file in your site directory (excluding the templates and blog folders) becomes a page.
Markdown files
Markdown files are always rendered to HTML. If the front matter includes a template field, the result is passed to that template as page.content. Without a template field (or without front matter entirely), the raw HTML is written directly.
---
template: base.html
title: My page
---
# Content
Regular markdown here
HTML files
HTML files with front matter are rendered through a template. The template field in front matter takes precedence; if omitted, the default template from [templates].default is used.
---
title: My page
---
<h1>Content here</h1>
HTML files without front matter are treated as Jinja templates directly:
{% extends "base.html" %}
{% block content %}
<h1>Hello</h1>
{% endblock %}
Front matter is YAML. All fields are available as page.metadata.<field> in your templates.
Static files
Anything that's not a .html or .md file (and not in your templates or blog folder) is copied as-is to the output directory.
Blog
Enable the blog feature in your config, then put .md files in your blog directory.
---
title: My post
date: 2025-01-15
---
Post content here
The date field is optional. If omitted, stapler tries to infer it from the file's git history.
Templates
Templates live in the directory you configured (default: site/templates/).
Available in all templates
data: build infodata.now: current build timedata.now.date.long: date as%B %d, %Y(e.g.April 12, 2026)data.now.date.short: date as%Y-%m-%d(e.g.2026-04-12)data.now.time: time as%H:%M:%Sdata.now.iso: datetime as ISO 8601
data.last_commit: last git commit info (Noneif not in a git repo)data.last_commit.hash.short: short 7-character commit hashdata.last_commit.hash.long: full commit hashdata.last_commit.dt.date.long: date as%B %d, %Y(e.g.April 12, 2026)data.last_commit.dt.date.short: date as%Y-%m-%d(e.g.2026-04-12)data.last_commit.dt.time: time as%H:%M:%Sdata.last_commit.dt.iso: datetime as ISO 8601
Regular page templates
page: the current pagepage.active_page: identifier derived from the filename (e.g.aboutforabout.html,homeforindex.html), useful for highlighting the active nav itempage.canonical_path: URL path of the page (e.g./about)page.content: page content as HTML (only present if the page has front matter)page.metadata.<field>: any front matter field (e.g.page.metadata.title)
Blog post template
page: navigation infopage.active_page: name of the blog directory (e.g.blog)page.canonical_path: URL path of the post (e.g./blog/my-post)
post: the current blog postpost.title: post title (from front matter, or derived from the filename)post.slug: URL slug (filename without.md)post.content: post content as HTMLpost.date: date as%Y-%m-%d(e.g.2026-04-12), only set if a date is availablepost.date_iso: date as ISO 8601, only set if a date is available
data: same as above
Blog index template
page: navigation infopage.active_page: name of the blog directory (e.g.blog)page.canonical_path: URL path of the blog index (e.g./blog)
posts: list of all blog posts sorted newest first; each item has the same fields aspostabovedata: same as above
CLI
# Build with default config (stapler.toml)
stapler build
# Build with custom config
stapler build -c myconfig.toml
# Serve on default port (8000)
stapler serve
# Serve on custom port
stapler serve -p 3000
# Serve with custom config and port
stapler serve -c myconfig.toml -p 3000
# Show version
stapler --version
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stapler_ssg-0.1.2.tar.gz.
File metadata
- Download URL: stapler_ssg-0.1.2.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a146d961183284e10e671177ecc652511dfaebaf08d8bb481f14c7358fb73c22
|
|
| MD5 |
4171228fb559cde067f8cd33d9563778
|
|
| BLAKE2b-256 |
28adcf112042e76de036510457db1960d556ac7d8a7163ffb5fe3fc9f28514b7
|
Provenance
The following attestation bundles were made for stapler_ssg-0.1.2.tar.gz:
Publisher:
publish.yml on Gijs6/stapler
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapler_ssg-0.1.2.tar.gz -
Subject digest:
a146d961183284e10e671177ecc652511dfaebaf08d8bb481f14c7358fb73c22 - Sigstore transparency entry: 1280964040
- Sigstore integration time:
-
Permalink:
Gijs6/stapler@470c430974d46e8d0e5eda3842777c9f715301b8 -
Branch / Tag:
refs/tags/0.1.2 - Owner: https://github.com/Gijs6
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@470c430974d46e8d0e5eda3842777c9f715301b8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file stapler_ssg-0.1.2-py3-none-any.whl.
File metadata
- Download URL: stapler_ssg-0.1.2-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a676e2c99df3dc40472795235b6ef62187232a42ee2ad157fe3d90f8dae8f01
|
|
| MD5 |
08e91810b01cf801ee10cbc97ef305e7
|
|
| BLAKE2b-256 |
3a84037042d13703223f9c0edf95d1472b36b26437f027eb452b706a3ac3abca
|
Provenance
The following attestation bundles were made for stapler_ssg-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on Gijs6/stapler
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapler_ssg-0.1.2-py3-none-any.whl -
Subject digest:
3a676e2c99df3dc40472795235b6ef62187232a42ee2ad157fe3d90f8dae8f01 - Sigstore transparency entry: 1280964041
- Sigstore integration time:
-
Permalink:
Gijs6/stapler@470c430974d46e8d0e5eda3842777c9f715301b8 -
Branch / Tag:
refs/tags/0.1.2 - Owner: https://github.com/Gijs6
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@470c430974d46e8d0e5eda3842777c9f715301b8 -
Trigger Event:
release
-
Statement type: