Skip to content

Use the Lit language server

The Lit language server makes your editor understand your components: it completes their tags, attributes, properties and events in html templates, shows their docs on hover, types your event handlers, and reports a handler that doesn’t fit its event. It reads your sources as you type, so a component’s docs are current before you save, and it works in a project without Vite. Language server lists everything it does.

  1. Build the extension in a checkout of the repository:

    Terminal window
    pnpm install
    cd packages/vscode
    pnpm run build && pnpm run package

    This writes lit-devtools-<platform>.vsix for your machine, with the server bundled in.

  2. In VS Code, run Extensions: Install from VSIX… from the command palette and pick that file.

  3. Open a project and hover a component’s tag in an html template.

The extension runs the project’s own @oddsquad/lit-language-server when the project installs it, directly or through @oddsquad/lit-devtools, so the version matches the project, and the bundled copy otherwise. To run a different build, point the lit.server.path setting at its dist/bin.mjs. Lit: Restart Language Server picks the server again, and the Lit Language Server output channel says which one started.

The type hints on event handlers are inlay hints, which VS Code shows unless editor.inlayHints.enabled is off.

  1. Build the server in a checkout of the repository:

    Terminal window
    pnpm install
    cd packages/lit-language-server && pnpm run build
  2. In Zed, run zed: install dev extension from the command palette and pick packages/zed. Zed compiles it, which needs Rust from rustup.

  3. Point Zed at the server you built, in its settings:

    ~/.config/zed/settings.json
    {
    "lsp": {
    "lit-language-server": {
    "binary": {
    "path": "/path/to/lit-devtools/packages/lit-language-server/dist/bin.mjs"
    }
    }
    }
    }

    Inside the repository itself the extension finds the server without this.

  4. Open a project and hover a component’s tag in an html template.

Once the package is published, a project that installs it as a dev dependency is found without the setting, and the extension otherwise installs the latest release itself.

The type hints on event handlers are inlay hints, which Zed shows when "inlay_hints": {"enabled": true} is set.

The server needs no settings. To change what it reads or how loud a problem is, set languageServer in your Vite config:

vite.config.ts
import {defineConfig} from 'vite';
import {litPlugin} from '@oddsquad/lit-devtools/vite';
export default defineConfig({
plugins: [
litPlugin({
languageServer: {
diagnostics: {css: 'off', 'event-type': 'warning'},
},
}),
],
});

In a project without Vite, put the same settings in a lit-devtools.config.ts (or .mts, .js, .mjs, .json) next to its package.json:

lit-devtools.config.ts
export default {
languageServer: {
scan: ['src', 'lib'],
cssTemplateTags: ['css', 'styles'],
},
};

When a package has both, the Vite config’s languageServer wins as a whole, and the config file gets a warning saying it’s ignored: the two are never merged key by key. A .ts config file needs Node 22.18 or later; a .js or .json one works on any. The settings reference lists every key.

The same problems can fail a lint run. Install @oddsquad/lit-devtools next to Oxlint and extend its Oxlint plugin’s recommended config, which turns each rule on at the severity the editor gives it:

Terminal window
npm install -D oxlint @oddsquad/lit-devtools
oxlint.config.ts
import {defineConfig} from 'oxlint';
import litDevtools from '@oddsquad/lit-devtools/oxlint';
export default defineConfig({
extends: [litDevtools.configs.recommended],
rules: {'lit-devtools/css': 'off'},
});

Rules set next to extends win, as lit-devtools/css does here. With Vite+, the same object goes under lint in vite.config.ts. A project that wants only the rules installs @oddsquad/oxlint-plugin-lit and imports from it instead. In an .oxlintrc.json, list the plugin and its rules yourself:

.oxlintrc.json
{
"jsPlugins": ["@oddsquad/lit-devtools/oxlint"],
"rules": {"lit-devtools/event-type": "error", "lit-devtools/css": "warn"}
}
  • Oxlint’s config decides each rule’s severity; the diagnostics setting is for the editor only. The other settings, such as scan, apply to both.
  • Silence one spot with Oxlint’s own comment, // oxlint-disable-next-line lit-devtools/event-type.
  • Oxlint reads JavaScript and TypeScript files, so problems in HTML, Astro and MDX pages still show only in the editor.
  • The first file waits while the plugin reads the workspace, once per run.

Open the monorepo at its root. The server finds the workspace from a pnpm-workspace.yaml or a workspaces field in package.json, and reads every package in it:

  • Each package is read with its own settings and its own Vite config, so its aliases apply.
  • A component can extend a base class from another workspace package; its inherited attributes and properties show up.
  • A tag from a published library completes only in the packages that depend on that library.
  • Check that the server started: in VS Code, the Lit Language Server output channel; in Zed, dev: open language server logs.
  • After rebuilding the server, restart it: Lit: Restart Language Server in VS Code, editor: restart language server in Zed.
  • Check the settings file for a warning on its first line: a mistake there is reported, not thrown.
  • With another Custom Elements Manifest server installed, such as the Web Components Language Server, hovers appear twice; disable one.