Parts of the SSG Helper are experimental. The API may change in future versions.
The SSG (Static Site Generation) Helper enables pre-rendering of Hono applications to static files, allowing you to generate static HTML, CSS, JSON, and other assets at build time.
Import
import { toSSG, fetchRoutesContent, saveContentToFile } from 'hono/ssg'
import { ssgParams, isSSGContext, disableSSG, onlySSG } from 'hono/ssg'
import type { ToSSGOptions, ToSSGResult } from 'hono/ssg'
Functions
toSSG()
Generates static files from a Hono application.
function toSSG(
app: Hono<any, any, any>,
fsModule: FileSystemModule,
options?: ToSSGOptions
): Promise<ToSSGResult>
The Hono application instance to generate static files from
File system module with writeFile and mkdir methods (e.g., Node.js fs/promises)
Configuration options for static generation
Result object containing:
success: boolean indicating if generation succeeded
files: array of generated file paths
error: Error object if generation failed
Example
import { Hono } from 'hono'
import { toSSG } from 'hono/ssg'
import fs from 'fs/promises'
const app = new Hono()
app.get('/', (c) => c.html('<h1>Home</h1>'))
app.get('/about', (c) => c.html('<h1>About</h1>'))
app.get('/api/data', (c) => c.json({ message: 'Hello' }))
// Generate static files
const result = await toSSG(app, fs, {
dir: './dist'
})
if (result.success) {
console.log(`Generated ${result.files.length} files`)
result.files.forEach(file => console.log(file))
} else {
console.error('Generation failed:', result.error)
}
ssgParams()
Middleware to define parameters for dynamic routes during SSG.
function ssgParams<E extends Env = Env>(
params: SSGParams
): MiddlewareHandler<E>
function ssgParams<E extends Env = Env>(
generateParams: (c: Context<E>) => SSGParams | Promise<SSGParams>
): MiddlewareHandler<E>
params
SSGParams | Function
required
Either an array of parameter objects or a function that returns them
Middleware that provides parameters for static generation
Example
import { ssgParams } from 'hono/ssg'
app.get(
'/posts/:id',
ssgParams(async (c) => {
// Generate pages for these post IDs
return [
{ id: '1' },
{ id: '2' },
{ id: '3' }
]
}),
(c) => {
const id = c.req.param('id')
return c.html(`<h1>Post ${id}</h1>`)
}
)
// Static array
app.get(
'/users/:userId',
ssgParams([
{ userId: 'alice' },
{ userId: 'bob' }
]),
(c) => {
const userId = c.req.param('userId')
return c.html(`<h1>User: ${userId}</h1>`)
}
)
isSSGContext()
Checks if the current request is running in SSG context.
function isSSGContext(c: Context): boolean
true if running in SSG context, false otherwise
Example
import { isSSGContext } from 'hono/ssg'
app.get('/page', (c) => {
if (isSSGContext(c)) {
// Render static version
return c.html('<h1>Static Page</h1>')
} else {
// Render dynamic version with live data
return c.html(`<h1>Dynamic Page - ${new Date()}</h1>`)
}
})
disableSSG()
Middleware to prevent a route from being statically generated.
function disableSSG(): MiddlewareHandler
Middleware that disables SSG for the route
Example
import { disableSSG } from 'hono/ssg'
// This route will not be statically generated
app.get('/admin', disableSSG(), (c) => {
return c.html('<h1>Admin Panel</h1>')
})
// Dynamic API route
app.get('/api/current-time', disableSSG(), (c) => {
return c.json({ time: new Date().toISOString() })
})
onlySSG()
Middleware to make a route only available during SSG (returns 404 at runtime).
function onlySSG(): MiddlewareHandler
Middleware that makes the route SSG-only
Example
import { onlySSG } from 'hono/ssg'
// This route only exists in static builds
app.get('/sitemap.xml', onlySSG(), (c) => {
const sitemap = generateSitemap()
return c.text(sitemap, 200, {
'Content-Type': 'application/xml'
})
})
fetchRoutesContent()
Generator function that fetches content for all routes.
function* fetchRoutesContent<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'>(
app: Hono<E, S, BasePath>,
beforeRequestHook?: BeforeRequestHook,
afterResponseHook?: AfterResponseHook,
concurrency?: number
): Generator<Promise<...>>
The Hono application instance
Hook called before each request
Hook called after each response
Number of concurrent requests (default: 2)
Generator yielding Promises that resolve to route content
saveContentToFile()
Saves content to a file.
function saveContentToFile(
data: Promise<{ routePath: string; content: string | ArrayBuffer; mimeType: string } | undefined>,
fsModule: FileSystemModule,
outDir: string,
extensionMap?: Record<string, string>
): Promise<string | undefined>
Promise resolving to content data
Custom MIME type to file extension mapping
return
Promise<string | undefined>
Path to the saved file, or undefined if save was skipped
Types
ToSSGOptions
interface ToSSGOptions {
dir?: string // Output directory (default: './static')
concurrency?: number // Concurrent requests (default: 2)
extensionMap?: Record<string, string> // MIME type to extension mapping
plugins?: SSGPlugin[] // SSG plugins
}
ToSSGResult
interface ToSSGResult {
success: boolean
files: string[]
error?: Error
}
SSGParams
type SSGParams = Array<Record<string, string>>
// Example:
const params: SSGParams = [
{ id: '1', slug: 'first' },
{ id: '2', slug: 'second' }
]
FileSystemModule
interface FileSystemModule {
writeFile(path: string, data: string | Uint8Array): Promise<void>
mkdir(path: string, options: { recursive: boolean }): Promise<void | string>
}
Advanced Usage
Custom Extension Mapping
import { toSSG } from 'hono/ssg'
import fs from 'fs/promises'
const result = await toSSG(app, fs, {
dir: './dist',
extensionMap: {
'text/html': 'html',
'application/json': 'json',
'text/xml': 'xml',
'application/rss+xml': 'rss'
}
})
Build Script Example
// build.ts
import { Hono } from 'hono'
import { toSSG } from 'hono/ssg'
import fs from 'fs/promises'
const app = new Hono()
// Define your routes
app.get('/', (c) => c.html('<h1>Home</h1>'))
app.get('/about', (c) => c.html('<h1>About</h1>'))
// Generate static site
async function build() {
console.log('Generating static site...')
const result = await toSSG(app, fs, {
dir: './dist',
concurrency: 4
})
if (result.success) {
console.log(`✓ Generated ${result.files.length} files`)
} else {
console.error('✗ Build failed:', result.error)
process.exit(1)
}
}
build()
Dynamic Routes with Database
import { ssgParams } from 'hono/ssg'
app.get(
'/blog/:slug',
ssgParams(async () => {
// Fetch all blog post slugs from database
const posts = await db.posts.findAll()
return posts.map(post => ({ slug: post.slug }))
}),
async (c) => {
const slug = c.req.param('slug')
const post = await db.posts.findBySlug(slug)
return c.html(`
<article>
<h1>${post.title}</h1>
<div>${post.content}</div>
</article>
`)
}
)
File Output
Generated files follow these patterns:
/ → /index.html
/about → /about.html
/about/ → /about/index.html
/api/data → /api/data.json (for JSON responses)
/feed.xml → /feed.xml