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)