Quickstart
Install the plugin, write a server, and run a production build with Vite or Rsbuild.
Install
npm install -D oxidejspnpm add -D oxidejsyarn add -D oxidejsbun add -D oxidejsExtend the TypeScript config:
{ "extends": "oxidejs/tsconfig" }
Add the plugin
Use Vite or Rsbuild. Same plugin, same output.
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
{@link
UserConfig
}
object, or a function that returns it.
The function receives a
{@link
ConfigEnv
}
object.defineConfig } from "vite";
import const oxide: (options?: OxidejsOptions | undefined) => import("vite").Plugin<any> | import("vite").Plugin<any>[]oxide from "oxidejs/vite";
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
{@link
UserConfig
}
object, or a function that returns it.
The function receives a
{@link
ConfigEnv
}
object.defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.plugins: [function oxide(options?: OxidejsOptions | undefined): import("vite").Plugin<any> | import("vite").Plugin<any>[]oxide()],
});Add scripts to package.json:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}import { import defineConfigdefineConfig } from "@rsbuild/core";
import const oxide: (options?: OxidejsOptions | undefined) => anyoxide from "oxidejs/rsbuild";
export default import defineConfigdefineConfig({
plugins: any[]plugins: [function oxide(options?: OxidejsOptions | undefined): anyoxide()],
});Add scripts to package.json:
{
"scripts": {
"dev": "rsbuild dev",
"build": "rsbuild build",
"preview": "rsbuild preview"
}
}Write a server entry
export default {
function fetch(request: Request): Response | undefinedfetch(request: Requestrequest: Request) {
if (new var URL: new (url: string | URL, base?: string | URL) => URLThe **`URL`** interface is used to parse, construct, normalize, and encode URLs. It works by providing properties which allow you to easily read and modify the components of a URL.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/URL)URL(request: Requestrequest.Request.url: stringThe **`url`** read-only property of the Request interface contains the URL of the request.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url)url).URL.pathname: stringThe **`pathname`** property of the URL interface represents a location in a hierarchical structure. It is a string constructed from a list of path segments, each of which is prefixed by a / character.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname)pathname === "/api/ok") {
return new var Response: new (body?: BodyInit | null, init?: ResponseInit) => ResponseThe **`Response`** interface of the Fetch API represents the response to a request.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Response)Response("ok");
}
},
};
Return undefined to fall through to static files. Missing files fall back to index.html only for navigations.
Build and run
vite build
node dist/server.js
No index.html → only dist/server.js. With index.html → client assets in dist/client/. Files in public/ land next to those assets.
Common workflows
celld
celld is a self-hosted alternative to Cloudflare Workers. It is not Cloudflare.
import const oxide: (options?: OxidejsOptions | undefined) => import("vite").Plugin<any> | import("vite").Plugin<any>[]oxide from "oxidejs/vite";
function oxide(options?: OxidejsOptions | undefined): import("vite").Plugin<any> | import("vite").Plugin<any>[]oxide({
OxidejsOptions.preset?: OxidejsPreset | undefined"fetch" (default) skips wrangler.jsonc and serves client assets. "celld" emits wrangler.jsonc.preset: "celld",
OxidejsOptions.wrangler?: OxidejsWranglerOptions | undefinedWrangler config fields to merge into the generated wrangler.jsonc.wrangler: { OxidejsWranglerOptions.name: stringname: "my-app", OxidejsWranglerOptions.compatibility_date: stringcompatibility_date: "2026-01-01" },
});
preset: "celld" writes dist/wrangler.jsonc and skips asset serving (ASSETS does that). See wrangler.
Server only
Leave out index.html. The plugin emits dist/server.js and does not build a client.
Next steps
- Server entry -
src/server.tsand fallback - Server actions -
*.server.tsand tacho - Configuration -
preset, paths,wranglerkeys