Options
litPlugin() takes a single options object, and this page lists every key in
it. Each entry gives the meaning, a config you can paste, the default, the
environment variable, and where to read more.
| Option | Type | Default | Env var |
|---|---|---|---|
hmr |
boolean | HmrOptions |
true |
LIT_PLUGIN_HMR |
hmr.enabled |
boolean |
true |
LIT_PLUGIN_HMR |
hmr.reconnect |
boolean |
false |
LIT_PLUGIN_HMR_RECONNECT |
hmr.privateFields |
boolean |
true |
LIT_PLUGIN_HMR_PRIVATE_FIELDS |
hmr.childState |
'reset' | 'transfer' | 'reuse' |
'transfer' |
LIT_PLUGIN_HMR_CHILD_STATE |
hmr.onIncompatible |
'reload' | 'warn' |
'reload' |
LIT_PLUGIN_HMR_ON_INCOMPATIBLE |
hmr.indicator |
boolean | HmrIndicatorOptions |
true |
LIT_PLUGIN_HMR_INDICATOR |
hmr.indicator.count |
boolean |
false |
LIT_PLUGIN_HMR_INDICATOR_COUNT |
sourceOverlay |
boolean | SourceOverlayOptions |
false |
LIT_PLUGIN_SOURCE_OVERLAY |
sourceOverlay.key |
string |
's' |
LIT_PLUGIN_SOURCE_OVERLAY_KEY |
sourceOverlay.editor |
string | EditorConfig |
'vscode' |
LIT_PLUGIN_SOURCE_OVERLAY_EDITOR |
sourceOverlay.workspaceRoot |
string |
— | — |
sourceOverlay.throttleMs |
number |
50 |
LIT_PLUGIN_SOURCE_OVERLAY_THROTTLE_MS |
sourceOverlay.hosts |
'source' | 'lit' |
'source' |
— |
sourceOverlay.exclude |
(el: Element) => boolean |
— | — |
sourceOverlay.onSelect |
(info: ElementInfo) => void |
— | — |
timeline |
boolean |
false |
LIT_PLUGIN_TIMELINE |
cssSheetBuild |
'auto' | 'url' | 'inline' | 'inline-raw' |
'auto' |
LIT_PLUGIN_CSS_SHEET_BUILD |
devtoolsWorkspace |
boolean | string |
true |
LIT_PLUGIN_DEVTOOLS_WORKSPACE |
Turns HMR for Lit component classes, and its on-page indicator, on or off.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: false})],});false skips every HMR transform and all runtime injection. The CSS import
queries and the Lightning CSS literal processing still apply.
Type: boolean | HmrOptions · Default: true · Env:
LIT_PLUGIN_HMR (maps to hmr.enabled)
See also: HMR that keeps state.
hmr.enabled
Section titled “hmr.enabled”The same switch as hmr: false, spelled out so you can keep configuring the
rest of the group.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {enabled: false, indicator: true}})],});Type: boolean · Default: true · Env: LIT_PLUGIN_HMR
See also: HMR that keeps state.
hmr.reconnect
Section titled “hmr.reconnect”Cycles disconnectedCallback() and connectedCallback() on live instances
after a hot patch, an edit applied to the running page without a reload.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {reconnect: true}})],});Cycling tears down and re-creates controllers, listeners, and animations, which
a plain patch leaves alone. Turn it on when a component does setup work in
connectedCallback() that you want re-run on every edit.
Type: boolean · Default: false · Env:
LIT_PLUGIN_HMR_RECONNECT
See also: HMR that keeps state.
hmr.privateFields
Section titled “hmr.privateFields”Rewrites native #private class members to keys the next evaluation of the
module shares, so components that use them are patched in place and keep their
private state. Only the dev server does this; builds keep real #private.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {privateFields: false}})],});In dev, a private member becomes a symbol-keyed property. It no longer throws
when read from the wrong object, and Object.getOwnPropertySymbols() lists it.
Turn the rewrite off if a test or a library relies on the brand check in dev.
Type: boolean · Default: true · Env:
LIT_PLUGIN_HMR_PRIVATE_FIELDS
See also: Limitations.
hmr.childState
Section titled “hmr.childState”What happens to a child element when a parent edit changes the template literal it sits in. Lit clones fresh DOM for an edited template, so the child is a new element.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {childState: 'reuse'}})],});'transfer'copies the old element’s reactive properties and#privatefields onto the new one before its first render. Properties and attributes the template sets are left alone, so an edited binding takes effect.'reuse'puts the old element back in place of the new one, keeping its identity, shadow DOM and controllers. It only does so when no binding targets the new element or its light DOM; otherwise it transfers.'reset'leaves the new element at its defaults.
Old and new elements are matched by tag and order within the same shadow root. An edit that removes one child and adds another of the same tag in the same root can hand the removed child’s state to the new one.
Type: 'reset' | 'transfer' | 'reuse' · Default: 'transfer' ·
Env: LIT_PLUGIN_HMR_CHILD_STATE
See also: HMR that keeps state.
hmr.onIncompatible
Section titled “hmr.onIncompatible”Chooses what happens when a component cannot be patched in place: reload the page, or only warn in the console.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {onIncompatible: 'warn'}})],});'reload' loses page state but never shows a half-patched component. 'warn'
logs one line and leaves the page alone, so you can read the message.
Type: 'reload' | 'warn' · Default: 'reload' · Env:
LIT_PLUGIN_HMR_ON_INCOMPATIBLE
See also: Limitations.
hmr.indicator
Section titled “hmr.indicator”Injects the on-page update indicator, a small dot that animates on each HMR update.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {indicator: false}})],});The indicator is forced off when HMR itself is disabled, whatever this is set to. It pulses in one of two colours, and only some updates advance its count:
| Playground edit | Delivery | Re-renders? | Counts? | Pulse |
|---|---|---|---|---|
hmr-vsheet.css |
?css-sheet shared sheet |
no | no | info (cyan) |
hmr-shared.css |
?raw → shared sheet |
no | no | info (cyan) |
hmr-utility-sheet.css |
?url → shared sheet |
no | no | info (cyan) |
hmr-linked-css.css |
?hmr-url <link> |
yes | yes | success (green) |
hmr-import-css.css |
?hmr-url @import |
yes | yes | success (green) |
hmr-css-url.css |
?url + devCacheBust() |
yes | yes | success (green) |
hmr-raw-css.css |
?raw static styles |
yes | yes | success (green) |
any *.ts component |
component module | yes | yes | success (green) |
main.ts |
not self-accepting | full reload | resets | — |
A shared-stylesheet hot-swap restyles adopted sheets in place without re-rendering, so it pulses in the calmer info colour. A full reload remounts everything and resets the count.
Type: boolean | HmrIndicatorOptions · Default: true · Env:
LIT_PLUGIN_HMR_INDICATOR (maps to hmr.indicator.enabled)
See also: Build your first component.
hmr.indicator.count
Section titled “hmr.indicator.count”Shows a cumulative update count next to the dot.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({hmr: {indicator: {count: true}}})],});The count raises the idle opacity from fully transparent to 0.5, so the number stays readable between updates.
Type: boolean · Default: false · Env:
LIT_PLUGIN_HMR_INDICATOR_COUNT
See also: Read the update explainer.
sourceOverlay
Section titled “sourceOverlay”Enables the dev-only inspector that opens a clicked element’s source file in your editor.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: true})],});Toggle it in the page with Ctrl+Shift+S, or ⌘⇧S on a Mac. true takes the defaults below; an
object configures them.
Type: boolean | SourceOverlayOptions · Default: false · Env:
LIT_PLUGIN_SOURCE_OVERLAY
See also: Open a component in your editor.
sourceOverlay.key
Section titled “sourceOverlay.key”Sets the letter that Ctrl+Shift is combined with to toggle the overlay.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: {key: 'i'}})],});Type: string · Default: 's' · Env:
LIT_PLUGIN_SOURCE_OVERLAY_KEY
See also: Open a component in your editor.
sourceOverlay.editor
Section titled “sourceOverlay.editor”Picks which editor a click opens.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: {editor: 'zed'}})],});Built-in keys are vscode, cursor, zed, idea, and windsurf. An unknown
name falls back to VS Code. A {name, url} object supplies your own URL
builder, called with the file path, the line number and, for a call site, the
column.
The DevTools panel’s source links also open in this editor (vscode, cursor,
zed and idea). Other values leave the choice to launch-editor’s
auto-detection. See Open a component in your editor.
Type: string | EditorConfig · Default: 'vscode' · Env:
LIT_PLUGIN_SOURCE_OVERLAY_EDITOR
See also: Open a component in your editor.
sourceOverlay.workspaceRoot
Section titled “sourceOverlay.workspaceRoot”Prefixes the absolute file paths the overlay hands to your editor.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: {workspaceRoot: '/Users/me/app'}})],});Type: string · Default: none · Env: none
See also: Open a component in your editor.
sourceOverlay.throttleMs
Section titled “sourceOverlay.throttleMs”Throttles how often the overlay recomputes the element under the pointer.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: {throttleMs: 16}})],});Type: number · Default: 50 · Env:
LIT_PLUGIN_SOURCE_OVERLAY_THROTTLE_MS
See also: Open a component in your editor.
sourceOverlay.hosts
Section titled “sourceOverlay.hosts”Which elements the picker can pick. 'source' is your own components, the
ones with a file to open. 'lit' adds every other Lit element on the page,
such as a component library’s <wa-button> or <sl-input>: those pick into
the DevTools Components tab, with no source to open. Your own
components keep theirs.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({sourceOverlay: {hosts: 'lit'}})],});Type: 'source' | 'lit' · Default: 'source' · Env: none
See also: Open a component in your editor.
sourceOverlay.exclude
Section titled “sourceOverlay.exclude”Skips elements during inspection. Return true to ignore an element.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [ litPlugin({ sourceOverlay: {exclude: (el) => el.tagName.startsWith('SL-')}, }), ],});Type: (el: Element) => boolean · Default: none · Env: none, this
is a function
See also: Open a component in your editor.
sourceOverlay.onSelect
Section titled “sourceOverlay.onSelect”Runs when you click a component, instead of or alongside opening the editor.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [ litPlugin({ sourceOverlay: {onSelect: (info) => console.log(info.source.filePath)}, }), ],});The callback receives {tagName, componentName?, source: {filePath, lineNumber}}.
Type: (info: ElementInfo) => void · Default: none · Env: none,
this is a function
See also: Open a component in your editor.
timeline
Section titled “timeline”Adds the Lit panel to Vite DevTools, the browser overlay from
@vitejs/devtools that hosts plugin panels.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({timeline: true})],});The panel records Lit lifecycle, render, mouse, keyboard, and custom events and Lit
warnings, and derives
per-component update counts, durations, and reasons from them. It needs
@vitejs/devtools active in the same Vite config. true enables every built-in
layer, one category of recorded events with its own colour and toggle.
Type: boolean · Default: false · Env: LIT_PLUGIN_TIMELINE
See also: Open the DevTools panel.
cssSheetBuild
Section titled “cssSheetBuild”Decides what a ?css-sheet import compiles to under vite build.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({cssSheetBuild: 'inline'})],});Dev always uses the fetch-backed HMR form, whatever this is set to.
| Value | Build output |
|---|---|
'auto' |
'inline' when build.lib is set, 'url' otherwise |
'url' |
fetch-backed sheet over the emitted .css asset |
'inline' |
CSS text in the JS chunk, processed by the CSS pipeline |
'inline-raw' |
CSS text in the JS chunk verbatim, skipping the CSS pipeline |
A value outside that set logs a warning and falls back to the default, so a typo cannot change your build output silently.
Type: 'auto' | 'url' | 'inline' | 'inline-raw' · Default: 'auto' ·
Env: LIT_PLUGIN_CSS_SHEET_BUILD
See also: Share one stylesheet with ?css-sheet.
devtoolsWorkspace
Section titled “devtoolsWorkspace”Lets Chrome DevTools connect your project as a workspace, so edits you make in
the Sources panel save to disk. In dev, the server answers
/.well-known/appspecific/com.chrome.devtools.json with the folder and a
stable ID, and DevTools offers to connect it.
import {defineConfig} from 'vite';import {litPlugin} from '@oddsquad/vite-plugin-lit';
export default defineConfig({ plugins: [litPlugin({devtoolsWorkspace: '../..'})],});true uses Vite’s workspace root, which in a monorepo is the repository root
rather than the app, so sources served from sibling packages map too. A string
names the folder, relative to Vite’s root. false leaves the request
unanswered.
The reply contains an absolute path on your machine, so the server only gives
it to clients on the same machine. A dev server exposed with --host passes
requests from the network on to Vite.
Type: boolean | string · Default: true · Env:
LIT_PLUGIN_DEVTOOLS_WORKSPACE