---
title: "How to add Hem to a Vite project"
description: "Install the plugin, point it at your tsconfig, and get .hem files compiling to siblings your build already understands."
image: "https://hem.run/social/hem.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://hem.run/llms.txt
> Use this file to discover all available pages before exploring further.

# How to add Hem to a Vite project

**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

```sh
npm install -D @hem-lang/vite-plugin vite
```

Vite 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

```ts title="vite.config.ts"
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/react` 19 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:

```ts title="vite.config.ts"
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:

```sh
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](/guides/integrate-generated-files).

## Write your first module

```hem title="src/greet.hem"
export let greet = (name: string) => `Hello, ${name}`
```

A `.ts` file imports it as it would import anything else:

```ts
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:

```json
{ "hem": { "effectImportSource": "effect" } }
```

Passing `hem({ effectImportSource })` overrides it for the Vite build only. See [how to enable the Effect extension](/guides/enable-effect) for what activating it changes.

## Next

- Running checks in CI, or without Vite at all: [how to check and build from the command line](/guides/use-the-cli)
- Tests: [how to test Hem modules](/guides/test-hem-code)
- Editor support: [how to set up your editor](/guides/editor-setup)

Source: https://hem.run/guides/set-up-a-project/index.mdx
