npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-code-locator

v0.4.7

Published

A package that lets you right-click any element in your React app during development to jump directly to its source code location.

Readme

React Code Locator

A package that lets you right-click any element in your React app during development to jump directly to its source code location.

  • Zero dependencies: No Babel, no React DevTools, no browser extension — just one build plugin.
  • React 19 support: fiber._debugSource was removed in React 19. This package injects source metadata at build time, so it works regardless of the React version.
  • Universal: Supports Vite, Webpack, Rollup, esbuild, and Rspack.
  • Dev only: No impact on production builds.

Installation

npm i -D react-code-locator

Quick Start

Vite

vitePlugin handles both source transform and automatic client runtime injection.

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vitePlugin } from "react-code-locator";

export default defineConfig({
  plugins: [
    react(),
    vitePlugin(),
  ],
});

Next.js (Webpack)

// next.config.js
const { webpackPlugin } = require("react-code-locator");

module.exports = {
  webpack(config) {
    config.plugins.push(webpackPlugin());
    return config;
  },
};

Webpack mode only — Turbopack is not supported. Under Turbopack, Next.js never calls the webpack() hook in next.config.js, so the plugin is never loaded and never runs. There is no error and no warning — the app boots normally and clicking simply does nothing. If locating stops working after a Next.js upgrade, this is almost certainly why: Turbopack became the default bundler in Next.js 16, and was opt-in via --turbopack in Next.js 15.

Run the dev server in webpack mode instead:

next dev --webpack   # Next.js 16+: opt back out of Turbopack
next dev             # Next.js 15 and below: webpack is already the default

The cause is upstream: unplugin ships vite, rollup, webpack, rspack and esbuild adapters, but no Turbopack adapter, because Turbopack has no public plugin API for source transforms yet. Support will land here once one exists.

Create React App

// config-overrides.js
const { webpackPlugin } = require("react-code-locator");

module.exports = {
  webpack(config) {
    config.plugins.push(webpackPlugin());
    return config;
  },
};

Rollup

// rollup.config.js
import { rollupPlugin } from "react-code-locator";

export default {
  plugins: [rollupPlugin()],
};

esbuild

import { esbuildPlugin } from "react-code-locator";

await esbuild.build({
  plugins: [esbuildPlugin()],
});

Rspack

// rspack.config.js
const { rspackPlugin } = require("react-code-locator");

module.exports = {
  plugins: [rspackPlugin()],
};

Options

// Options shared by all plugins
webpackPlugin({
  // Whether to enable the plugin (default: NODE_ENV === "development")
  // Set this explicitly if you use a custom environment variable instead of NODE_ENV.
  enabled: process.env.MY_ENV === "dev",

  projectRoot: process.cwd(),    // Project root — base path for resolving source locations (default: process.cwd())
});

// Vite-only additional options
vitePlugin({
  enabled: process.env.MY_ENV === "dev",
  projectRoot: process.cwd(),

  // injectClient: true (default) — automatically injects enableReactComponentJump() into the HTML.
  // injectClient: false — disables auto injection; call enableReactComponentJump() manually.
  injectClient: true,

  editor: "code",                // Editor to open files in (default: "code"). See supported editors below.

  locator: {                     // Runtime options (injected automatically when injectClient: true)
    triggerKey: "shift",         // Trigger key: "alt" | "meta" | "ctrl" | "shift" | "none" (default: "shift")
    projectRoot: process.cwd(),  // Base path for normalizing source paths (default: not set)
    openInEditor: true,          // Show "Open in editor" in the right-click menu (default: false)
    onLocate(result) {},         // Callback when a source location is found
    onError(error) {},           // Callback on error
  },
});

Usage

Hover Highlight

Hold the trigger key (default: Shift) and hover over an element. The element will be highlighted in blue like a DevTools inspector, showing the component file name and line number.

Right-Click Context Menu

Hold the trigger key and right-click any element to open the context menu.

┌─────────────────────────────────┐
│  Open in editor                 │  ← openInEditor: true 일 때 표시
│  Copy path                      │
└─────────────────────────────────┘

| Item | Action | |------|--------| | Open in editor | Opens the source file in your editor and jumps to the exact line (shown when openInEditor: true) | | Copy path | Copies the source path in file:line:col format to the clipboard |

