FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
cricks_unmixed4u
cricks_unmixed4ullm-tips
Public
Like
1
llm-tips
Home
Code
9
.vscode
1
articles
1
src
1
tips
5
.cursorrules
knowledge-voting-addition.md
knowledge.md
H
main.tsx
template.html
Branches
1
Pull requests
Remixes
History
Environment variables
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.
Sign up now
Code
/
knowledge.md
Code
/
knowledge.md
Search
8/6/2025
Viewing readonly version of main branch: v325
View latest version
knowledge.md

Next step - Refactor to use jsxRenderer

refactor static HTML to jsxRenderer

This guide shows how to migrate a static HTML file served by Hono into Hono’s jsxRenderer middleware and apply it to dynamic tip pages such as /tips/youtube-transcript-to-notes.


1. Original setup (serving static file)

Create val
import { Hono } from 'hono' import { serveStatic } from 'hono/static' const app = new Hono() app.use('/', serveStatic({ root: './public', spa: false })) app.listen({ port: 3000 })
  • public/index.html contains your HTML template.

2. Install and import jsxRenderer

npm install hono jsx-renderer
Create val
import { jsxRenderer } from 'hono/jsx-renderer'

3. Convert index.html into a JSX component

  1. Create src/Layout.tsx:

    Create val
    import React from 'react' export function Layout({ children, title }: { children: React.ReactNode; title?: string }) { return ( <html lang="en"> <head> <meta charSet="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>{title || 'My App'}</title> <link rel="stylesheet" href="/styles.css" /> </head> <body> <header> <nav><a href="/">Home</a> | <a href="/tips">Tips</a></nav> </header> <main id="root">{children}</main> <script src="/bundle.js"></script> </body> </html> ) }
  2. Move static assets (styles.css, bundle.js, images) into public/.

4. Apply jsxRenderer globally

Create val
import { Hono } from 'hono' import { jsxRenderer } from 'hono/jsx-renderer' import { Layout } from './Layout' const app = new Hono() // Apply renderer for all GET requests app.use('GET', '*', jsxRenderer(Layout))

5. Define static and dynamic routes

  1. Home route:

    Create val
    app.get('/', (c) => { return c.render( <> <h1>Welcome to My App</h1> <p>This replaces index.html</p> </> ) })
  2. Tips list:

    Create val
    import tips from './data/tips.json' // array of { slug, title } app.get('/tips', (c) => { return c.render( <ul> {tips.map((tip) => ( <li key={tip.slug}> <a href={`/tips/${tip.slug}`}>{tip.title}</a> </li> ))} </ul> ) })
  3. Dynamic tip page:

    Create val
    import fs from 'fs' import path from 'path' app.get('/tips/:slug', async (c) => { const { slug } = c.req.param() const file = path.join(process.cwd(), 'tips', `${slug}.html`) const content = fs.readFileSync(file, 'utf-8') return c.render(<div dangerouslySetInnerHTML={{ __html: content }} />, { title: slug.replace('-', ' ') }) })

6. Run and verify

npm run dev
  • Visit http://localhost:3000/ for home.
  • Visit http://localhost:3000/tips for list of tips.
  • Visit http://localhost:3000/tips/youtube-transcript-to-notes to see a rendered tip.

Next steps?

  • Nested layouts (e.g. tutorial pages under /tips)
  • Streaming rendering for large content
  • Context‑aware UI (useRequestContext) for breadcrumbs and metadata
Go to top
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Product
FeaturesPricing
Developers
DocsStatusAPI ExamplesNPM Package Examples
Explore
ShowcaseTemplatesNewest ValsTrending ValsNewsletter
Company
AboutBlogCareersBrandhi@val.town
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.