serveFile() is a Val Town utility function that serves files from your project with proper content types and handling.
Content-Type header based on file extensionimport { serveFile } from "https://esm.town/v/std/utils/index.ts";
import { Hono } from "https://esm.sh/hono";
const app = new Hono();
// Serve a specific file
app.get("/", (c) => serveFile("/frontend/index.html", import.meta.url));
// Serve any file in the frontend directory
app.get("/frontend/*", (c) => serveFile(c.req.path, import.meta.url));
path (string) - The file path within your project (e.g., "/frontend/index.html")import.meta.url - Always pass this as the second parameter (tells the function where your project is located)serveFile() automatically sets the correct Content-Type header:
| File Extension | Content-Type |
|---|---|
.html | text/html |
.css | text/css |
.js, .mjs | application/javascript |
.json | application/json |
.png | image/png |
.jpg, .jpeg | image/jpeg |
.svg | image/svg+xml |
.txt | text/plain |
.pdf | application/pdf |
| And many more... |
// Serve everything in frontend/ and shared/
app.get("/frontend/*", (c) => serveFile(c.req.path, import.meta.url));
app.get("/shared/*", (c) => serveFile(c.req.path, import.meta.url));
import { readFile } from "https://esm.town/v/std/utils/index.ts";
app.get("/", async (c) => {
// Read the HTML file manually to modify it
let html = await readFile("/frontend/index.html", import.meta.url);
// Inject initial data to avoid extra round-trips
const initialData = await fetchInitialData();
const dataScript = `<script>
window.__INITIAL_DATA__ = ${JSON.stringify(initialData)};
</script>`;
html = html.replace("</head>", `${dataScript}</head>`);
return c.html(html);
});
app.get("*", async (c) => {
// Try to serve the requested file first
try {
return await serveFile(c.req.path, import.meta.url);
} catch {
// If file doesn't exist, serve index.html (for client-side routing)
return serveFile("/frontend/index.html", import.meta.url);
}
});
app.get("/assets/*", async (c) => {
const response = await serveFile(c.req.path, import.meta.url);
// Add cache headers for static assets
response.headers.set("Cache-Control", "public, max-age=31536000");
return response;
});
When you call serveFile(c.req.path, import.meta.url):
import.meta.url to locate your projectResponse object with the file contents and headersimport.meta.url as the second parameter/ (e.g., "/frontend/index.html", not "frontend/index.html")ā DON'T use Hono's serveStatic middleware in Val Town:
// This doesn't work in Val Town
import { serveStatic } from 'https://esm.sh/hono/middleware';
ā DO use Val Town's serveFile utility:
// This works perfectly in Val Town
import { serveFile } from "https://esm.town/v/std/utils/index.ts";
readFile() - Read file contents as a string (useful for modifying before serving)listFiles() - List all files in the project