# Open a component in your editor from the page

> Point at any element in the running page and jump to the file and line that renders it.

Point at any element on the page and jump to the file and line that renders it. Off by default; one option turns it on.

## Turn it on

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

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

Prefer to leave the config alone? Every option also reads from the environment, so a personal `.env.local` works just as well.

```ini
LIT_PLUGIN_SOURCE_OVERLAY=true
LIT_PLUGIN_SOURCE_OVERLAY_EDITOR=zed
```

Dev only

The overlay, its middleware and its injected runtime exist only on the dev server. Nothing about it reaches a production build.

## Use it

1. Press **Ctrl+Shift+S** (**⌘⇧S** on a Mac) in the page to arm the picker.

2. Move the pointer. The element under it is outlined, and a panel shows its tag name and source file, plus a second row for the line that renders it when it has one.

   ![The source overlay outlining hmr-counter. The tooltip shows its tag name and source file, and a second row reads rendered at index.html:120.](/_astro/source-overlay-armed.light.D0FeFGDN_ZpShKq.webp)![The source overlay outlining hmr-counter. The tooltip shows its tag name and source file, and a second row reads rendered at index.html:120.](/_astro/source-overlay-armed.dark.B2tSlWSl_ZTtlb5.webp)

3. Press **↑** to outline the element around it instead, and **↓** to go back in. The tooltip names where each key goes.

4. Hold **Ctrl** (or **⌘**) and click the element to open that file in your editor at that line. Add **Shift** to open the line where that element is written instead, see [Where an element is rendered](#where-an-element-is-rendered). While you hold the keys, the tooltip lights up the row the click will open.

5. Press the same keys again to put the picker away without opening anything.

A plain click, without the modifier, hands the element to the DevTools Components tab instead, when the panel is set up.

## Where an element is rendered

The overlay and the Components tab open the file that declares a component. The dev transform also stamps a `data-lit-source="file:line:column"` attribute on every custom element written in an `html` or `svg` template, and the Components tab uses it for a **Rendered at** link to the tag that created that instance. The overlay reads it too: when the hovered element has one, a second row in the tooltip reads **rendered at** `file:line`, and **Ctrl/⌘+Shift+click** opens that spot in your editor, at its column, instead of the declaration. Without a call site it opens the declaration. A library element picked with `hosts: 'lit'` has no declaration to show, so its call site is the only row, and **Ctrl/⌘+click** opens it. Either way, the highlighted row is the one the click opens. The tooltip is only a readout: the pointer passes through it to the page, so there is nothing in it to click.

Custom elements written straight into an HTML entry file such as `index.html` get the same attribute, with the line and column in that file. That includes elements inside a `<template>`, whose clones keep it. Comments, `<script>` and `<style>` are left alone.

The attribute is added only on the dev server and only while `sourceOverlay` is on, so it never reaches a production build. It is also visible in the page’s DOM and in your own selectors, which is worth knowing if a test matches attributes exactly.

## Choose your editor

The built-in keys are `vscode` (the default), `cursor`, `zed`, `idea` and `windsurf`. An unknown key falls back to VS Code.

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

export default defineConfig({
  plugins: [litPlugin({sourceOverlay: {editor: 'zed'}})],
});
```

Anything else is a `{name, url}` pair. The `url` function receives the absolute path and the line number, and returns the URL to open. A third argument, the column, is passed when opening a call site; you can ignore it.

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

export default defineConfig({
  plugins: [
    litPlugin({
      sourceOverlay: {
        editor: {
          name: 'My Editor',
          url: (path, line, column = 1) =>
            `my-editor://open?file=${path}&line=${line}&column=${column}`,
        },
      },
    }),
  ],
});
```

The DevTools panel’s source links open the file in the editor you chose, or in whichever one the panel’s Settings tab overrides it with, for `vscode`, `cursor`, `zed` and `idea`. The in-page overlay’s own click follows the same choice. `windsurf` and custom editors have no matching launch command, and neither has a project that never picked one: those links open in the editor `launch-editor` auto-detects, set `LAUNCH_EDITOR` to steer it. Your choice still shapes the overlay’s URL-scheme fallback either way.

Opening normally goes through a dev-server middleware and falls back to the editor’s URL scheme when the server is unreachable, as in a StackBlitz preview. [DevTools architecture](/concepts/devtools-architecture/) explains why, and why the DevTools panel’s own source links take a different route.

## Change the shortcut and other options

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

export default defineConfig({
  plugins: [
    litPlugin({
      sourceOverlay: {
        key: 'i',
        throttleMs: 50,
        hosts: 'lit',
        workspaceRoot: '/Users/me/projects/app',
        exclude: (el) => el.tagName.startsWith('MY-INTERNAL-'),
        onSelect: (info) => console.log(info.tagName, info.source.filePath),
      },
    }),
  ],
});
```

* `key` is the letter combined with Ctrl+Shift, so `'i'` gives Ctrl+Shift+I. Defaults to `'s'`. Avoid `'e'`: it collides with the panel’s Meta+Shift+E pick shortcut.

* `throttleMs` throttles pointer-move hit testing. Defaults to `50`.

* `workspaceRoot` prefixes source paths, for when your editor needs absolute ones. Unset by default.

* `hosts` is `'source'` (the default) to pick only your own components, or `'lit'` to pick any Lit element, a component library’s included. Those have no source, so a click selects them in the DevTools Components tab.

* `exclude` returns `true` for elements the picker should skip.

* `onSelect` fires with the picked element’s info: `tagName`, `componentName`, `source`, and `callSite` (file, line and column of the template that wrote it) when the transform stamped one. It does not fire for an element with no `source`, such as a library one. The default action still runs after it.

The picker is shared: the DevTools Components tab reuses it for its **Pick** button, so element picking there needs `sourceOverlay` enabled too.

## Related

[Options](/reference/options/)sourceOverlay and its sub-options, with defaults and env vars.

[Inspect the component tree](/guides/devtools/components/)The DevTools tab that reuses this picker.
