Capabilities
Static Websites
Serve an HTML, CSS, and JavaScript site from a Dynamic App.
An app is a directory with a package.json and a server entrypoint. A static
site is the same directory plus a public/ folder and a handler that serves
it. View the complete example.
import { readFile } from "node:fs/promises";
import { Hono } from "hono";
const contentTypes: Record<string, string> = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "text/javascript; charset=utf-8",
};
const app = new Hono();
// Serve every file under public/. "/" maps to public/index.html. The build
// bundles this entrypoint next to public/, so paths resolve from the bundle.
app.get("/*", async (c) => {
const path = c.req.path.endsWith("/")
? `${c.req.path}index.html`
: c.req.path;
const type = contentTypes[path.slice(path.lastIndexOf("."))];
if (!type || path.includes("..")) return c.notFound();
try {
const file = await readFile(new URL(`./public${path}`, import.meta.url));
return c.body(file, 200, { "content-type": type });
} catch {
return c.notFound();
}
});
export default app;
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Static site on Dynamic Apps</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<main>
<h1>Static sites scale to zero too.</h1>
<p id="status">Loading JavaScript…</p>
</main>
<script type="module" src="./app.js"></script>
</body>
</html>
{
"name": "static-website-app",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "src/index.ts",
"dependencies": {
"hono": "4.13.3"
}
}
Deploy the directory and open /apps/static-website/:
// Deploy the site directory. Its package.json and src/index.ts serve public/.
await deployApp({
appId: "static-website",
source: new URL("../fixtures/app/", import.meta.url),
});
Directories that contain only static files are rejected. Every app needs a
package.json and an entrypoint that default-exports a fetch handler.