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:
Set up the server
Section titled “Set up the server”Install the Elysia v2 static plugin:
bun add elysia@rc @elysia/static@next react react-dombun add -d @types/react @types/react-domimport { Elysia } from 'elysia'import { staticPlugin } from '@elysia/static'
new Elysia() .use(await staticPlugin({ bunFullstack: true })) .listen(3000)Create the HTML entry point
Section titled “Create the HTML entry point”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 the React app
Section titled “Create the React app”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
Section titled “Enable JSX”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" }}Custom prefix path
Section titled “Custom prefix path”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)Tailwind CSS
Section titled “Tailwind CSS”We can also use Tailwind CSS with Bun Fullstack Dev Server.
Configure the Tailwind plugin
Section titled “Configure the Tailwind plugin”Install dependencies:
bun add tailwindcss@4bun add -d bun-plugin-tailwindCreate bunfig.toml with the Tailwind plugin.
[serve.static]plugins = ["bun-plugin-tailwind"]Add Tailwind directives
Section titled “Add Tailwind directives”Create public/global.css with Tailwind directives.
@import "tailwindcss";Add the stylesheet to HTML
Section titled “Add the stylesheet to HTML”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>Import the stylesheet in React
Section titled “Import the stylesheet in React”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 />)Path alias
Section titled “Path alias”We can also use path aliases in Bun Fullstack Dev Server.
Configure path aliases
Section titled “Configure path aliases”Add paths in tsconfig.json.
{ "compilerOptions": { "baseUrl": ".", "paths": { "@public/*": ["public/*"] } }}Use the alias
Section titled “Use the alias”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 />)Build for production
Section titled “Build for production”You can build a fullstack server as if it were a normal Elysia server.
bun build --compile --target bun --outfile server src/index.tsThis 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.