This is a plugin to make Lit work with modern tooling featuring Vite and enabled HMR along side other nice things.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A Vite plugin for Lit projects with true HMR, CSS delivery helpers for shadow roots, and a DevTools timeline. Started life in a fork of the lit monorepo as a proposed @lit-labs/vite-hmr package, and now lives here on its own. Lit is kept checked out as a read-only submodule for reference and opt-in canary testing against lit main. Each demo opens on StackBlitz and runs the latest release of the plugin in your browser. The Demos page embeds them, so you can try one without leaving the docs. [README 설치·사용 부분 발췌] Usage檢視完整 README ↗
A Vite plugin for Lit projects with true HMR, CSS delivery helpers for shadow roots, and a DevTools timeline.
https://github.com/user-attachments/assets/d396d3a8-ab26-46de-8c3f-71558db1a392
Started life in a fork of the lit monorepo as a
proposed @lit-labs/vite-hmr package, and now lives here on its own. Lit is
kept checked out as a read-only submodule for reference and opt-in
canary testing against lit main.
Each demo opens on StackBlitz and runs the latest release of the plugin in your browser. The Demos page embeds them, so you can try one without leaving the docs.
| Demo | What to try | |
|---|---|---|
| Playground | HMR and the Vite DevTools dock: click the counter, edit src/hmr-counter.ts, and the count stays. | |
| Standalone mode | The same page as static files, without Vite, next to the DevTools panel on its own port. | |
| Lit flame | One component: turn its colours, stoke it and blow it out, then edit it and watch its state, #private field and colours survive. | |
| Web Awesome | A component built from a third-party Lit library: edit it, and the library's elements keep their checked boxes and half-typed text. | |
| Fernhouse | A small shop to explore the DevTools on: pick a card, open its slots and parts in Anatomy, then record adding it to the cart. | |
| CSS |
lit-html decides "is this the same template?" by object identity of the
TemplateStringsArray, not by content. Vite HMR re-executes an edited module,
so a one-character edit rebuilds the component's entire subtree and takes
focus, scroll, input state, and child @state with it.
This plugin fixes that with two cooperating mechanisms:
html/svg/mathml/css tags are
wrapped in dev so strings arrays are canonicalized by content, and only the
edited template rebuilds its part of the DOM.customElements.define from the
re-executed module patches the originally-registered class in place, then
restores reactive property values through the new accessors.Install @oddsquad/lit-devtools, which brings this plugin together with the
Lit language server and its Oxlint rules, or this package on its own and
import from @oddsquad/vite-plugin-lit instead of
@oddsquad/lit-devtools/vite:
npm i -D @oddsquad/lit-devtools
// vite.config.ts
import {defineConfig} from 'vite';
import {litPlugin} from '@oddsquad/lit-devtools/vite';
export default defineConfig({
plugins: [litPlugin()],
});
The HMR feature only applies to the dev server (apply: 'serve'); production
builds are untouched. The CSS queries (?hmr-url, ?css-sheet) and Lightning
CSS literal processing apply in both dev and build.
The dev-only code (HMR, the source overlay, DevTools) is imported only when
the dev server (or vite preview) starts, so vite build never loads it. For
a build config that depends on even less, use the build-only entry, which
carries just the CSS plugins:
import {litCss} from '@oddsquad/lit-devtools/build';
export default defineConfig(({command}) => ({
plugins: [command === 'serve' ? litPlugin() : litCss()],
}));
CSSStyleSheet for thousands of shadow roots, hot-swapped
without re-rendering, from a bare ?css-sheet import.Reference: options · environment variables · import queries · runtime API · CLI · limitations · compatibility · benchmarks · troubleshooting
The DevTools panel's data is also exposed as MCP tools — lit_list-components,
lit_component-details, lit_component-docs, lit_update-summary,
lit_recent-events, lit_range-summary, lit_get-meta, lit_hmr-history,
lit_hmr-incompatibilities, lit_set-recording, lit_set-prop — so an agent
can read the live component tree and timeline instead of guessing from source,
and try a property value on a live element.
These answer only while a Vite dev server with timeline on is running. There
is no stored data and nothing to go stale: with the server down an agent gets
a connection error, not an empty answer.
Point an MCP client at it either way:
# Auto-discovers every running dev server on this machine.
claude mcp add lit-devtools -- npx -y @oddsquad/lit-devtools mcp
# Or dial one server directly, if you'd rather pin the port
# (`/__lit/__mcp` when the server runs without Vite DevTools).
claude mcp add lit-devtools -- npx -y mcp-remote http://localhost:5179/__devtools/__mcp
The equivalent claude_desktop_config.json / mcp.json entry:
{
"mcpServers": {
"lit-devtools": {
"command": "npx",
"args": ["-y", "@oddsquad/lit-devtools", "mcp"]
}
}
}
The two differ in how the server is found, not in what it can do:
... vite-plugin-lit mcp runs devframe's connector over stdio. The
plugin publishes each running dev server to devframe's instance registry,
and the connector lists them through two gateway tools
(devframe_connect_list-instances, devframe_connect_call-tool) — so one
entry covers every project you have running, with no port to keep in sync.
Being a shared connector, it surfaces every devframe on the machine, not
only this plugin's tools.mcp-remote <url> bridges stdio to one fixed URL and depends on no
discovery at all. Reach for it if registry discovery is unavailable — the
plugin logs a warning saying so at startup, naming the reason.The CLI also has lit-devtools dev, a standalone devframe server. Started on
its own it shows an empty panel, because it has no page of its own. Add the
script tag it prints at startup to any page, including one that Vite does not
serve and one on another origin, and the page feeds it the live component tree,
inspector and timeline:
<script src="http://localhost:5180/lit-devtools.js"></script>
Put it before the scripts that define your components. The server asks for its
one-time code unless you start it with --no-auth: type the code into the
prompt the page shows, open the page once with #devframe_otp=<code> on its
URL, or skip the gate on a loopback host with --no-auth. Pages on a loopback
origin (any port) may connect; for any other origin, pass it to the server with
--allow-origin https://myapp.test:8443 (repeatable). A * in the host
matches any subdomain, e.g. --allow-origin 'https://*.webcontainer-api.io'
for StackBlitz, where every port gets its own origin.
Outside Vite the page has no build-time transforms, so HMR patching and
open-in-editor do not work there; the tree, inspector and timeline do. Source
locations come from the page's sourcemaps when it ships them. A page that Vite serves can instead call connectToDevServer() from
@oddsquad/vite-plugin-lit/connect.js.
Production builds. The script tag also attaches to a minified build with
Lit's production condition, checked by
src/test/e2e/standalone-production_test.ts:
@lit/task instances, even though Lit mangles its private controller set)
and Record with the lifecycle layer (performUpdate, willUpdate,
update, updated).lit-debug events.script-src (for example script-src 'self') refuses the tag, so
nothing attaches.To try it from this repo, run pnpm run standalone:demo; see
Playground.
A manually inspectable fixture app (also the source for the e2e fixtures), and self-contained enough to open on StackBlitz straight from the repo URL. Run it locally from the repo root:
pnpm dev # builds the plugin, then serves http://localhost:5179
The same playground runs in standalone mode, built to static files and fed to
a lit-devtools dev panel on another port: pnpm run standalone:demo here, or
on StackBlitz
with ?startScript=standalone. StackBlitz installs the published plugin, so
both links run the latest release of the plugin against the playground on main.
Requires Node 26 and pnpm 12 (corepack enable picks up the pinned version).
The published package itself runs on Node 20 or newer.
pnpm install # workspace: root package + playground + docs
pnpm build # tsc + panel assets -> ./lib, ./panel, ./index.js
pnpm exec vp check # format, lint, type check
pnpm test # unit + e2e
pnpm docs:dev # the documentation site
More in Development and Testing. Issues and pull requests are welcome; see CONTRIBUTING.md.
BSD-3-Clause. See LICENSE.
The DevTools bundle Web Awesome components (MIT) in the panel and Phosphor icons (MIT) in the panel and the in-page overlay.
One card per way of styling a shadow root, with UnoCSS as one shared sheet: change --brand and every card recolours without re-rendering. |
| Tailwind | Tailwind v4 utilities as one shared sheet: add a class to a component and the sheet swaps in place, with shadows and gradients working in shadow roots. |
| Forms | Form-associated custom elements from one base class, in a plain <form>: submit it empty, reset it, and watch each control's :state() change. |
| Lit SSR | Components rendered on the server with @lit-labs/ssr, then hydrated: click the counter, edit src/ssr-counter.ts, and the count stays. |