1.0.1 • Published 7 months ago
@hono/react-renderer v1.0.1
React Renderer Middleware
React Renderer Middleware allows for the easy creation of a renderer based on React for Hono.
Installation
npm i @hono/react-renderer react react-dom hono
npm i -D @types/react @types/react-domSettings
tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}If you are using Vite, add ssr external config to vite.config.ts:
import build from '@hono/vite-cloudflare-pages'
import devServer from '@hono/vite-dev-server'
import adapter from '@hono/vite-dev-server/cloudflare'
import { defineConfig } from 'vite'
export default defineConfig({
ssr: {
external: ['react', 'react-dom'], // <== add
},
plugins: [
build(),
devServer({
adapter,
entry: 'src/index.tsx',
}),
],
})Usage
Basic
import { Hono } from 'hono'
import { reactRenderer } from '@hono/react-renderer'
const app = new Hono()
app.get(
'*',
reactRenderer(({ children }) => {
return (
<html>
<body>
<h1>React + Hono</h1>
<div>{children}</div>
</body>
</html>
)
})
)
app.get('/', (c) => {
return c.render(<p>Welcome!</p>)
})Extending Props
You can define types of Props:
declare module '@hono/react-renderer' {
interface Props {
title: string
}
}Then, you can use it in the reactRenderer() function and pass values as a second argument to c.render():
app.get(
'*',
reactRenderer(({ children, title }) => {
return (
<html>
<head>
<title>{title}</title>
</head>
<body>
<div>{children}</div>
</body>
</html>
)
})
)
app.get('/', (c) => {
return c.render(<p>Welcome!</p>, {
title: 'Top Page',
})
})useRequestContext()
You can get an instance of Context in a function component:
const Component = () => {
const c = useRequestContext()
return <p>You access {c.req.url}</p>
}
app.get('/', (c) => {
return c.render(<Component />)
})Options
docType
If you set it true, DOCTYPE will be added:
app.get(
'*',
reactRenderer(
({ children }) => {
return (
<html>
<body>
<div>{children}</div>
</body>
</html>
)
},
{
docType: true,
}
)
)The HTML is the following:
<!DOCTYPE html>
<html>
<body>
<div><p>Welcome!</p></div>
</body>
</html>You can specify the docType as you like.
app.get(
'*',
reactRenderer(
({ children }) => {
return (
<html>
<body>
<div>{children}</div>
</body>
</html>
)
},
{
docType:
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
}
)
)stream
It enables returning a streaming response. You can use a Suspense with it:
import { reactRenderer } from '@hono/react-renderer'
import { Suspense } from 'react'
app.get(
'*',
reactRenderer(
({ children }) => {
return (
<html>
<body>
<div>{children}</div>
</body>
</html>
)
},
{
stream: true,
}
)
)
let done = false
const Component = () => {
if (done) {
return <p>Done!</p>
}
throw new Promise((resolve) => {
done = true
setTimeout(resolve, 1000)
})
}
app.get('/', (c) => {
return c.render(
<Suspense fallback='loading...'>
<Component />
</Suspense>
)
})Limitation
A streaming feature is not available on Vite or Vitest.
Author
Yusuke Wada https://github.com/yusukebe
License
MIT