Get the source file location of any DOM element. Works with React, Preact, Vue, Svelte, and Solid.
I originally built this to power React Grab, a way to select elements in the browser and give the source as context to coding agents. Agents are incredibly good and token efficient at using file sources. Now anyone can use what makes React Grab possible.
npm install element-sourceIf you're using Preact, import preact/debug in development so owner stacks and source locations are available.
Pass any DOM element to resolveElementInfo to get its source file location, component name, and full component stack.
import{resolveElementInfo}from"element-source";constinfo=awaitresolveElementInfo(element);// {// tagName: "button",// componentName: "App",// source: { filePath: "src/App.tsx", lineNumber: 42, columnNumber: 10, componentName: "App" },// stack: [...]// }Returns complete metadata: tag name, component name, source location, and full stack.
constinfo=awaitresolveElementInfo(document.querySelector("#root button"));// {// tagName: "button",// componentName: "Counter",// source: { filePath: "src/Counter.tsx", lineNumber: 12, columnNumber: 5, componentName: "Counter" },// stack: [// { filePath: "src/Counter.tsx", lineNumber: 12, columnNumber: 5, componentName: "Counter" },// { filePath: "src/App.tsx", lineNumber: 8, columnNumber: 3, componentName: "App" },// ]// }Returns the primary source location.
constsource=awaitresolveSource(element);// { filePath: "src/Counter.tsx", lineNumber: 12, columnNumber: 5, componentName: "Counter" }Returns the full stack of source frames (React + framework combined).
conststack=awaitresolveStack(element);// [// { filePath: "src/Counter.tsx", lineNumber: 12, columnNumber: 5, componentName: "Counter" },// { filePath: "src/App.tsx", lineNumber: 8, columnNumber: 3, componentName: "App" },// ]Returns the nearest user-defined component name.
constname=awaitresolveComponentName(element);// "Counter"Creates a resolver with custom framework resolvers.
import{createSourceResolver,svelteResolver,vueResolver}from"element-source";const{ resolveSource, resolveStack, resolveComponentName, resolveElementInfo }=createSourceResolver({resolvers: [svelteResolver,vueResolver],});constinfo=awaitresolveElementInfo(element);Formats a single source frame as a stack-trace-style string.
constframe={filePath: "src/App.tsx",lineNumber: 42,columnNumber: 10,componentName: "App"};formatStackFrame(frame);// "\n in App (at src/App.tsx:42:10)"Formats an array of source frames.
conststack=[{filePath: "src/Counter.tsx",lineNumber: 12,columnNumber: 5,componentName: "Counter"},{filePath: "src/App.tsx",lineNumber: 8,columnNumber: 3,componentName: "App"},];formatStack(stack);// "\n in Counter (at src/Counter.tsx:12:5)\n in App (at src/App.tsx:8:3)"formatStack(stack,1);// "\n in Counter (at src/Counter.tsx:12:5)"Returns the tag name from any host instance. Handles DOM Element.tagName, Ink nodeName, and falls back to "".
getTagName(document.createElement("div"));// "div"getTagName({nodeName: "ink-text"});// "ink-text"getTagName({});// ""