Htmlpress
Htmlpress is the static site generator for humans and agents. It turns a folder of Markdown and HTML pages into a site. Each page keeps its own markup, and htmlpress adds one layout, one theme, a navigation and a live-reloading dev server. It is one Rust binary, installed from npm, PyPI or crates.io.
You or your agent write plain Markdown or semantic HTML, which agents know better than any templating language, and every page comes out looking like the same site: no framework, no build config, no utility classes.
Htmlpress is in technical preview: the page contract may still change before 1.0.
What you get
-
Just write Markdown or HTML. A page is any
.mdfile with frontmatter, or any.htmlfile, a full document or a bare fragment, whose<title>and<meta>tags do what frontmatter does; see Markdown pages. -
A theme styled from the elements. Headings, tables, code blocks,
<details>,<figure>and the rest look right with no classes, in light and dark mode. -
Rich elements. Callouts, tabs, steps, cards, badges, file trees,
sortable tables, terminals, package tabs (npm/pnpm/yarn/bun), Mermaid diagrams and
embeds (YouTube, SoundCloud), all as
htmlpress-*custom elements. - Navigation for free. A sidebar nested by folder, breadcrumbs, a table of contents, previous/next links and an "Edit on GitHub" link.
- Multilingual. Translations beside their originals, a language picker, and the UI in seven bundled languages; see Translations.
-
Made for agents. Every build publishes a Markdown copy of each
page,
llms.txtand your agent skills; every command speaks JSON. -
Themable.
--htmlpress-*tokens, and per-file overrides of the layout, styles and components via a.htmlpress/folder. -
One binary. No runtime and no dependencies: a static site with
sitemap.xml,robots.txtand a 404 page, ready for any host.
Quick start
You need nothing but the binary. Every channel ships the same binary at the same version, so pick the package manager the project already uses.
Install
curl -fsSL https://htmlpress.dev/install.sh | sh
Downloads the binary for your platform from the
GitHub releases,
checks its SHA-256 and puts it in ~/.local/bin. Set
HTMLPRESS_VERSION to pin a version and
HTMLPRESS_INSTALL_DIR to install elsewhere.
npm install --global gethtmlpress
# or run it without installing
npx gethtmlpress --version
The gethtmlpress package pulls in one platform package, such as
@htmlpress/linux-x64, through an optional dependency. Installing
with --omit=optional leaves the binary out.
pip install htmlpress
# or run it without installing
uvx htmlpress --version
The wheel for your platform carries the binary; no Python code runs.
cargo install htmlpress
Builds from source with the Rust toolchain, 1.88 or newer.
Prebuilt binaries cover Linux, macOS and Windows on x64 and arm64:
| Platform | npm package | Rust target |
|---|---|---|
| Linux x64 | @htmlpress/linux-x64 |
x86_64-unknown-linux-musl |
| Linux arm64 | @htmlpress/linux-arm64 |
aarch64-unknown-linux-musl |
| macOS x64 | @htmlpress/darwin-x64 |
x86_64-apple-darwin |
| macOS arm64 | @htmlpress/darwin-arm64 |
aarch64-apple-darwin |
| Windows x64 | @htmlpress/win32-x64 |
x86_64-pc-windows-msvc |
| Windows arm64 | @htmlpress/win32-arm64 |
aarch64-pc-windows-msvc |
On any other platform, build with cargo install htmlpress and, for the
npm package, point HTMLPRESS_BINARY at the result.
Configure
Configuration is optional. To set the site's metadata, create
htmlpress.yaml at the root of your project:
title: My docs
description: Documentation for my project
include: docs/**/*.html
Without include, every .html file in the project is a
page; with it, only the matching ones are, served without the glob's base, so
docs/usage.html is at /usage/. See
Configuration File for every key.
Add content
-
docs/
- index.html
- getting-started.html
- usage.html
Each page sets its title, description and order in tags a fragment may carry:
<title>Getting Started</title>
<meta name="description" content="Set up the project in five minutes." />
<meta name="order" content="1" />
<h1>Getting Started</h1>
<p>Welcome!</p>
---
title: Getting Started
description: Set up the project in five minutes.
order: 1
---
# Getting Started
Welcome!
See Frontmatter for what each tag means.
Run
htmlpress start # dev server with live reload at http://localhost:8000
htmlpress build # production build to .htmlpress/build/
htmlpress preview # serve the production build locally
htmlpress clear # remove .htmlpress/build/
That's the whole loop. Add .htmlpress/build/ to your
.gitignore. This site is built that way from the
rust/docs/
folder of the repository.
Project structure
A typical htmlpress site:
-
my-project/
- htmlpress.yaml — configuration (optional)
- index.html — the home page
-
guides/ — a sidebar group, named by its
index.html- index.html
- build.html
- logo.svg — any other file, copied as-is
-
.htmlpress/ — escapes and the build output
- layout.html
- build/
A file under .htmlpress/ at a built-in's path, such as
layout.html, styles/tokens.css or
components/tabs/tabs.css, replaces the default, so you can customize
the layout, the theme and components without forking. Run
htmlpress escape <name> to copy a default into
.htmlpress/ ready to edit.
Commands
htmlpress build [root]
Renders the project at root (default .) into
root/.htmlpress/build, replacing what is there. --out /
-o picks another folder; one that would hold the project, or a visible
folder inside it, is refused, since its pages would be read back as sources.
$ htmlpress build docs
built 6 pages and 0 files into docs/.htmlpress/build
htmlpress start [root]
Builds the project, serves it on http://localhost:8000 and rebuilds on
every change. Open pages reload themselves after each build. A failed build prints
its error and shows it in the browser: over the pages already open, and in place of
any page opened next. The server stays up, even when the first build fails, and the
next good build reloads every page.
When 8000 is taken it tries 8001 and on to 8009, and prints the URL it got.
--port / -p names one port instead (0 picks
any free one). --host alone serves on every address, so a phone on the
same network can open the site, and prints that URL too;
--host <address> serves on one. With base in
htmlpress.yaml the site is served under it, the printed URLs include
it, and / leads there; restart after changing base.
--open opens the site in the browser once it listens;
--open <path> opens that page, under base.
--out / -o works as for build.
$ htmlpress start docs --host
serving http://localhost:8000
network http://192.168.1.107:8000
htmlpress preview [root]
Builds the project for production and serves it as it will be deployed: no watching,
no reload. Takes --port, --host, --open and
--out like start.
htmlpress clear [root]
Removes the build output (--out to name another), with the same guard
as build.
htmlpress escape [names…]
Copies built-in files into .htmlpress/ to edit them: a name is a path
such as components/tabs/tabs.js, or a component's tag for all its
files. Without names it lists every built-in file and whether the project has
escaped it. --root / -r picks the project (default
.). It refuses to overwrite a file; see
Overriding/Escaping.
$ htmlpress escape htmlpress-tabs
escaped .htmlpress/components/tabs/tabs.css
escaped .htmlpress/components/tabs/tabs.js
For scripts and agents
The CLI is built with Fireargs, so every command
speaks JSON. --json prints the result as JSON, and with a value it also
takes the input as JSON:
$ htmlpress build docs --json
{
"output": "docs/.htmlpress/build",
"pages": 6,
"files": 0
}
$ htmlpress build --json '{"root":"docs","out":"dist"}'
htmlpress start --json and preview --json never return, so
they print {"url": "…", "network": []} as one line once the server
listens.
htmlpress --llms prints every command's input and output schema as an
MCP-shaped tool list, so an agent learns the CLI without reading this page.
Customization
Htmlpress is meant to look right with zero configuration and stay out of your way when you need more. Common customizations:
- Site metadata: title, description, favicon, logo. See Site Metadata.
- Sections: group pages by folder in the sidebar. See Article Sections.
- External links: header and sidebar links to anywhere. See External Links.
- Overriding/Escaping: replace the layout, any stylesheet or component from your project. See Overriding/Escaping.
- AI agents: what every build publishes for agents. See AI Agents.
Documentation
Full documentation lives at htmlpress.dev:
- Authoring reference: every element and component with examples
- Customization: every knob, with copy-pasteable snippets
- Deployment: publishing the build to a host
- Contributing: local setup and PR flow
Resources
- npm: npmjs.com/package/gethtmlpress
- PyPI: pypi.org/project/htmlpress
- crates.io: crates.io/crates/htmlpress
- GitHub: github.com/datisthq/htmlpress
- Issues: github.com/datisthq/htmlpress/issues
- Discussions: github.com/datisthq/htmlpress/discussions
License
MIT; see LICENSE.md.
Created with ❤ and Htmlpress