Skip to main content

useComponentId

Get a unique identifier for each component instance.

Overview

useComponentId generates a unique ID for each component instance, useful for debugging, tracking, and identifying specific components in large applications.

API

const id = useComponentId(componentName?: string);

Parameters:

  • componentName: Optional prefix for the ID (default: 'component')

Returns:

  • string: Unique ID like "component-1", "MyComponent-5"

Basic Example

import { useComponentId } from 'react-dev-debugger';

function Card() {
const id = useComponentId('Card');

console.log(`Card instance: ${id}`); // "Card-1", "Card-2", etc.

return <div id={id}>Card Content</div>;
}

Live Playground

Component Instances

Multiple Instances
Card 1Card-1
Count: 0
Renders: 0
Card 2Card-2
Count: 0
Renders: 0
function Card() {
  const id = useComponentId('Card');
  
  console.log('Card ID:', id);
  // Card-1, Card-2, Card-3, etc.
  
  return <div id={id}>Card Content</div>;
}
Dynamic Components
ButtonButtonComponent-3
Instance #1
InputInputComponent-4
Instance #2
SelectSelectComponent-5
Instance #3
function DynamicWidget({ type }) {
  const id = useComponentId(`${type}Widget`);
  
  useEffect(() => {
    console.log(`[${id}] Mounted`);
    return () => console.log(`[${id}] Unmounted`);
  }, [id]);
  
  return <div id={id}>{type} Widget</div>;
}
Accessibility Example
Choose a unique username
We'll never share your email
function FormField({ label, name }) {
  const fieldId = useComponentId(`FormField-${name}`);
  
  return (
    <div>
      <label htmlFor={fieldId}>{label}</label>
      <input
        id={fieldId}
        name={name}
        aria-describedby={`${fieldId}-hint`}
      />
      <span id={`${fieldId}-hint`}>Helper text</span>
    </div>
  );
}

Component IDs

How it works:

  • Each component gets unique ID on mount
  • ID persists across re-renders
  • Format: prefix-number
  • Useful for debugging and tracking
Use Cases
  • Accessibility: Generate unique IDs for labels and ARIA attributes
  • Debugging: Track specific component instances in logs
  • Analytics: Identify which instance triggered events
  • Testing: Use as stable test IDs
API
// Basic usage
const id = useComponentId();
// "component-1"

// With prefix
const id = useComponentId('Card');
// "Card-1"

// With component name
const id = useComponentId('UserProfile');
// "UserProfile-1"

Best Practices:

  • Use descriptive prefixes
  • Don't use as React keys
  • Perfect for ARIA IDs
  • Great for debugging logs

Advanced Examples

List Items

function TodoList({ todos }: { todos: Todo[] }) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}

function TodoItem({ todo }: { todo: Todo }) {
const componentId = useComponentId('TodoItem');

useEffect(() => {
console.log(`${componentId} mounted`);
return () => console.log(`${componentId} unmounted`);
}, [componentId]);

return (
<li data-component-id={componentId}>
{todo.text}
</li>
);
}

Dynamic Components

function DynamicWidget({ type }: { type: string }) {
const id = useComponentId(`Widget-${type}`);

useEffect(() => {
// Track widget lifecycle
analytics.track('widget_mounted', {
componentId: id,
widgetType: type,
});
}, [id, type]);

return (
<div id={id} className={`widget-${type}`}>
<h3>Widget {id}</h3>
</div>
);
}
function Modal({ title, children }: ModalProps) {
const modalId = useComponentId('Modal');

useEffect(() => {
console.log(`Modal ${modalId} opened: ${title}`);

return () => {
console.log(`Modal ${modalId} closed`);
};
}, [modalId, title]);

return (
<div
id={modalId}
className="modal"
aria-labelledby={`${modalId}-title`}
>
<h2 id={`${modalId}-title`}>{title}</h2>
{children}
</div>
);
}

Form Fields

function FormField({ 
label,
name,
value,
onChange
}: FormFieldProps) {
const fieldId = useComponentId(`FormField-${name}`);

return (
<div className="form-field">
<label htmlFor={fieldId}>{label}</label>
<input
id={fieldId}
name={name}
value={value}
onChange={onChange}
aria-describedby={`${fieldId}-error`}
/>
<span id={`${fieldId}-error`} className="error" />
</div>
);
}

