A highly customizable Vite plugin that automatically adds data attributes to JSX/TSX elements during development. Track, debug, and understand component rendering with powerful features like path filtering, attribute transformers, presets, and more. Perfect for AI-generated code and debugging "which component rendered this?" π€
Performance Optimizations (v2.2.0):
- π 15-30% faster build times with 3 micro-optimizations
- β‘ 5-10x faster path matching with pre-compiled glob patterns
- π¦ 2-3x faster metadata encoding with optimized JSON serialization
- π§ Modular architecture - Clean, maintainable 7-file structure
V2 Features - Complete control over component debugging:
- π― Path Filtering - Include/exclude files with glob patterns
- π§ Attribute Transformers - Customize any attribute value (privacy, formatting)
- π¨ Presets - Quick configs for common use cases (minimal, testing, debugging, production)
- β‘ Conditional Tagging - Tag only specific components with
shouldTagcallback - π·οΈ Custom Attributes - Add your own data attributes (git info, environment, etc.)
- π¦ Metadata Encoding - Choose JSON, Base64, or plain text encoding
- π Statistics & Callbacks - Track processing stats and export metrics
- ποΈ Depth Filtering - Control tagging by component nesting level
- π Attribute Grouping - Combine all attributes into single JSON attribute
- πΊοΈ Source Map Hints - Better debugging with source map comments
π View Detailed Examples & Use Cases
# 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()
enabled: process.env.NODE_ENV === "development", // When to run
attributePrefix: "data-dev", // Custom prefix
extensions: [".jsx", ".tsx"], // File types
}),
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 (Default - All Attributes):
<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 (Minimal Preset - Clean):
componentDebugger({ preset: 'minimal' })
// Results in:
<button
data-dev-id="src/components/Button.tsx:10:2"
className="btn-primary"
onClick={handleClick}
>
Click me
</button>After (Custom Filtering):
componentDebugger({
includeAttributes: ["id", "name", "line"]
})
// Results in:
<button
data-dev-id="src/components/Button.tsx:10:2"
data-dev-name="button"
data-dev-line="10"
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
});// Minimal - only ID attribute (cleanest DOM)
componentDebugger({ preset: "minimal" });
// Testing - ID, name, component (perfect for E2E)
componentDebugger({ preset: "testing" });
// Debugging - everything + metadata (full visibility)
componentDebugger({ preset: "debugging" });
// Production - privacy-focused with shortened paths
componentDebugger({ preset: "production" });π See all preset details in EXAMPLES.md
π― Clean DOM - Minimal Attributes
componentDebugger({
includeAttributes: ["id", "name"], // Only these attributes
});
// Result: Only data-dev-id and data-dev-nameποΈ Path Filtering - Specific Directories
componentDebugger({
includePaths: ["src/components/**", "src/features/**"],
excludePaths: ["**/*.test.tsx", "**/*.stories.tsx"],
});π§ Privacy - Transform Paths
componentDebugger({
transformers: {
path: (p) => p.split("/").slice(-2).join("/"), // Shorten paths
id: (id) => id.split(":").slice(-2).join(":"), // Remove path from ID
},
});β‘ Conditional - Tag Specific Components
componentDebugger({
shouldTag: ({ elementName }) => {
// Only tag custom components (uppercase)
return elementName[0] === elementName[0].toUpperCase();
},
});π‘ Pro Tip: Use
includeAttributesfor cleaner DOM instead of legacyincludeProps/includeContent
β οΈ Gotcha: When bothincludeAttributesandexcludeAttributesare set,includeAttributestakes priority
Core Options
| 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 |
preset |
Preset |
undefined |
Quick config: 'minimal' | 'testing' | 'debugging' | 'production' |
V2 Features - Attribute Control
| Option | Type | Default | Description |
|---|---|---|---|
includeAttributes |
AttributeName[] |
undefined |
Recommended: Only include these attributes |
excludeAttributes |
AttributeName[] |
undefined |
Exclude these attributes |
transformers |
object |
undefined |
Transform attribute values (privacy, formatting) |
groupAttributes |
boolean |
false |
Combine all into single JSON attribute |
Available: 'id', 'name', 'path', 'line', 'file', 'component', 'metadata'
V2 Features - Path & Element Filtering
| Option | Type | Default | Description |
|---|---|---|---|
includePaths |
string[] |
undefined |
Glob patterns to include |
excludePaths |
string[] |
undefined |
Glob patterns to exclude |
excludeElements |
string[] |
['Fragment', 'React.Fragment'] |
Element names to skip |
customExcludes |
Set<string> |
Three.js elements | Custom elements to skip |
V2 Features - Conditional & Custom
| Option | Type | Default | Description |
|---|---|---|---|
shouldTag |
(info) => boolean |
undefined |
Conditionally tag components |
customAttributes |
(info) => Record<string, string> |
undefined |
Add custom attributes dynamically |
metadataEncoding |
MetadataEncoding |
'json' |
Encoding: 'json' | 'base64' | 'none' |
V2 Features - Depth, Stats & Advanced
| Option | Type | Default | Description |
|---|---|---|---|
maxDepth |
number |
undefined |
Maximum nesting depth |
minDepth |
number |
undefined |
Minimum nesting depth |
tagOnlyRoots |
boolean |
false |
Only tag root elements |
onTransform |
(stats) => void |
undefined |
Per-file callback |
onComplete |
(stats) => void |
undefined |
Completion callback |
exportStats |
string |
undefined |
Export stats to file |
includeSourceMapHints |
boolean |
false |
Add source map comments |
debug |
boolean |
false |
Enable debug logging |
π‘ All v2 features are opt-in - Existing configs work unchanged
π See complete TypeScript types:
import { type TagOptions } from 'vite-plugin-component-debugger'
π View 50+ Detailed Examples in EXAMPLES.md β
Examples include: E2E testing setups, debug overlays, monorepo configs, feature flags, performance monitoring, and more!
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 (v2.2.0):
- π 15-30% faster than v2.1 with 3 micro-optimizations
- β‘ Pre-compiled glob patterns - 5-10x faster path matching
- π¦ Optimized JSON serialization - 2-3x faster metadata encoding
- π§ Smart string operations - 2x faster debug logging
- Time savings: 200-500ms on 100-file projects, 2-5s on 1000-file projects
- Efficient AST traversal with caching
- Minimal HMR impact
- Automatically skips
node_modules - Only runs during development
β οΈ Line numbers are wrong/offset by ~19? (Most common issue)
Problem: data-dev-line shows numbers ~19 higher than expected
Cause: Plugin order is wrong - React plugin adds ~19 lines of imports/HMR setup
Fix: Move componentDebugger() BEFORE react() in Vite config
// β 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: File must match
extensions(default:.jsx,.tsx) - Check exclusions: Element not in
excludeElementsorcustomExcludes - Check paths: File not excluded by
excludePathspattern - Check plugin order:
componentDebugger()beforereact() - Check enabled: Plugin is enabled (
enabled: true) - Check shouldTag: If using
shouldTag, callback must returntrue
Debug with:
componentDebugger({
debug: true, // Shows what's being processed
enabled: true,
});Build performance issues?
Quick fixes:
- Use
includeAttributesto reduce DOM size:includeAttributes: ["id", "name"]; // Only essential attributes
- Filter paths to only process needed directories:
includePaths: ['src/components/**'], excludePaths: ['**/*.test.tsx', '**/*.stories.tsx']
- Use
maxDepthto limit deep nesting:maxDepth: 5; // Only tag up to 5 levels deep
- Skip test files with
excludePaths
Attributes appearing in production?
componentDebugger({
enabled: process.env.NODE_ENV !== "production",
});Or use environment-specific configs:
enabled: isDev || isStaging, // Not in productionincludeAttributes vs excludeAttributes priority?
Gotcha: When both are set, includeAttributes takes priority
componentDebugger({
includeAttributes: ["id", "name", "line"],
excludeAttributes: ["name"], // β οΈ This is IGNORED
});
// Result: Only id, name, line are includedBest practice: Use one or the other, not both
TypeScript type errors?
Import types for full IntelliSense:
import componentDebugger, {
type TagOptions,
type ComponentInfo,
type AttributeName,
} from "vite-plugin-component-debugger";
const config: TagOptions = {
// Full type checking
};π 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
mainbranch 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 validationTonye 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!
