vite-plugin-html-pages
v1.3.7
Published
Minimal static site generation (SSG) for Vite using JavaScript functions that return HTML
Maintainers
Readme
vite-plugin-html-pages
Minimal static site generation for Vite using JavaScript files that return HTML.
Generate static HTML pages from *.ht.js modules using Vite and
javascript-to-html.
⭐ If this project helps you, please consider starring it.
Built for the Vite ecosystem
Works seamlessly with:
- ⚡ Vite
- 📦 Rollup / Rolldown
- 🧩 javascript-to-html
A minimal static site generator for Vite that keeps pages as simple JavaScript functions returning HTML.
TL;DR
Write:
// src/index.ht.js
import { fragment, html, body, head, title, h1 } from 'javascript-to-html'
export default () => fragment(
'<!doctype html>',
html({lang: "en"},
head(
title("My website")
),
body(
h1('Hello world')
)
)
)Run:
vite buildGet:
<!-- dist/index.html -->
<!doctype html>
<html lang="en">
<head>
<title>My website</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>Features
- File-based routing
- Dynamic routes
[slug] - Multiple parameters
[year]/[slug] - Catch-all routes
[...slug] - Optional catch-all routes
[...slug]? - Route groups
(admin)/users.ht.js - Index routes
blog/[slug]/index.ht.js - Static params generation
- Fetch caching for data loading
- Dev server SSR rendering
- Parallel static generation
- Automatic
404.html - Automatic
sitemap.xml - Optional RSS feed generation
- Debug logging
Installation
npm install vite-plugin-html-pages javascript-to-htmlWhy this exists
Modern static site tools are powerful, but they often introduce:
- frameworks
- component systems
- complex build pipelines
- opinionated conventions
Sometimes you just want to:
- write HTML
- organize pages in folders
- generate static files
vite-plugin-html-pages exists for that use case.
It gives you:
- file‑based routing
- dynamic pages
- static generation
- dev server rendering
while keeping pages as simple JavaScript functions that return HTML.
Quick Start
vite.config.js
import { defineConfig } from "vite"
import htmlPages from "vite-plugin-html-pages"
export default defineConfig({
plugins: [htmlPages()]
})Example Project Structure
src/
index.ht.js
about.ht.js
blog/
index.ht.js
[slug].ht.js
[year]/[slug].ht.js
docs/
[...slug]?.ht.js
(admin)/
users.ht.jsRouting
Routes are generated directly from the filesystem.
| Feature | File | URL |
|-----|-----|-----|
| static routes | index.ht.js | / |
| dynamic routes | blog/[slug].ht.js | /blog/my-post |
| multiple params | blog/[year]/[slug].ht.js | /blog/2026/my-post |
| catch-all | docs/[...slug].ht.js | /docs/api/auth/login |
| optional catch-all | docs/[...slug]?.ht.js | /docs or /docs/getting-started |
| index routes | products/[product]/index.ht.js | /products/iphone-18 |
| route groups | (admin)/users.ht.js | /users |
Dynamic Routes
src/blog/[slug].ht.jsMatches:
/blog/hello-world
/blog/my-first-postExample:
import { fragment, html, body, h1 } from 'javascript-to-html'
export function generateStaticParams() {
return [
{ slug: 'hello-world' },
{ slug: 'my-first-post' }
]
}
export default ({ params }) => fragment(
'<!doctype html>',
html(
body(
h1(params.slug)
)
)
)Multiple Parameters
src/blog/[year]/[slug].ht.jsMatches:
/blog/2026/vite-routing
/blog/2025/my-first-postExample:
import { fragment, html, body, h1 } from 'javascript-to-html'
export function generateStaticParams() {
return [
{
year: 2026,
slug: "vite-routing"
},
{
year: 2025,
slug: 'my-first-post'
}
]
}
export default ({ params }) => fragment(
'<!doctype html>',
html(
body(
h1(params.slug)
)
)
)Catch-All Routes
src/docs/[...slug].ht.jsMatches:
/docs/api/auth/login
/docs/guides/rendering/staticParams:
params.slug === "api/auth/login"Optional Catch-All Routes
src/docs/[...slug]?.ht.jsMatches both:
/docs
/docs/getting-started
/docs/api/auth/loginParams:
| URL | params.slug |
|-----|-------------|
| /docs | "" |
| /docs/api | "api" |
| /docs/api/auth | "api/auth" |
Route Groups
Folders wrapped in parentheses are ignored in URLs.
src/(admin)/users.ht.jsURL:
/usersIndex Routes
Files named index.ht.js map to the parent route.
src/blog/index.ht.js -> /blog
src/blog/[slug]/index.ht.js -> /blog/my-postStatic Params
Dynamic routes can export generateStaticParams.
export function generateStaticParams() {
return [
{ slug: "hello-world" },
{ slug: "vite-routing" }
]
}Data Loading
Pages can export a data() function.
export async function data({ params }) {
return { title: params.slug }
}Caching
Use fetchWithCache for HTTP requests during static generation. Responses are
cached to avoid repeated network calls across page builds.
import { fetchWithCache } from 'vite-plugin-html-pages'
export async function data({ params }) {
const res = await fetchWithCache(
`https://api.example.com/posts/${params.slug}`,
{
// fetch API options
},
{ maxAge: 3600 }
)
const post = await res.json()
return { post }
}Options
| Option | Description |
|--------|-------------|
| maxAge | Cache TTL in seconds (default: 3600) |
| cacheKey | Custom cache key (default: hash of URL + method + headers) |
| forceRefresh | Bypass cache and fetch fresh |
| cache | 'auto' | 'memory' | 'fs' | 'none' |
Cache modes
auto(default): memory in dev, filesystem in productionmemory: in-process cache, cleared when the build process exitsfs: persisted undernode_modules/.cache/vite-plugin-html-pages/fetch/none: no caching, always fetches
Only GET requests are cached by default. For other methods, provide a
cacheKey to enable caching.
Layouts
Reusable layout functions work naturally with HT.js.
import { fragment, html, head, body } from 'javascript-to-html'
export default (...content) => fragment(
'<!doctype html>',
html(
head(),
body(
...content
)
)
)Plugin Options
| Option | Description |
|------|------|
| pagesDir | root directory for pages |
| include | page glob |
| exclude | excluded files |
| cleanUrls | /page/index.html instead of /page.html |
| renderConcurrency | parallel rendering |
| renderBatchSize | batch size |
| debug | enable debug logging |
| site | base URL for sitemap |
| rss | RSS configuration |
Debug Mode
Enable debug logging when troubleshooting.
htmlPages({
debug: true
})Example output:
[vite-plugin-html-pages] discovered entries [...]
[vite-plugin-html-pages] dev pages [...]
[vite-plugin-html-pages] render bundle ...This helps diagnose routing or build issues without modifying plugin code.
Automatic Sitemap
A sitemap.xml is generated automatically.
dist/sitemap.xmlOptional RSS Feed
htmlPages({
rss: {
site: "https://example.com",
title: "My Blog",
description: "Latest posts",
routePrefix: "/blog"
}
})Produces:
dist/rss.xmlPerformance
Large sites can increase concurrency:
htmlPages({
renderConcurrency: 16,
renderBatchSize: 128
})Comparison
| Tool | Focus | |-----|-----| | Astro | component‑based SSG | | Next.js | React SSR framework | | vite-plugin-html-pages | minimal HTML SSG for Vite |
Use Cases
vite-plugin-html-pages works well for:
- Vite static site generation
- File-based routing with Vite
- Generating static HTML with Vite
- Vite blog generators
- Documentation sites
- Minimal static site generators
- HTML‑first Vite projects
License
MIT
