DEV Community

Cover image for Building Full-Stack Apps with Bun, React, and Hono: A Modern Approach
Sai Madhan
Sai Madhan

Posted on

Building Full-Stack Apps with Bun, React, and Hono: A Modern Approach

I've been exploring Bun lately and I'm impressed by its capabilities. When I decided to try building a full-stack application using Bun with React and Hono, I discovered a surprisingly elegant pattern that I wanted to share.

If you're looking for a modern, performant way to build full-stack applications, this combination might be exactly what you need.

Why This Stack?

  • Bun: Blazing fast JavaScript runtime with built-in bundling and excellent React support
  • React: The frontend framework we all know and love
  • Hono: Lightweight, fast web framework with excellent TypeScript support and middleware ecosystem

Setting Up Your Full-Stack App

Let's start by creating a new Bun application with React support:

bun init --react=shadcn
Enter fullscreen mode Exit fullscreen mode

This gives us a React application with shadcn/ui support out of the box. But here's where it gets interesting—Bun's routing system allows us to seamlessly integrate a Hono API server.

The Setup: Combining the Best of Both Worlds

Here's how we can set up our server to handle both API routes and React serving:

import { serve } from "bun";
import { Hono } from "hono";
import index from "./index.html";
import API from "./api";

const app = new Hono();
app.route("/api/", API);

const server = serve({
  routes: {
    "/api/*": app.fetch,  // API routes handled first
    "/*": index,          // React app serves everything else
  },
  development: process.env.NODE_ENV !== "production" && {
    hmr: true,
    console: true,
  },
});

console.log(`🚀 Server running at ${server.url}`);
Enter fullscreen mode Exit fullscreen mode

That's it! By mapping /api/* to app.fetch, we get a clean separation between our API and frontend, with both served from the same process.

Alternative Approaches

You could certainly build your API using Bun's built-in methods directly in the routes configuration:

const server = serve({
  routes: {
    "/api/users": {
      async GET(req) {
        return Response.json({ users: [...] });
      },
    },
    "/*": index,
  },
});
Enter fullscreen mode Exit fullscreen mode

Or use Hono's JSX capabilities to build React-style components:

import { Hono } from 'hono'
import { jsx } from 'hono/jsx'

const app = new Hono()
app.get('/', (c) => {
  return c.html(<h1>Hello Hono!</h1>)
})
Enter fullscreen mode Exit fullscreen mode

However, I prefer the approach outlined above because it gives us access to the full React ecosystem—Tailwind CSS, shadcn/ui, and countless React libraries—while still providing a powerful backend with Hono's extensive middleware collection.

Project Structure

Here's how I like to organize these projects:

├── index.ts          # Main server file
├── main.tsx          # React entry point
├── index.html        # HTML template
├── app/
│   └── App.tsx       # React components
├── api/
│   ├── index.ts      # API router
│   └── v1/
│       └── index.ts  # Versioned API routes
Enter fullscreen mode Exit fullscreen mode

API Setup with Hono

api/index.ts:

import { Hono } from "hono";
import V1Routes from "./v1";

const API = new Hono();
API.route("/v1/", V1Routes);

export default API;
Enter fullscreen mode Exit fullscreen mode

api/v1/index.ts:

import { Hono } from "hono";

const V1Routes = new Hono();

V1Routes.get("/", (c) => c.json({ message: "Welcome to V1" }));

V1Routes.get("/books", (c) => 
  c.json({ 
    books: [
      { id: 1, title: "The Great Gatsby", author: "F. Scott Fitzgerald" },
      { id: 2, title: "To Kill a Mockingbird", author: "Harper Lee" },
      { id: 3, title: "1984", author: "George Orwell" }
    ]
  })
);

V1Routes.get("/users", (c) => 
  c.json({ 
    users: [
      { id: 1, name: "John Doe", email: "john@example.com" },
      { id: 2, name: "Jane Smith", email: "jane@example.com" }
    ]
  })
);

export default V1Routes;
Enter fullscreen mode Exit fullscreen mode

Adding Client-Side Routing

For a proper SPA experience, you'll want to add React Router:

bun add react-router-dom
Enter fullscreen mode Exit fullscreen mode

App.tsx:

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/books" element={<Books />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}
Enter fullscreen mode Exit fullscreen mode

The beauty of this setup is that Bun's "/*": index route serves your React app for all non-API requests, giving you client-side routing that works perfectly with React Router.

One Important Detail

When using app.route() with this setup, make sure to include the trailing slash:

app.route("/api/", API); // ✅ Works
// app.route("/api", API); // ❌ Won't work
Enter fullscreen mode Exit fullscreen mode

However, individual route handlers work as expected without any special configuration:

V1Routes.get("/books", handler); // Works at /api/v1/books
Enter fullscreen mode Exit fullscreen mode

What You Get

With this setup, you get:

  • API endpoints at /api/v1/books, /api/v1/users, etc.
  • React app served from / with full client-side routing
  • Hot module replacement for React components
  • Single process serving both frontend and backend
  • Type safety across your entire stack
  • Full React ecosystem: Tailwind CSS, shadcn/ui, and thousands of React libraries
  • Hono's middleware ecosystem for authentication, CORS, logging, validation, and more
  • Best of both worlds: Modern frontend tooling with a powerful, flexible backend

Performance Benefits

This approach gives you several performance advantages:

  • Bun's speed: Both your API and frontend benefit from Bun's performance
  • No proxy overhead: Everything runs in the same process
  • Efficient bundling: Bun handles all the heavy lifting for your React app
  • Fast startup: Your entire stack boots up quickly

Conclusion

Building full-stack applications with Bun, React, and Hono is remarkably straightforward once you understand how all three interact with each other. The combination gives you modern tooling, excellent performance, and a clean development experience.

Whether you're building a personal project or a production application, this stack provides a solid foundation with room to grow. The integration feels natural, and you get the best of all three technologies without any awkward compromises.

Give it a try—I think you'll be pleasantly surprised by how well these tools work together!

Top comments (0)