Skip to content

Bun fullstack dev server

Bun’s fullstack dev server bundles frontend assets and supports hot module replacement (HMR). This guide uses Bun 1.3 or later and the Elysia v2 static plugin.

This allows us to directly use React without any bundler like Vite or Webpack.

You can use this example to quickly try it out.

Otherwise, install it manually:

Install the Elysia v2 static plugin:

Terminal window
bun add elysia@rc @elysia/static@next react react-dom
bun add -d @types/react @types/react-dom
import { Elysia } from 'elysia'
import { staticPlugin } from '@elysia/static'
new Elysia()
.use(await staticPlugin({ bunFullstack: true }))
.listen(3000)

Create public/index.html and load public/index.tsx as a module.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Elysia React App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>

Create public/index.tsx with a counter component.

import { useState } from 'react'
import { createRoot } from 'react-dom/client'
function App() {
const [count, setCount] = useState(0)
const increase = () => setCount((c) => c + 1)
return (
<main>
<h2>{count}</h2>
<button onClick={increase}>
Increase
</button>
</main>
)
}
const root = createRoot(document.getElementById('root')!)
root.render(<App />)

Enable JSX in tsconfig.json.

Navigate to http://localhost:3000/public and see the result.

Bun handles frontend bundling alongside the API, so you don’t need a separate bundler process.

The source example combines HMR, Tailwind, TanStack Query, Eden Treaty, and path aliases. Use versions compatible with your Bun and Elysia release.

{
"compilerOptions": {
"jsx": "react-jsx"
}
}

We can change the default /public prefix by passing the prefix option to staticPlugin.

This would serve the static files at / instead of /public.

See static plugin for more configuration options.

import { Elysia } from 'elysia'
import { staticPlugin } from '@elysia/static'
new Elysia()
.use(
await staticPlugin({
prefix: '/',
bunFullstack: true
})
)
.listen(3000)

We can also use Tailwind CSS with Bun Fullstack Dev Server.

Install dependencies:

Terminal window
bun add tailwindcss@4
bun add -d bun-plugin-tailwind

Create bunfig.toml with the Tailwind plugin.

[serve.static]
plugins = ["bun-plugin-tailwind"]

Create public/global.css with Tailwind directives.

@import "tailwindcss";

Add the stylesheet link to public/index.html.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Elysia React App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./global.css">
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>

Alternatively, import public/global.css in public/index.tsx instead of adding the stylesheet link to HTML.

import { useState } from 'react'
import { createRoot } from 'react-dom/client'
import './global.css'
function App() {
const [count, setCount] = useState(0)
const increase = () => setCount((c) => c + 1)
return (
<main>
<h2>{count}</h2>
<button onClick={increase}>
Increase
</button>
</main>
)
}
const root = createRoot(document.getElementById('root')!)
root.render(<App />)

We can also use path aliases in Bun Fullstack Dev Server.

Add paths in tsconfig.json.

{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@public/*": ["public/*"]
}
}
}

Use the alias in your code.

This will work out of the box without any additional configuration.

import { useState } from 'react'
import { createRoot } from 'react-dom/client'
import '@public/global.css'
function App() {
const [count, setCount] = useState(0)
const increase = () => setCount((c) => c + 1)
return (
<main>
<h2>{count}</h2>
<button onClick={increase}>
Increase
</button>
</main>
)
}
const root = createRoot(document.getElementById('root')!)
root.render(<App />)

You can build a fullstack server as if it were a normal Elysia server.

Terminal window
bun build --compile --target bun --outfile server src/index.ts

This creates an executable named server. Test the production build as well as development HMR.

When running the server executable, make sure to include the public folder as in the development environment.

See Deploy to Production for more information.

Type to search the documentation.

    Up arrowDown arrow to navigateEnter to selectEscape to close