Skip to main content
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

tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

For Client-Side Rendering

tsconfig.json
{
  "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

Build docs developers (and LLMs) love