Bun Native Frontend
Zero-config React/TS/CSS with HMR, served on the same port as your API. The only frontend mode that supports compiling the whole app (server + frontend assets) into a single executable.
Overview
With frontend: { type: 'bun' }, tekir uses Bun's built-in HTMLBundle pipeline. Each .html file in resources/ becomes a route; <script> and <link> tags are bundled by Bun automatically. No config file, no plugin install. JSX, TypeScript, CSS, and import with { type: 'file' } embeds all work out of the box.
Setup
const { router, service, start } = await tekir({
config: { app: { port: 3000 } },
frontend: { type: 'bun' } // HTML lives in resources/
})
router.get('/api/hello', () => ({ message: 'from server' }))
start()Place HTML pages in resources/. The filename becomes the route: resources/index.html → /, resources/about.html → /about, and so on.
<!-- resources/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>tekir + Bun</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>// resources/main.tsx
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(<App />)Multi-page apps
Drop additional HTML files alongside index.html. tekir registers each one as a static route automatically:
resources/
├── index.html → /
├── about.html → /about
├── dashboard.html → /dashboard
├── main.tsx
└── styles.cssHMR & Dev Server
In development (NODE_ENV !== 'production'), Bun watches resources/and pushes hot module updates over the same port your API listens on. There is no second dev process and no proxy hop — one server, one port, everything reloads on save.
Production Build
For production, bundle the frontend ahead of time. Bun emits minified, hashed assets and inlines them into the response:
tekir build
tekir serveSingle Executable
Bun's --compileembeds your HTML, bundled JS/CSS, and the Bun runtime into one binary. Drop it on a server and run it — no Bun, Node, or node_modulesneeded at runtime.
# Build a single executable that bundles
# server + Bun runtime + frontend assets
tekir build --compile
# Run anywhere — no Bun, Node, or node_modules needed
./serverCross-compile and advanced flags (--target, --define, --bytecode, --sourcemap etc.) live in the fluent API reference.
Frontend Environment Variables
Bun's HTMLBundle pipeline does not substitute process.env at bundle time, so secrets in .env stay on the server by default. To expose values to the browser, write a regular API route and fetch it:
# .env (server only — never reaches the browser)
DATABASE_URL=postgres://localhost/mydb
APP_KEY=...
PUBLIC_API_URL=https://api.example.com
PUBLIC_APP_NAME=My App// index.ts — expose values you want the browser to read.
router.get('/api/config', () => ({
apiUrl: process.env.PUBLIC_API_URL,
appName: process.env.PUBLIC_APP_NAME,
}))// resources/main.tsx
const config = await fetch('/api/config').then((r) => r.json())
console.log(config.apiUrl, config.appName)Limits
Bun native is the fastest and simplest path, but it's an SPA-style bundler — no server-side rendering, no file-based dynamic routes, no pre-rendering. If you need any of those, use Next.js instead. If you want the Vite plugin ecosystem (Vue, Svelte, Solid, custom transforms), switch to Vite.