Vite Frontend
Use Vite's plugin ecosystem (React, Vue, Svelte, Solid, Qwik...) with HMR in dev and a hashed, code-split bundle in production. Same port for API and frontend. Compiles to a single executable.
Overview
@tekir/vitewires Vite's dev server and build pipeline into your tekir app. In development a Vite dev server runs in-process and tekir proxies non-API requests to it — you keep a single port for everything. In production tekir serves the pre-bundled output directly from disk (or from memory inside a compiled binary).
Install
bun add @tekir/vite vite
bun add -d @vitejs/plugin-react @types/react @types/react-dom
bun add react react-domvite is a peer dependency, so you control the version. Pick the framework plugin you need (@vitejs/plugin-react, @vitejs/plugin-vue, @sveltejs/vite-plugin-svelte etc.) and add it to vite.config.ts.
Setup
import { tekir } from '@tekir/core'
const { router, start } = await tekir({
config: { app: { port: 3000 } },
frontend: { type: 'vite' } // looks for vite.config.ts in cwd
})
router.get('/api/hello', () => ({ message: 'from server' }))
start()import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
root: 'resources', // where your index.html lives
plugins: [react()],
build: { outDir: '../dist/client' },
})Typical project layout:
my-app/
├── index.ts
├── vite.config.ts
├── resources/
│ ├── index.html
│ ├── main.tsx
│ └── App.tsx
├── public/ # optional, copied into the build as-is
└── package.jsonvite.config.ts
tekir auto-discovers vite.config.ts (or .js/.mts/.mjs) in your project root and passes it to both the dev server and the build hook. This way every plugin you add — React, Vue, Tailwind, etc. — works exactly the way Vite documents it. Without this, plugins silently drop and your production bundle ends up using the classic JSX runtime (e.g. you see ReferenceError: React is not defined in the browser).
React, Vue, Svelte, Solid
Any framework Vite supports works. Swap the plugin and adjust your entry file:
// React
import react from '@vitejs/plugin-react'
plugins: [react()]
// Vue
import vue from '@vitejs/plugin-vue'
plugins: [vue()]
// Svelte
import { svelte } from '@sveltejs/vite-plugin-svelte'
plugins: [svelte()]
// Solid
import solid from 'vite-plugin-solid'
plugins: [solid()]Environment Variables
Vite inlines only variables prefixed with VITE_ into the client bundle (we wire envPrefix: 'VITE_' for you). Anything else stays server-side and never reaches the browser:
# .env (project root)
VITE_API_URL=https://api.example.com # exposed to the browser
DATABASE_URL=postgres://... # stays on the server// resources/main.tsx
console.log(import.meta.env.VITE_API_URL)
// → "https://api.example.com" (inlined at build time)
console.log(import.meta.env.DATABASE_URL)
// → undefined (stripped by Vite's envPrefix filter)Production Build
# Build the frontend (runs vite build) + start production server
tekir build
tekir servebuild runs the Vite production build (your vite.config.ts plugins, your publicDir, your build.outDir) and writes hashed assets into dist/client/. start serves them with the right MIME types and falls back to index.html for client-side routes.
Single Executable
Vite frontend is fully supported by --compile. tekir runs the Vite build, walks dist/client/, and embeds every asset into the binary as a Bun blob. The compiled binary serves them straight from memory — no node_modules on disk, no Vite, no rollup at runtime.
# One self-contained binary: server + bundled frontend
tekir build --compile
./serverCross-compile, build-time defines, splitting, plugins and the rest live in the fluent API reference.
Options
Most settings belong in vite.config.ts. The few options on the tekir side override the dev/build wiring:
frontend: {
type: 'vite',
root: 'resources', // override vite.config.ts root
buildDir: 'dist/client', // where Vite writes the production bundle
plugins: [/* additional inline plugins */],
define: { __APP_VERSION__: '"1.0.0"' },
resolve: { alias: { '@': '/src' } },
}