18-LAB / Documentation

Overlay developer tooling

The starter overlay uses Vite for development/builds, TypeScript for checking, oxlint for linting, and oxfmt for formatting. These are separate jobs; a successful Vite build is not a type check.

Where the tools fit

Tool Role in an overlay project
Vite Development server, HMR, and a production build of HTML/CSS/TypeScript.
Oxc / oxlint JavaScript/TypeScript diagnostics using explicitly configured rules.
oxfmt Consistent source formatting and a check-only mode for CI.
Rolldown Bundler used by current Vite; no separate bundler configuration is needed here.
Vitest Optional Vite-powered tests for pointer traversal, frame reconstruction, or formatting helpers. The starter does not install it.

This is the VoidZero ecosystem's role in development tooling, not an overlay runtime. The installed counter only contains built web assets and package descriptors.

Install and pin

Use Node.js 22.12+ (or 20.19+ for this Vite version). Create a plain TypeScript app:

pnpm create vite starter-overlay --template vanilla-ts
cd starter-overlay
pnpm add -D vite@8.3.4 typescript@7.0.2 oxlint@1.87.0 oxfmt@0.72.0 @types/node@24

The tested Oxc CLIs are oxlint 1.87.0 and oxfmt 0.72.0. Their actual options can be inspected without installing a project:

npx --yes oxlint@1.87.0 --help
npx --yes oxfmt@0.72.0 --help

Commit your package-manager lockfile alongside the config. When starting from the example, run pnpm install in examples/starter-overlay/, not in an unrelated repository.

Configure the project

vite.config.ts sets base: "./" for counter-relative asset URLs and builds into dist/<package name>/. The unscoped package name is the installed folder name. Vite copies public/ metadata and schemas on every build, including watch builds. JavaScript targets ES2026; cssTarget: "es2025" works around Lightning CSS's unsupported es2026 target. TypeScript 7.0.2 uses ESNext for no-emit checks, while Vite handles ES2026 lowering. Its serve-only plugin supplies the same counter-path value as a daemon.

<<< ../../../examples/starter-overlay/vite.config.ts

Use .oxlintrc.json; correctness rules are errors, browser globals are enabled for runtime code, and Node globals are enabled only for build files:

<<< ../../../examples/starter-overlay/.oxlintrc.json

Use .oxfmtrc.json; this is deliberately ordinary formatting configuration:

<<< ../../../examples/starter-overlay/.oxfmtrc.json

The CLIs support --init to generate their default config. -c / --config selects an explicit config. oxlint's category switches are -D (deny), -W (warn), and -A (allow), evaluated in command-line order. Enable rules intentionally rather than turning on every plugin or every category for a small browser HUD.

Scripts

The tutorial's complete package.json is the source of these commands:

<<< ../../../examples/starter-overlay/package.json

Command Result
pnpm dev Vite development server; daemon defaults to 127.0.0.1:24050.
pnpm lint oxlint --deny-warnings; warnings also fail the command.
pnpm format oxfmt --write; edits files in place.
pnpm format:check oxfmt --check; fails if formatting differs.
pnpm typecheck tsc --noEmit; checks types without generating assets.
pnpm build vite build; output and copied public/ files go into dist/starter-overlay/.
pnpm preview Preview built files; this is not the osu! telemetry daemon.

oxfmt writes by default; use --check in automation. Both tools honor the project ignore files; the example's .gitignore excludes node_modules/ and dist/. oxfmt --threads=1 is available when limiting formatting parallelism matters.

Editors and IDEs

VS Code

Install the official Oxc VS Code extension, also available on Open VSX. Keep both CLI packages installed locally: the extension uses their language servers.

Recommend it in .vscode/extensions.json:

{
  "recommendations": ["oxc.oxc-vscode"]
}

Use .vscode/settings.json:

{
  "editor.defaultFormatter": "oxc.oxc-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.oxc": "always"
  }
}

See the official linter editor setup and formatter editor setup. Other LSP-capable editors can use oxlint --lsp and oxfmt --lsp.

JetBrains IDEs

Install the Oxc plugin through Settings → Plugins → Marketplace, search Oxc, and select Install. It supports both oxlint and oxfmt: inline lint diagnostics, quick fixes/fix-on-save, and formatting through Code → Reformat Code / Reformat File, plus format-on-save. See the official plugin README for capabilities and installation.

Zed

Open Extensions (zed: extensions), search Oxc, and install the official extension. Its documented minimums are Zed 0.205.0 and oxlint 1.35.0; the pinned oxlint here satisfies that minimum.

Configure .zed/settings.json to use oxfmt on save for JavaScript and TypeScript, following the extension's combined configuration example:

{
  "languages": {
    "JavaScript": {
      "format_on_save": "on",
      "prettier": { "allowed": false },
      "formatter": [{ "language_server": { "name": "oxfmt" } }]
    },
    "TypeScript": {
      "format_on_save": "on",
      "prettier": { "allowed": false },
      "formatter": [{ "language_server": { "name": "oxfmt" } }]
    }
  }
}

CI

For a standalone overlay repository, add this GitHub Actions workflow. Commit a pnpm-lock.yaml first. In the site repository, set defaults.run.working-directory to examples/starter-overlay; that folder has its own workspace and lockfile.

name: Overlay checks
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 11.28.5
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm format:check
      - run: pnpm typecheck
      - run: pnpm build

These static checks do not prove socket compatibility or in-game rendering. Exercise both servers and test the built folder in the actual consumers; see package and test.

Migrate an existing project

From Prettier or Biome formatting

oxfmt's verified migration flag generates .oxfmtrc.json from the existing formatter:

pnpm exec oxfmt --migrate=prettier

Or, when the source is Biome:

pnpm exec oxfmt --migrate=biome

Choose one, review the generated config, format the project, and replace the old formatting script/editor formatter. Biome's lint rules are not migrated by a formatter migration. Do not keep two format-on-save providers fighting over a file.

From ESLint

Oxlint does not blindly consume an ESLint flat config. Use the official migration utility, then inspect supported rules and plugins:

npx @oxlint/migrate
pnpm exec oxlint

The official migration guide explains flat-config conversion and the legacy ESLint v8 caveat: legacy configs need manual translation or conversion to a flat config first. If a required rule is not covered, retain only that part of ESLint temporarily; eslint-plugin-oxlint can disable overlapping ESLint rules. Remove the obsolete config and dependency once the project's required coverage has actually moved. This starter needs neither ESLint nor Biome.

Next: build the overlay or optimize the render path.

Edit this page on GitHub