vitest-react-profiler
December 17, 2025 ยท View on GitHub
React component render tracking and performance testing utilities for Vitest
๐ Documentation โข ๐ Quick Start โข ๐ API Reference โข ๐ฌ Discussions
Features
- ๐ Precise Render Tracking - Count exact number of renders with zero guesswork
- โก Performance Monitoring - Detect unnecessary re-renders and track component behavior
- ๐ฏ Phase Detection - Distinguish between mount, update, and nested update phases
- ๐ธ Snapshot API - Create render baselines with
snapshot()and measure deltas with Extended Matchers - ๐ช Hook Profiling - Profile custom hooks with full Context support via
wrapperoption - โฑ๏ธ Async Testing - Subscribe to renders with
onRender()and wait withwaitForNextRender() - ๐ Real-Time Notifications - React to renders immediately with event-based subscriptions
- โ๏ธ React 18+ Concurrent Ready - Full support for
useTransitionanduseDeferredValue - ๐งน True Automatic Cleanup - Zero boilerplate! Components auto-clear between tests
- ๐ Zero Config - Works out of the box with Vitest and React Testing Library
- ๐ก๏ธ Built-in Safety Mechanisms - Automatic detection of infinite render loops and memory leaks
- ๐ช Full TypeScript Support - Complete type safety with custom Vitest matchers
- ๐งฌ Battle-Tested Quality - 100% mutation score, property-based testing, stress tests, SonarCloud verified.
- ๐ฌ Mathematically Verified - 266 property tests with 140,000+ randomized scenarios per run
- ๐๏ธ Stress-Tested - 34 stress tests validate performance on 10,000-render histories
- ๐ Performance Baselines - 46 benchmarks establish regression detection metrics
๐ฅ Who Is This For?
๐จ UI-Kit and Design System Developers
Building a UI-kit for your project or company? You need to track, measure, and improve component performance. This tool helps you:
- Catch unnecessary re-renders during development
- Set performance budgets for components
- Document performance characteristics in tests
๐ฆ Open Source React Library Maintainers
Publishing React components? It's critical to prove your solution is optimized and won't degrade performance in user projects. With this tool, you can:
- Add performance tests to CI/CD pipelines
- Showcase performance metrics in documentation
- Track performance regressions between releases
๐ Teams with Strict Performance SLAs
Have strict performance requirements (fintech, healthcare, real-time systems)? The tool allows you to:
- Set thresholds for render counts
- Automatically verify SLA compliance in tests
- Track asynchronous state updates
Quick Start
Installation
npm install --save-dev vitest-react-profiler
# or
yarn add -D vitest-react-profiler
# or
pnpm add -D vitest-react-profiler
Setup
// vitest-setup.ts
import "vitest-react-profiler"; // Auto-registers afterEach cleanup
Configure Vitest:
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "jsdom",
setupFiles: ["./vitest-setup.ts"],
},
});
Your First Test
import { render } from '@testing-library/react';
import { withProfiler } from 'vitest-react-profiler';
import { MyComponent } from './MyComponent';
it('should render only once on mount', () => {
const ProfiledComponent = withProfiler(MyComponent);
render(<ProfiledComponent />);
expect(ProfiledComponent).toHaveRenderedTimes(1);
expect(ProfiledComponent).toHaveMountedOnce();
});
โฑ๏ธ Async Testing
Test components with asynchronous state updates using event-based utilities.
const AsyncComponent = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
return <div>{data ?? "Loading..."}</div>;
};
it('should handle async updates', async () => {
const Profiled = withProfiler(AsyncComponent);
render(<Profiled />);
// Wait for mount + async update
await expect(Profiled).toEventuallyRenderTimes(2);
});
Key Matchers
toEventuallyRenderTimes(n)- Wait for exact render counttoEventuallyRenderAtLeast(n)- Wait for minimum renderstoEventuallyReachPhase(phase)- Wait for specific phase
๐ Read the complete guide โ
๐ฏ Stabilization API (v1.12.0)
Wait for components to "stabilize" - useful for virtualized lists, debounced search, and animations.
Key Methods
waitForStabilization(options)- Wait for renders to stop (debounce pattern)toEventuallyStabilize(options)- Matcher version for cleaner assertions
๐ Read the complete guide โ
๐ช Hook Profiling
Profile custom hooks with full Context support.
import { profileHook } from 'vitest-react-profiler';
const useCounter = (initial: number) => {
const [count, setCount] = useState(initial);
return { count, increment: () => setCount(c => c + 1) };
};
it('should track hook renders', () => {
const { result, profiler } = profileHook(() => useCounter(0));
expect(profiler).toHaveRenderedTimes(1);
act(() => result.current.increment());
expect(profiler).toHaveRenderedTimes(2);
expect(result.current.count).toBe(1);
});
With Context Support
const { result, profiler } = profileHook(() => useTheme(), {
wrapper: ({ children }) => (
<ThemeProvider theme="dark">{children}</ThemeProvider>
),
});
๐ Read the complete guide โ
โ๏ธ React 18+ Concurrent Features
Full support for React 18+ Concurrent rendering features - no special configuration needed!
The library automatically tracks renders from:
useTransition / startTransition
Test components using transitions for non-urgent updates
useDeferredValue
Test components using deferred values for performance optimization
How It Works
The library uses React's built-in <Profiler> API, which automatically handles Concurrent mode:
- โ Transitions are tracked as regular renders
- โ Deferred values trigger additional renders (as expected)
- โ Interrupted renders are handled correctly by React
- โ No special configuration or setup required
Note: The library tracks renders, not React's internal scheduling. Concurrent Features work transparently - your tests verify component behavior, not React internals.
๐ Read the complete guide โ
๐ธ Snapshot API
Create render baselines and measure deltas for optimization testing.
Testing Single Render Per Action
const ProfiledCounter = withProfiler(Counter);
render(<ProfiledCounter />);
ProfiledCounter.snapshot(); // Create baseline
fireEvent.click(screen.getByText('Increment'));
expect(ProfiledCounter).toHaveRerenderedOnce(); // Verify single rerender
Testing React.memo Effectiveness
const ProfiledList = withProfiler(MemoizedList);
const { rerender } = render(<ProfiledList items={items} theme="light" />);
ProfiledList.snapshot();
rerender(<ProfiledList items={items} theme="dark" />);
expect(ProfiledList).toNotHaveRerendered(); // Memo prevented rerender
Extended Matchers
// Sync matchers
expect(ProfiledComponent).toHaveRerendered(); // At least one rerender
expect(ProfiledComponent).toHaveRerendered(3); // Exactly 3 rerenders
// Async matchers - wait for rerenders
await expect(ProfiledComponent).toEventuallyRerender();
await expect(ProfiledComponent).toEventuallyRerenderTimes(2, { timeout: 2000 });
Key Methods & Matchers
| Method/Matcher | Description |
|---|---|
snapshot() | Mark baseline for render counting |
getRendersSinceSnapshot() | Get number of renders since baseline |
toHaveRerenderedOnce() | Assert exactly one rerender |
toNotHaveRerendered() | Assert no rerenders |
toHaveRerendered() | Assert at least one rerender |
toHaveRerendered(n) | Assert exactly n rerenders |
toEventuallyRerender() | Wait for rerender |
toEventuallyRerenderTimes(n) | Wait for exact count |
๐ Read the complete guide โ
Documentation
๐ Full documentation is available in the Wiki
Quick Links
- Architecture Documentation - ๐ Complete technical architecture (15 sections, ~14,000 lines)
- Getting Started Guide - Installation and configuration
- API Reference - Complete API documentation
- Snapshot API - Extended matchers for optimization testing
- Hook Profiling - Testing React hooks
- React 18+ Concurrent Features - useTransition & useDeferredValue
- Examples - Real-world usage patterns
- Best Practices - Tips and recommendations
- Troubleshooting - Common issues and solutions
Contributing
We welcome contributions! Please read our Contributing Guide and Code of Conduct.
# Run tests
npm test # Unit/integration tests (811 tests)
npm run test:properties # Property-based tests (266 tests, 140k+ checks)
npm run test:stress # Stress tests (34 tests, large histories)
npm run test:bench # Performance benchmarks (46 benchmarks)
npm run test:mutation # Mutation testing (100% score)
# Build
npm run build
License
MIT ยฉ Oleg Ivanov
Made with โค๏ธ by the community
Report Bug โข Request Feature โข Discussions