Building a Unified React App with HONO
Published: 2024-05-16 06:21:19

## Introduction
Exploring the latest upgrades within the Hono framework, we uncover its strengthened alliance with Vite, presenting developers with a more seamless fusion of React SPAs and Hono APIs. Within this article, our aim is to unveil a straightforward approach to project development, where constructing a React SPA alongside an API server becomes an effortless endeavor with the aid of Hono's enhanced capabilities.

## Creating a Hono Project using bun package manager 
Creating a project with the Hono framework with using the bun package manager is very simple. Follow these steps:
- First, open the terminal and run the following command to create a new Hono project:

```
bun create hono app
```
- Next, install the necessary packages to use Reactjs:

```
bun add react react-dom zod @hono/zod-validator
bun add -D vite @hono/vite-dev-server @types/react @types/react-dom bun-types @hono/vite-cloudflare-pages

```
- If you are using TypeScript, change `jsxImportSource` in `tsconfig.json` from `hono/jsx` to `react`.
- Finally, edit the `vite.config.ts` file and specify the react libraries in `ssr.external`. This ensures that react functions correctly during the server side rendering.

Now, the basic setup of a project combining Hono and React is complete. Then we will move on to the client side and server side implementations.

## Client Side Implementation
For the client side implementation, create a `src/client.jsx` file. In this file define the Ui components using React. Here is the simple example :
```
import { createRoot } from "react-dom/client";
import { hc } from "hono/client";
import { AppType } from ".";

interface TodoListProps {
  todos: string[];
}

const client = hc<AppType>("/");

function TodoList({ todos }: TodoListProps) {
  return (
    <div>
      <a href="/">
        <h1>Todo App</h1>
      </a>
      <div>
        <input type="text" name="title" />
        <button
          type="submit"
          onClick={() => {
            const title = document.querySelector("input")?.value;
            client.todos.$post({
              form: {
                title
              },
            });
          }}
        >
          Add
        </button>
      </div>
      <ul>
        {todos.map((todo, i) => (
          <li key={i}>
            {todo}
            <button
              onClick={() => {
                client.todos[":id"].$delete({
                  param: {
                    id: i.toString(),
                  },
                });
              }}
            >
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

const domNode = document.getElementById("root")!;
const root = createRoot(domNode);
root.render(<TodoList todos={["sample"]} />);
```

## Server Side Implementation
For the server side implementation, create a `src/server.ts` file and define the API endpoints using hono. Here is a simple example. With this, the API server that communicates with react application created on the client side is complete. By using Hono, you can easily set up API endpoints and establish communication with the react application.
```
import { Hono } from "hono";
import { renderToString } from "react-dom/server";
import { z } from "zod";
import { zValidator } from '@hono/zod-validator'

const app = new Hono();

app.get("/", (c) => {
  return c.html(
    renderToString(
      <html>
        <head>
          <meta charSet="utf-8" />
          <meta content="width=device-width, initial-scale=1" name="viewport" />
          {import.meta.env.PROD ? (
            <script type="module" src="/static/client.js"></script>
          ) : (
            <script type="module" src="/src/client.tsx"></script>
          )}
        </head>
        <body>
          <div id="root"></div>
        </body>
      </html>
    )
  );
});

const schema = z.object({
  title: z.string().min(1),
});

app.post(
  "/todos",
  zValidator("form", schema),
  (c) => {
    const { title } = c.req.valid('form')
    // TODO: DB insert
    return c.json({title})
  }
);

app.delete(
  "/todos/:id",
  (c) => {
    const id = c.req.param.id
    // TODO: DB delete
    return c.json({ id });
  }
);

export type AppType = typeof app;

export default app;
```

## Building & Deploying the project
To build and deploy the project, use the following command:

1. Build client and server:
`vite build --mode client && vite build`

2. Deploy to  Cloudflare Pages:
`wrangler pages deploy dist`

Additionally, you can deploy this using Vercel. However, in my case, I am using the Bun package manager, and unfortunately, Vercel deployment is not working for me. I encounter errors when attempting to deploy this on Vercel.

## Conclusion 
In summary, this article demonstrated the streamlined process of constructing a project incorporating a React Single Page Application (SPA) and an API server using the Hono framework. By harnessing the synergy between Hono and Vite, developers can seamlessly navigate through development, building, and deployment phases. Hono emerges as a valuable tool for crafting personal or internal applications not reliant on SEO, including WebView applications.