Hem’s packages are not published to npm yet. The commands below name them as they will ship. Today they resolve inside the Hem repository’s own workspace.
Hem compiles each .hem file to a .ts or .tsx sibling on disk. Vite, your framework, your test runner, and your editor all keep seeing ordinary TypeScript, so adding Hem to an existing project changes what you write, not what your toolchain consumes.
Install the plugin
npm install -D @hem-lang/vite-plugin viteVite is a peer dependency and Hem accepts versions 6, 7, and 8. A React project also needs react, react-dom, and @types/react.
Register the plugin
import { defineConfig } from "vite";
import { hem } from "@hem-lang/vite-plugin";
export default defineConfig({
plugins: [hem()],
});On build start the plugin compiles the whole tsconfig project and writes each sibling. In dev it watches every .hem file and re-emits the siblings whose bytes actually changed.
Place hem() alongside your framework plugin rather than instead of it. The plugin does not own the JSX transform.
Confirm the tsconfig covers your Hem files
Hem reads your real tsconfig.json for module resolution, libraries, paths, JSX, and project membership. A .hem file has to be covered by that config’s include or files, otherwise the compiler refuses it rather than checking it in isolation.
You do not need to change your strictness settings. Hem’s own checker overlays strict, strictNullChecks, and noUncheckedIndexedAccess internally, so your existing TypeScript build keeps its current options while Hem code still gets its guarantees.
Configure JSX if you write components
JSX stays a TypeScript and framework concern:
"jsx": "react-jsx"works against@types/react19 as written."jsx": "preserve"needs"jsxImportSource": "react"beside it. Bare"preserve"fails with TypeScript’s own TS7026, which is not a Hem error and will not mention Hem.
For the runtime transform, use your framework’s React plugin. On Vite 8 you can instead set the oxc runtime directly:
import { defineConfig } from "vite";
import { hem } from "@hem-lang/vite-plugin";
export default defineConfig({
plugins: [hem()],
oxc: { jsx: { runtime: "automatic", importSource: "react" } },
});Decide what to do with the generated siblings
Every emitted file carries a // Generated from … by hem — do not edit. hem:sha256=<16 hex> banner, and each successful build refreshes .hem/generated-files.json.
Commit the manifest. To keep generated siblings out of reviews, add their exact
project-relative paths to .gitignore. Obtain the list after a successful build:
hem generated exclusions .For example, if the list contains src/greet.ts, ignore /src/greet.ts rather
than *.ts. Update the entries when Hem modules are added or removed. Keep
handwritten TypeScript, declarations, and configuration files visible to Git.
A .hem file sitting beside a handwritten file of the same name is a build
error (HEM3002), never a silent overwrite. Editing a generated sibling also causes
an error (HEM3008) on build, watch, Vite refresh, or hem check. Move the change
into the .hem source, then delete the sibling (or restore it with git) and rebuild.
To configure formatters, linters, and other tools to ignore generated siblings, see how to keep other tools off generated files.
Write your first module
export let greet = (name: string) => `Hello, ${name}`A .ts file imports it as it would import anything else:
import { greet } from "./greet.hem";The Vite plugin resolves the .hem specifier to the emitted sibling. Without
the plugin, compile Hem first and import the emitted module using your
toolchain’s normal resolution rules. Neither path requires bindings or wrappers.
If you use Effect
Set the transport once in your tsconfig so the CLI, the language server, and the plugin all read the same value:
{ "hem": { "effectImportSource": "effect" } }Passing hem({ effectImportSource }) overrides it for the Vite build only. See how to enable the Effect extension for what activating it changes.
Next
- Running checks in CI, or without Vite at all: how to check and build from the command line
- Tests: how to test Hem modules
- Editor support: how to set up your editor