Files ending in .jsx and .tsx execute through the same load hook. JSX is recognized in .jsx / .tsx only, never in plain .js. The defaults are the modern ones — automatic runtime, react as the import source — matching oxc-transformer, Vite's React plugins, Rolldown, and Bun, so a React project needs no setup.

nub render.tsx

nub.jsonc

Configure the runtime directly in the project file. The field names match TypeScript's compiler options, and the jsx field accepts the three modes that emit executable JavaScript: react, react-jsx, and react-jsxdev.

nub.jsonc
{
  "jsx": "react-jsx",
  "jsxImportSource": "preact"
}

The classic runtime also accepts jsxFactory and jsxFragmentFactory.

nub.jsonc
{
  "jsx": "react",
  "jsxFactory": "h",
  "jsxFragmentFactory": "Fragment"
}

tsconfig.json

Nub still reads the same settings from compilerOptions when they are absent from nub.jsonc, so the runtime can share configuration with the rest of the toolchain. Top-level Nub fields win when both files set the same option.

OptionEffect
jsxThe runtime mode: preserve, react, react-jsx, react-jsxdev, or react-native
jsxImportSourceThe package the automatic runtime imports from, such as preact
jsxFactoryThe factory function for the classic runtime
jsxFragmentFactoryThe fragment factory for the classic runtime
tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

Read the full docs for jsx on typescriptlang.org.

Preact, Hono, and Vue JSX work via passthrough. Solid is the exception — its JSX needs babel-preset-solid's reactive-graph compilation, which a per-file transpiler can't do, so run Solid through its bundler (e.g. nub run vite).

@jsxImportSource

A per-file /** @jsxImportSource ... */ pragma overrides the tsconfig import source for that one file. This is the standard mechanism for mixing JSX runtimes in a single project — a Hono route alongside React components.

/** @jsxImportSource hono/jsx */
export default function Page() {
  return <h1>Hello</h1>;
}

The pragma is read from the file source itself; no tsconfig entry is needed for it to take effect.