# Installation

> Install @oddsquad/vite-plugin-lit, add it to vite.config.ts, register the ambient types, and confirm the dev server is patching.

Install the plugin, add one line to `vite.config.ts`, and register the ambient types. If you have never used the plugin, follow the tutorial instead; it includes these steps.

Working with a coding agent? Hand it this prompt and it does the steps below, then reports which files it changed.

```
Set up @oddsquad/vite-plugin-lit in this project, following the agent skill at https://vite-plugin-lit.oddsquad.dev/.well-known/agent-skills/vite-plugin-lit/SKILL.md.

1. Install it as a dev dependency with the package manager this project already uses.
2. Add litPlugin() to the plugins array in the Vite config. Leave the rest of the config as it is.
3. Register the "@oddsquad/vite-plugin-lit/client" types in tsconfig.json, next to "vite/client" rather than instead of it.
4. Start the dev server and check that the plugin's indicator appears on a page with a Lit component.

Don't enable the DevTools panel or the source overlay unless I ask. When you're done, list the files you changed.
```

## Install

* npm

  ```sh
  npm i -D @oddsquad/vite-plugin-lit
  ```

* pnpm

  ```sh
  pnpm add -D @oddsquad/vite-plugin-lit
  ```

* yarn

  ```sh
  yarn add -D @oddsquad/vite-plugin-lit
  ```

* bun

  ```sh
  bun add -d @oddsquad/vite-plugin-lit
  ```

Lit and Vite stay your dependencies. [Compatibility](/reference/compatibility/) lists the versions each release supports and the one dev mode it does not.

## Add it to Vite

Add `litPlugin()` to the `plugins` array. Nothing else moves.

```ts
import {defineConfig} from 'vite';
import {litPlugin} from '@oddsquad/vite-plugin-lit';

export default defineConfig({
  plugins: [litPlugin()],
});
```

Hot patching, the on-page indicator and the CSS import queries are on from here. The DevTools timeline and the source overlay are opt-in; the [options reference](/reference/options/) has the switches.

The patching applies to the dev server only, so a production build is untouched. The `?css-sheet` and `?hmr-url` queries resolve in dev and build alike.

### Keep dev code out of the build

`litPlugin()` doesn’t load its dev-only code until the dev server (or `vite preview`) starts. HMR, the source overlay and the DevTools panel are imported in a hook that `vite build` never calls, so a build loads only the options and the CSS plugins.

What those CSS plugins emit carries no dev code either: under `vite build`, a `?css-sheet` compiles to a sheet that is filled once, with no hot-swap callback, and a `?hmr-url` to the plain asset URL.

If you want the build to depend on less than that, use `litCss()` from the `/build` entry for builds. It carries only what a build uses: the CSS queries, Lightning CSS for `css` literals, and the no-op timeline stub.

```ts
import {defineConfig} from 'vite';
import {litPlugin} from '@oddsquad/vite-plugin-lit';
import {litCss} from '@oddsquad/vite-plugin-lit/build';

export default defineConfig(({command}) => ({
  plugins: [command === 'serve' ? litPlugin() : litCss()],
}));
```

`litCss()` takes `cssSheetBuild` and reads `LIT_PLUGIN_CSS_SHEET_BUILD` the same way `litPlugin()` does. A unit test walks both entries’ import graphs so neither can start pulling in dev code unnoticed.

## Add the types

The import queries ship ambient declarations in `@oddsquad/vite-plugin-lit/client`. Without them TypeScript cannot type `import sheet from './theme.css?css-sheet'`.

The `types` array replaces TypeScript’s default, so list `vite/client` next to it rather than instead of it. The rest of this file is the `lit-ts` template’s, unchanged.

```json
{
  "compilerOptions": {
    "target": "es2023",
    "experimentalDecorators": true,
    "useDefineForClassFields": false,
    "module": "esnext",
    "lib": ["ES2023", "DOM"],
    "types": ["vite/client", "@oddsquad/vite-plugin-lit/client"],
    "allowArbitraryExtensions": true,
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,

    /* Linting */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
```

One `.d.ts` anywhere in your `include` does the same job, if you would rather not touch `types`:

```ts
/// <reference types="@oddsquad/vite-plugin-lit/client" />
```

## Check it works

Start the dev server and open a page that renders a Lit component.

```sh
npm run dev
```

A small indicator sits in the corner of the page. It is idle until an update arrives.

![The HMR indicator at rest in the corner of the page: a flame icon, a dot and a zero.](/_astro/indicator-idle.light.CMIydUfp_Zh58Nt.webp)![The HMR indicator at rest in the corner of the page: a flame icon, a dot and a zero.](/_astro/indicator-idle.dark.dDKkKFdc_Z2t39gs.webp)

Type into an input inside a component, then edit that component’s template and save. The markup changes, what you typed stays, and the indicator pulses green. That is a hot patch: an edit applied to the running page without a reload.

If the page reloads instead, the edit hit a shape that cannot be patched in place. [Limitations](/reference/limitations/) names each one.

## Related

[Options](/reference/options/)Every option litPlugin() takes, with defaults.

[Keep state across edits](/guides/hmr/)What a hot patch preserves, and the three settings worth changing.
