DEV Community

Cover image for Building a Unified React App with HONO
Soham Dalal ⚛️
Soham Dalal ⚛️

Posted on

Building a Unified React App with HONO

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
Enter fullscreen mode Exit fullscreen mode
  • 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

Enter fullscreen mode Exit fullscreen mode
  • 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"]} />);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)