Public
Likewow_
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v45View latest version
The starter template is designed for static serving (Twind shim + esm.sh imports, no build step), so it maps to Val Town pretty directly. Here's the approach:
1. Create an HTTP val that serves the HTML
// your-app.ts (HTTP val)
export default async function(req: Request): Promise<Response> {
const url = new URL(req.url);
// Serve component files from companion vals
if (url.pathname === "/style.css") {
return new Response(styleCSS, { headers: { "content-type": "text/css" } });
}
if (url.pathname === "/index.tsx") {
return new Response(indexTSX, { headers: { "content-type": "application/typescript" } });
}
return new Response(indexHTML, { headers: { "content-type": "text/html" } });
}
2. The challenge: multi-file structure
Val Town vals are single files, so you have two options:
- Inline everything — Collapse the components into one or two vals. This is simplest but loses the nice file structure.
- Use Val Town's module imports — Publish each component as its own val, then change the import paths in
index.tsxtohttps://esm.town/v/yourname/Buttonetc.
3. Practical steps
- Create a new HTTP val
- Paste the
index.htmlcontent as a template string, updating the<script src>and<link href>paths to route through your val's request handler - Either inline all components into the val, or publish them as separate vals and update import paths from
./components/Button.tsxtohttps://esm.town/v/yourname/Button
4. Quickest start — just paste index.html into a val and inline style.css:
const html = `<!DOCTYPE html>
<html lang="en" hidden>
<head>
<!-- paste the <head> contents from index.html -->
<style>
/* paste style.css contents here instead of linking */
</style>
</head>
<body class="min-h-screen font-sans bg-gray-50">
<div id="root"></div>
<script type="module">
// paste index.tsx contents here, or import from esm.town
</script>
</body>
</html>`;
export default () => new Response(html, { headers: { "content-type": "text/html" } });
The esm.sh React imports and Twind CDN shim will work as-is in Val Town since they're already URL-based. The main adaptation is flattening the file structure to fit Val Town's single-file-per-val model.