A Vite plugin that automatically adds data attributes to JSX/TSX elements during development, making it easier to track, debug, and understand component rendering in your React applications. Perfect for AI-generated code and debugging "which component rendered this?" π€
# Install
pnpm add -D vite-plugin-component-debugger
# or: npm install --save-dev vite-plugin-component-debugger
# or: yarn add -D vite-plugin-component-debugger
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import componentDebugger from "vite-plugin-component-debugger";
export default defineConfig({
plugins: [
componentDebugger(), // β οΈ IMPORTANT: Must be BEFORE react()
react(),
],
});
β οΈ CRITICAL: componentDebugger() must be placed BEFORE react() plugin, otherwise line numbers will be wrong
Before:
// src/components/Button.tsx (line 10)
<button className="btn-primary" onClick={handleClick}>
Click me
</button>
After (Basic - Default):
<button
data-dev-id="src/components/Button.tsx:10:2"
data-dev-name="button"
data-dev-path="src/components/Button.tsx"
data-dev-line="10"
data-dev-file="Button.tsx"
data-dev-component="button"
className="btn-primary"
onClick={handleClick}
>
Click me
</button>
After (With Metadata Enabled):
componentDebugger({
includeProps: true,
includeContent: true
})
// Results in:
<button
data-dev-id="src/components/Button.tsx:10:2"
data-dev-name="button"
data-dev-path="src/components/Button.tsx"
data-dev-line="10"
data-dev-file="Button.tsx"
data-dev-component="button"
data-dev-metadata="%7B%22className%22%3A%22btn-primary%22%2C%22text%22%3A%22Click%20me%22%7D"
className="btn-primary"
onClick={handleClick}
>
Click me
</button>
- π Debug Faster: Find which component renders any DOM element
- π Jump to Source: Go directly from DevTools to your code
- π― Stable Testing: Use data attributes for reliable E2E tests
- β‘ Zero Runtime Cost: Only runs during development
- π§ Smart Exclusions: Automatically skips Fragment and Three.js elements
componentDebugger({
enabled: process.env.NODE_ENV === "development", // When to run
attributePrefix: "data-dev", // Custom prefix
extensions: [".jsx", ".tsx"], // File types
});
componentDebugger({
// Core settings
enabled: process.env.NODE_ENV === "development",
attributePrefix: "data-dev",
extensions: [".jsx", ".tsx"],
// Content capture (disabled by default for performance)
includeProps: true, // Enable to capture component props in data-dev-metadata
includeContent: true, // Enable to capture text content in data-dev-metadata
// Element exclusions
excludeElements: ["Fragment", "React.Fragment"],
customExcludes: new Set(["mesh", "group", "camera"]), // Three.js elements
});
Option | Type | Default | Description |
---|---|---|---|
enabled |
boolean |
true |
Enable/disable the plugin |
attributePrefix |
string |
'data-dev' |
Prefix for data attributes |
extensions |
string[] |
['.jsx', '.tsx'] |
File extensions to process |
includeProps |
boolean |
false |
Include component props in metadata |
includeContent |
boolean |
false |
Include text content in metadata |
excludeElements |
string[] |
['Fragment', 'React.Fragment'] |
Elements to exclude |
customExcludes |
Set<string> |
Three.js elements | Custom elements to exclude |
Find components in the DOM:
// In browser console
document.querySelectorAll('[data-dev-component="Button"]');
console.log("Button locations:", [...$$('[data-dev-path*="Button"]')]);
Stable selectors for tests:
// Cypress
cy.get('[data-dev-component="SubmitButton"]').click();
cy.get('[data-dev-path*="LoginForm"]').should("be.visible");
// Playwright
await page.click('[data-dev-component="SubmitButton"]');
await expect(page.locator('[data-dev-path*="LoginForm"]')).toBeVisible();
Build custom debugging overlays:
// Show component boundaries on hover
document.addEventListener("mouseover", (e) => {
const target = e.target;
if (target.dataset?.devComponent) {
target.style.outline = "2px solid red";
console.log(`Component: ${target.dataset.devComponent}`);
console.log(`Location: ${target.dataset.devPath}:${target.dataset.devLine}`);
}
});
Track component render activity:
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.dataset?.devId) {
console.log(`Component rendered: ${node.dataset.devId}`);
}
});
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
// Different configs per environment
const isDev = process.env.NODE_ENV === "development";
const isStaging = process.env.NODE_ENV === "staging";
export default defineConfig({
plugins: [
componentDebugger({
enabled: isDev || isStaging,
attributePrefix: isStaging ? "data-staging" : "data-dev",
includeProps: isDev, // Enable metadata in development
includeContent: isDev, // Enable content capture in development
}),
react(),
],
});
Automatically excludes Three.js elements:
// Default exclusions
componentDebugger({
customExcludes: new Set([
"mesh",
"group",
"scene",
"camera",
"ambientLight",
"directionalLight",
"pointLight",
"boxGeometry",
"sphereGeometry",
"planeGeometry",
"meshBasicMaterial",
"meshStandardMaterial",
// ... and many more
]),
});
// To include Three.js elements
componentDebugger({
customExcludes: new Set(), // Empty set = tag everything
});
Full type definitions included:
import componentDebugger, { type TagOptions } from "vite-plugin-component-debugger";
const config: TagOptions = {
enabled: true,
attributePrefix: "data-track",
};
export default defineConfig({
plugins: [componentDebugger(config), react()],
});
π Component Debugger Statistics:
Total files scanned: 45
Files processed: 32
Elements tagged: 287
Performance optimizations:
- Efficient AST traversal with caching
- Minimal HMR impact
- Automatically skips
node_modules
- Only runs during development
- Most common issue: Plugin order is wrong
- Fix: Move
componentDebugger()
BEFOREreact()
in Vite config - Cause: React plugin adds ~19 lines of imports/HMR setup
// β WRONG - Line numbers will be offset
export default defineConfig({
plugins: [
react(), // Transforms code first, adds ~19 lines
componentDebugger(), // Gets wrong line numbers
],
});
// β
CORRECT - Accurate line numbers
export default defineConfig({
plugins: [
componentDebugger(), // Processes original source first
react(), // Transforms after tagging
],
});
Elements not being tagged?
- Check file extension is in
extensions
- Verify element isn't in exclusion lists
- Ensure plugin is enabled
- Verify plugin order (componentDebugger before react)
Build performance issues?
- Limit
extensions
scope - Add more elements to
excludeElements
- Keep
includeProps
/includeContent
disabled (default) for better performance and less noise in the DOM
Attributes not in production?
componentDebugger({
enabled: process.env.NODE_ENV !== "production",
});
Debug line number issues:
componentDebugger({
debug: true, // Shows processed code and line numbers
enabled: true,
});
π Every commit to main
triggers automatic release:
Commit Message β Version Bump:
BREAKING CHANGE:
ormajor:
β Major (1.0.0 β 2.0.0)feat:
orfeature:
orminor:
β Minor (1.0.0 β 1.1.0)- Everything else β Patch (1.0.0 β 1.0.1)
Example commit messages:
# Major version (breaking changes)
git commit -m "BREAKING CHANGE: removed deprecated API"
git commit -m "major: complete rewrite of plugin interface"
# Minor version (new features)
git commit -m "feat: add TypeScript 5.0 support"
git commit -m "feature: new configuration option for props"
git commit -m "minor: add custom exclude patterns"
# Patch version (bug fixes, docs, chores)
git commit -m "fix: resolve memory leak in transformer"
git commit -m "docs: update README examples"
git commit -m "chore: update dependencies"
# Skip release
git commit -m "docs: fix typo [skip ci]"
What happens automatically:
- Tests run, package builds
- Version bump based on commit message
- GitHub release created with changelog
- Package published to npm
Setup auto-publishing:
- Get NPM token:
npm token create --type=automation
- Add to GitHub repo: Settings β Secrets β
NPM_TOKEN
- Commit to
main
branch to trigger first release
- Fork and clone
pnpm install
- Make changes and add tests
pnpm run check
(lint + test + build)- Commit with semantic message (see above)
- Open PR
See .github/COMMIT_CONVENTION.md
for examples.
git clone https://github.com/yourusername/vite-plugin-component-debugger.git
cd vite-plugin-component-debugger
pnpm install
pnpm run test # Run tests
pnpm run build # Build package
pnpm run check # Full validation
Tonye Brown - Builder, Front-end developer, designer, and performance optimization expert crafting immersive web experiences. Also a Music Producer and Artist.
Connect:
- π Website
- π Plugin Docs
- π¦ Twitter
- πΌ LinkedIn
Support This Project:
- β Star this repository
- β Buy me a coffee
- π Sponsor on GitHub
- π Report issues or suggest features
- π€ Contribute code via pull requests
- π’ Share with other developers
MIT Β© Tonye Brown
Made with β€οΈ by Tonye Brown
Inspired by lovable-tagger, enhanced for the Vite ecosystem.
β Star this repo if it helped you!