Skip to main content
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>
app
Hono
required
The Hono application instance to generate static files from
fsModule
FileSystemModule
required
File system module with writeFile and mkdir methods (e.g., Node.js fs/promises)
options
ToSSGOptions
Configuration options for static generation
return
Promise<ToSSGResult>
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
return
MiddlewareHandler
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
c
Context
required
The Hono context object
return
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
return
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
return
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<...>>
app
Hono
required
The Hono application instance
beforeRequestHook
BeforeRequestHook
Hook called before each request
afterResponseHook
AfterResponseHook
Hook called after each response
concurrency
number
Number of concurrent requests (default: 2)
return
Generator
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>
data
Promise<object>
required
Promise resolving to content data
fsModule
FileSystemModule
required
File system module
outDir
string
required
Output directory path
extensionMap
Record<string, string>
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

Build docs developers (and LLMs) love