Error Boundaries

function ComponentWithErrorBoundary() {
const componentId = useComponentId('TrackedComponent');

const handleError = useCallback((error: Error) => {
console.error(`Error in ${componentId}:`, error);

// Report to error tracking service
errorTracker.report({
componentId,
error: error.message,
stack: error.stack,
});
}, [componentId]);

return (
<ErrorBoundary onError={handleError}>
<YourComponent />
</ErrorBoundary>
);
}

Debugging Features

Component Lifecycle

Track component instances:

function TrackedComponent() {
const id = useComponentId('TrackedComponent');

useEffect(() => {
console.log(`[${id}] Mounted`);

return () => {
console.log(`[${id}] Unmounted`);
};
}, [id]);

useEffect(() => {
console.log(`[${id}] Updated`);
});

return <div>{id}</div>;
}

Performance Monitoring

function PerformanceMonitored() {
const id = useComponentId('PerformanceMonitored');

useEffect(() => {
const startTime = performance.now();

return () => {
const endTime = performance.now();
const duration = endTime - startTime;

console.log(`${id} lifetime: ${duration}ms`);
};
}, [id]);

return <div>Content</div>;
}

Analytics Integration

function AnalyticsTracked() {
const id = useComponentId('AnalyticsTracked');

useEffect(() => {
analytics.track('component_view', {
componentId: id,
timestamp: Date.now(),
});
}, [id]);

return <div>Tracked content</div>;
}

Best Practices

✅ Do's

  • Use descriptive prefixes: 'UserCard' instead of 'comp'
  • Combine with component name: Makes debugging easier
  • Use for accessibility: Generate unique IDs for ARIA attributes
  • Track component lifecycle: Log mount/unmount events

❌ Don'ts

  • Don't use as React key: Use stable IDs from data instead
  • Don't rely on ID format: Implementation may change
  • Don't use for business logic: Only for debugging/tracking
  • Don't generate IDs outside components: Hook must be called in component

Use Cases

Accessibility

function Accordion() {
const accordionId = useComponentId('Accordion');

return (
<div>
<button
id={`${accordionId}-trigger`}
aria-controls={`${accordionId}-content`}
aria-expanded="false"
>
Toggle
</button>
<div
id={`${accordionId}-content`}
aria-labelledby={`${accordionId}-trigger`}
>
Content
</div>
</div>
);
}

Testing

function TestableComponent() {
const id = useComponentId('TestableComponent');

return (
<div data-testid={id}>
<button data-testid={`${id}-button`}>Click</button>
</div>
);
}

// In tests:
test('renders component', () => {
render(<TestableComponent />);
expect(screen.getByTestId(/TestableComponent-\d+/)).toBeInTheDocument();
});

Debug Logging

function DebugComponent({ value }: { value: number }) {
const id = useComponentId('DebugComponent');

useEffect(() => {
console.log(`[${id}] Value changed:`, value);
}, [id, value]);

return <div>{value}</div>;
}

TypeScript Support

Full type safety:

function TypedComponent<T>({ data }: { data: T }) {
const id: string = useComponentId('TypedComponent');

useEffect(() => {
console.log(`${id} received data:`, data);
}, [id, data]);

return <div id={id}>Content</div>;
}

Component Identification

function ComponentTree() {
const parentId = useComponentId('Parent');

return (
<div id={parentId}>
<Child parentId={parentId} />
<Child parentId={parentId} />
</div>
);
}

function Child({ parentId }: { parentId: string }) {
const childId = useComponentId('Child');

console.log(`${childId} is child of ${parentId}`);

return <div id={childId}>Child</div>;
}

Multiple Instances

function ComponentFactory() {
return (
<>
<Card /> {/* Card-1 */}
<Card /> {/* Card-2 */}
<Card /> {/* Card-3 */}
</>
);
}

function Card() {
const id = useComponentId('Card');

return (
<div id={id}>
Card instance: {id}
</div>
);
}

Next Steps