About
HonoX - Hono based meta framework
# HonoX
**HonoX** is a simple and fast meta-framework for creating full-stack websites or Web APIs - (formerly _[Sonik](https://github.com/sonikjs/sonik)_). It stands on the shoulders of giants; built on [Hono](https://hono.dev/), [Vite](https://vitejs.dev/), and UI libraries.
**Note**: _HonoX is currently in the "alpha stage". Breaking changes are introduced within the same major version, following [semantic versioning for zerover](https://semver.org/#spec-item-4)._
## Features
- **File-based routing** - You can create a large application like Next.js.
- **Fast SSR** - Rendering is ultra-fast thanks to Hono.
- **BYOR** - You can bring your own renderer, not only one using hono/jsx.
- **Islands hydration** - If you want interactions, create an island. JavaScript is hydrated only for it.
- **Middleware** - It works as Hono, so you can use a lot of Hono's middleware.
## Installing
You can install the `honox` package from the npm.
```txt
npm install hono honox
```
## Starter template
If you are starting a new HonoX project, use the `hono-create` command. Run the following and choose `x-basic` (use the arrow keys to find the option).
```txt
npm create hono@latest
```
## Get Started - Basic
Let's create a basic HonoX application using hono/jsx as a renderer. This application has no client JavaScript and renders JSX on the server side.
### Project Structure
Below is a typical project structure for a HonoX application.
```
…
```
### `vite.config.ts`
The minimum Vite setup for development is as follows:
```ts
import { defineConfig } from 'vite'
import honox from 'honox/vite'
export default defineConfig({
plugins: [honox()],
})
```
### Server Entry File
A server entry file is required. The file should be placed at `app/server.ts`. This file is first called by the Vite during the development or build phase.
In the entry file, simply initialize your app using the `createApp()` function. `app` will be an instance of Hono, so you can use Hono's middleware and the `showRoutes()` in `hono/dev`.
```ts
// app/server.ts
import { createApp } from 'honox/server'
import { showRoutes } from 'hono/dev'
const app = createApp()
showRoutes(app)
export default app
```
### Routes
There are three ways to define routes.
#### 1. `createRoute()`
Each route should return an array of `Handler | MiddlewareHandler`. `createRoute()` is a helper function to return it. You can write a route for a GET request with `default export`.
```tsx
// app/routes/index.tsx
// `createRoute()` helps you create handlers
import { createRoute } from 'honox/factory'
export default createRoute((c) => {
return c.render(
)
})
```
You can also handle methods other than GET by `export` `POST`, `PUT`, and `DELETE`.
```tsx
// app/routes/index.tsx
import { createRoute } from 'honox/factory'
import { getCookie, setCookie } from 'hono/cookie'
export const POST = createRoute(async (c) => {
const { name } = await c.req.parseBody<{ name: string }>()
setCookie(c, 'name', name)
return c.redirect('/')
})
export default createRoute((c) => {
const name = getCookie(c, 'name') ?? 'no name'
return c.render(
)
})
```
#### 2. Using a Hono instance
You can create API endpoints by exporting an instance of the Hono object.
```ts
// app/routes/about/index.ts
import { Hono } from 'hono'
const app = new Hono()
// matches `/about/:name`
app.get('/:name', (c) => {
const name = c.req.param('name')
return c.json({
'your name is': name,
})
})
export default app
```
#### 3. Just return JSX
Or simply, you can just return JSX.
```tsx
// app/routes/index.tsx
export default function Home(_c: Context) {
return
Welcome!
}
```
### Renderer
Define your renderer - the middleware that does `c.setRender()` - by writing it in `_renderer.tsx`.
Before writing `_renderer.tsx`, write the Renderer type definition in `global.d.ts`.
```ts
// app/global.d.ts
import type {} from 'hono'
type Head = {
title?: string
}
declare module 'hono' {
interface ContextRenderer {
(content: string | Promise, head?: Head): Response | Promise
}
}
```
The JSX Renderer middleware allows you to create a Renderer as follows:
```tsx
// app/routes/_renderer.tsx
import { jsxRenderer } from 'hono/jsx-renderer'
export default jsxRenderer(({ children, title }) => {
return (
{title ? {title} : <></>}
{children}
)
})
```
The `_renderer.tsx` is applied under each directory, and the `app/routes/posts/_renderer.tsx` is applied in `app/routes/posts/*`.
### Not Found page
You can write a custom Not Found page in `_404.tsx`.
```tsx
// app/routes/_404.tsx
import { NotFoundHandler } from 'hono'
const handler: NotFoundHandler = (c) => {
return c.render(Sorry, Not Found...
)
}
export default handler
```
### Error Page
You can write a custom Error page in `_error.tsx`.
```tsx
// app/routes/_error.tsx
import { ErrorHandler } from 'hono'
const handler: ErrorHandler = (e, c) => {
return c.render(Error! {e.message}
)
}
export default handler
```
## Get Started - with Client
Let's create an application that includes a client side. Here, we will use hono/jsx/dom.
### Project Structure
Below is the project structure of a minimal application including a client side:
```txt
.
├── app
│ ├── client.ts // client entry file
│ ├── global.d.ts
│ ├── islands
│ │ └── counter.tsx // island component
│ ├── routes
│ │ ├── _renderer.tsx
│ │ └── index.tsx
│ └── server.ts
├── package.json
├── tsconfig.json
└── vite.config.ts
```
### Renderer
This is a `_renderer.tsx`, which will load the `/app/client.ts` entry file for the client. It will load the JavaScript file for production according to the variable `import.meta.env.PROD`. And renders the inside of `` if there are islands on that page.
```
…
```
If you have a manifest file in `dist/.vite/manifest.json`, you can easily write it using `