The browser's default context menu is automatically suppressed.

Keyboard Shortcuts

| Key | Action | |-----|--------| | Shift + Click | Print source location to the console | | Alt + 1 | Screen mode (components visible on screen, default) | | Alt + 2 | Implementation mode (implementation location) |

Opening in Editor

Set openInEditor: true to show the "Open in editor" option in the right-click menu.

Vite

The /__open-in-editor endpoint is automatically registered by vitePlugin. No extra setup needed.

// vite.config.ts
vitePlugin({
  editor: "code",   // editor command (default: "code")
  locator: {
    openInEditor: true,
  },
})

Webpack / Rspack

Add openInEditorMiddleware to your devServer and pass the editor command.

// webpack.config.js
const { webpackPlugin, openInEditorMiddleware } = require("react-code-locator");

module.exports = {
  plugins: [webpackPlugin()],
  devServer: {
    setupMiddlewares(middlewares) {
      middlewares.unshift({
        name: "open-in-editor",
        path: "/__open-in-editor",
        middleware: openInEditorMiddleware("code"),  // pass your editor command
      });
      return middlewares;
    },
  },
};
// main.tsx
import { enableReactComponentJump } from "react-code-locator";

enableReactComponentJump({ openInEditor: true });

Supported Editors

| Editor | Command | |--------|---------| | VS Code | "code" | | VS Code Insiders | "code-insiders" | | Cursor | "cursor" | | VSCodium | "codium" / "vscodium" | | WebStorm | "webstorm" | | IntelliJ IDEA | "idea" | | GoLand | "goland" | | PyCharm | "pycharm" | | PhpStorm | "phpstorm" | | RubyMine | "rubymine" | | CLion | "clion" | | Rider | "rider" | | Zed | "zed" | | Sublime Text | "subl" | | Atom | "atom" | | Vim | "vim" | | Emacs | "emacs" |

Manual Setup

Disable auto injection with injectClient: false and call enableReactComponentJump manually to control activation. This approach is also used in non-Vite environments.

// vite.config.ts
vitePlugin({ injectClient: false })
// main.tsx
import { enableReactComponentJump } from "react-code-locator";

enableReactComponentJump({
  enabled: true,                   // default: true. Set to false to disable.
  triggerKey: "shift",             // "alt" | "meta" | "ctrl" | "shift" | "none" (default: "shift")
  projectRoot: "/path/to/project", // Base path for normalizing source paths (optional)
  openInEditor: true,              // Show "Open in editor" in the right-click menu
  onLocate(result) {
    console.log("Source:", result.source);  // result.source, result.mode
  },
  onError(error) {
    console.error("Error:", error);
  },
});

What Gets Detected

Every JSX element gets its source location registered, so clicking any rendered markup resolves. On top of that, these component declaration forms are annotated with their definition site (used by implementation mode, Alt+2):

| Form | Example | | --- | --- | | Function declaration | function Card() {} | | Arrow / function expression | const Card = () => {} | | Class component | class Card extends React.Component {} | | memo / forwardRef | const Card = memo(...) | | Any HOC call | const Card = withAuth(Base) | | Tagged template | const Card = styled(Base)`...` | | Nested (non-top-level) | function Outer() { const Row = () => {} } |

Detection keys off the capitalized-name convention: only identifiers starting with an uppercase letter are treated as components. Classes are only annotated when they extend a base class, so plain data classes are left alone.

File types: .tsx, .jsx, .js and .ts. JSX wrapping runs on everything except .ts (which cannot contain JSX, and where the JSX parser would misread <T,> generic arrow functions).

Known Limitations

  • React Native not supported: Relies on the DOM API.
  • Turbopack not supported: Turbopack has no public plugin API for source transforms, and unplugin has no Turbopack adapter as a result. Under Turbopack the plugin is never loaded and fails silently — see the Next.js section for the --webpack workaround.
  • Disabled elements / blocked pointer-events: Elements with a disabled attribute or pointer-events: none applied will not fire click events and cannot be detected.
  • CRA (Create React App): The webpack config is hidden, so plugin injection requires react-app-rewired or craco.
  • Dev only: The plugin's enabled option defaults to NODE_ENV === "development", so it is automatically disabled in production builds.

License

MIT