Hono provides a comprehensive JSX implementation that works seamlessly for both server-side and client-side rendering. Unlike traditional JSX libraries, Hono’s JSX is designed to be lightweight, fast, and works directly with Hono’s request/response model.
What is Hono JSX?
Hono JSX is a React-like JSX implementation that:
Works on the server - Renders directly to HTML strings
Works on the client - Provides DOM rendering capabilities
React-compatible - Uses familiar React APIs and hooks
Type-safe - Full TypeScript support with proper type inference
No build required - Works with standard JSX transpilation
Hono JSX is compatible with React 19 APIs and provides hooks like useState, useEffect, useContext, and more.
TypeScript Configuration
To use JSX in your Hono project, configure your tsconfig.json:
For Server-Side Rendering
{
"compilerOptions" : {
"jsx" : "react-jsx" ,
"jsxImportSource" : "hono/jsx"
}
}
For Client-Side Rendering
{
"compilerOptions" : {
"jsx" : "react-jsx" ,
"jsxImportSource" : "hono/jsx/dom"
}
}
Basic Usage
Here’s a simple example using JSX in a Hono application:
import { Hono } from 'hono'
const app = new Hono ()
const Layout = ({ children } : { children : any }) => {
return (
< html >
< body >
< header >
< h1 > My App </ h1 >
</ header >
< main > { children } </ main >
</ body >
</ html >
)
}
const Home = () => {
return (
< div >
< h2 > Welcome </ h2 >
< p > This is rendered with Hono JSX! </ p >
</ div >
)
}
app . get ( '/' , ( c ) => {
return c . html (
< Layout >
< Home />
</ Layout >
)
})
export default app
JSX Components
Hono JSX supports functional components with props:
type GreetingProps = {
name : string
age ?: number
}
const Greeting = ({ name , age } : GreetingProps ) => {
return (
< div >
< h2 > Hello, { name } ! </ h2 >
{ age && < p > You are { age } years old. </ p > }
</ div >
)
}
app . get ( '/greet/:name' , ( c ) => {
const name = c . req . param ( 'name' )
return c . html ( < Greeting name = { name } age = { 25 } /> )
})
Props and Children
Components receive props and children just like React:
import type { FC , PropsWithChildren } from 'hono/jsx'
type CardProps = PropsWithChildren <{
title : string
variant ?: 'primary' | 'secondary'
}>
const Card : FC < CardProps > = ({ title , variant = 'primary' , children }) => {
return (
< div className = { `card card- ${ variant } ` } >
< h3 > { title } </ h3 >
< div className = "card-body" >
{ children }
</ div >
</ div >
)
}
// Usage
< Card title = "My Card" variant = "primary" >
< p > Card content goes here </ p >
</ Card >
hono/jsx vs hono/jsx/dom
Hono provides two JSX implementations:
hono/jsx Server-side rendering
Renders to HTML strings
Used in route handlers with c.html()
Optimized for server performance
Supports async components
hono/jsx/dom Client-side rendering
Renders to actual DOM
Used for interactive UIs
React-compatible hooks
Supports hydration
Attributes and HTML Properties
Hono JSX supports standard HTML attributes:
const Form = () => {
return (
< form method = "POST" action = "/submit" >
< input
type = "text"
name = "username"
placeholder = "Enter username"
required
/>
< input
type = "password"
name = "password"
minLength = { 8 }
/>
< button type = "submit" > Submit </ button >
</ form >
)
}
Style Objects
Styles can be passed as objects:
const StyledDiv = () => {
const style = {
backgroundColor: 'blue' ,
color: 'white' ,
padding: '20px' ,
borderRadius: '8px'
}
return < div style = { style } > Styled content </ div >
}
Fragments
Use fragments to return multiple elements:
import { Fragment } from 'hono/jsx'
const List = () => {
return (
<>
< li > Item 1 </ li >
< li > Item 2 </ li >
< li > Item 3 </ li >
</>
)
}
// Or explicitly
const ListExplicit = () => {
return (
< Fragment >
< li > Item 1 </ li >
< li > Item 2 </ li >
</ Fragment >
)
}
Conditional Rendering
Render elements conditionally:
const UserStatus = ({ isLoggedIn , username } : { isLoggedIn : boolean ; username ?: string }) => {
return (
< div >
{ isLoggedIn ? (
< p > Welcome back, { username } ! </ p >
) : (
< p > Please log in </ p >
) }
</ div >
)
}
Lists and Keys
Render lists with keys:
type TodoItem = {
id : number
text : string
completed : boolean
}
const TodoList = ({ items } : { items : TodoItem [] }) => {
return (
< ul >
{ items . map (( item ) => (
< li key = { item . id } className = { item . completed ? 'completed' : '' } >
{ item . text }
</ li >
)) }
</ ul >
)
}
dangerouslySetInnerHTML
Insert raw HTML (use with caution):
const RawHTML = ({ html } : { html : string }) => {
return < div dangerouslySetInnerHTML = { { __html: html } } />
}
Only use dangerouslySetInnerHTML with trusted content to prevent XSS attacks.
API Compatibility
Hono JSX exports a version that indicates React API compatibility:
import { version } from 'hono/jsx'
console . log ( version ) // '19.0.0-hono-jsx'
Source: src/jsx/base.ts:442
Next Steps
Server Rendering Learn about server-side JSX rendering with Hono
DOM Rendering Build interactive UIs with client-side rendering
Streaming Stream HTML with Suspense-like patterns
Helpers Use the HTML helper for advanced rendering