How to fix Warning: An update to ForwardRef(Component) inside a test was not wrapped in act(...) in React
This React Testing Library warning occurs when component state updates happen in tests without proper async handling. The warning indicates untested asynchronous behavior in your components.
Quick fix
Fixes Warning: An update to ForwardRef(Component) inside a test was not wrapped in act(...)
// ❌ Bad - synchronous query
const button = screen.getByRole('button');
fireEvent.click(button);
expect(screen.getByText('Success')).toBeInTheDocument();
// ✅ Good - async query with waitFor
const button = screen.getByRole('button');
fireEvent.click(button);
await waitFor(() => {
expect(screen.getByText('Success')).toBeInTheDocument();
});
// ✅ Better - use findBy which includes waiting
const button = screen.getByRole('button');
fireEvent.click(button);
expect(await screen.findByText('Success')).toBeInTheDocument();- Use React Testing Library async utilities instead of manual act()
- Replace fireEvent with userEvent for user interactions
- Wrap async assertions in waitFor()
- Handle timers in tests with fake timers
// ❌ Bad - synchronous queryconst button = screen.getByRole('button');fireEvent.click(button);expect(screen.getByText('Success')).toBeInTheDocument();// ✅ Good - async query with waitForconst button = screen.getByRole('button');fireEvent.click(button);await waitFor(() => {expect(screen.getByText('Success')).toBeInTheDocument();});// ✅ Better - use findBy which includes waitingconst button = screen.getByRole('button');fireEvent.click(button);expect(await screen.findByText('Success')).toBeInTheDocument();Warning: An update to ForwardRef(Component) inside a test was not wrapped in act(...)
What this error means
This warning appears when React detects state updates or side effects happening in your test environment that weren't properly awaited or wrapped in the act() utility. React's testing utilities require that all component updates be wrapped in act() to ensure the test waits for updates to complete before making assertions. The warning is React's way of telling you that something asynchronous happened in your component that your test didn't account for. This often occurs with useEffect hooks, timers, async API calls, or event handlers that update state. The warning doesn't mean your code is broken in production, but it indicates your test isn't fully capturing the component's behavior, which could lead to flaky or unreliable tests.
Typical symptoms
- Warning message appears in test console output
- Tests pass but show act() warnings
- Warning references specific component names like ForwardRef or component function names
- Multiple warnings appear for a single test case
- Warnings only occur in test environment, not in browser
- Async state updates happen after test assertions complete
- useEffect hooks or timers trigger after test finishes
Common causes
- Using fireEvent instead of userEvent for interactions that trigger async updates
- Not awaiting async operations with waitFor() or findBy queries
- State updates in useEffect hooks that run after component renders
- setTimeout or setInterval calls that update state asynchronously
- API calls or promises that resolve after test assertions
- Event handlers that trigger state changes without proper async handling
- Third-party components with internal async behavior
- Missing cleanup in useEffect causing updates after unmount
How to fix "Warning: An update to ForwardRef(Component) inside a test was not wrapped in act(...)"
React Testing Library provides built-in async utilities that automatically wrap updates in act(). Replace synchronous queries with async alternatives:
// ❌ Bad - synchronous query
const button = screen.getByRole('button');
fireEvent.click(button);
expect(screen.getByText('Success')).toBeInTheDocument();
// ✅ Good - async query with waitFor
const button = screen.getByRole('button');
fireEvent.click(button);
await waitFor(() => {
expect(screen.getByText('Success')).toBeInTheDocument();
});
// ✅ Better - use findBy which includes waiting
const button = screen.getByRole('button');
fireEvent.click(button);
expect(await screen.findByText('Success')).toBeInTheDocument();The findBy queries automatically wait for elements to appear and are wrapped in act().
The userEvent library from @testing-library/user-event provides more realistic user interactions and handles async updates properly:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('handles button click', async () => {
const user = userEvent.setup();
render(<MyComponent />);
// ❌ fireEvent doesn't wait for async updates
// fireEvent.click(screen.getByRole('button'));
// ✅ userEvent properly awaits all updates
await user.click(screen.getByRole('button'));
expect(screen.getByText('Updated')).toBeInTheDocument();
});Always await userEvent methods as they return promises.
For assertions that depend on async state updates, use waitFor() to wait for the condition to be met:
import { render, screen, waitFor } from '@testing-library/react';
test('updates after async operation', async () => {
render(<AsyncComponent />);
// Trigger the async operation
const button = screen.getByRole('button');
await userEvent.click(button);
// Wait for the update to complete
await waitFor(() => {
expect(screen.getByText('Loading complete')).toBeInTheDocument();
});
// Or wait for element to appear
await waitFor(() => {
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
});waitFor() automatically retries the callback until it succeeds or times out.
If your component uses setTimeout or setInterval, use Jest fake timers to control timing:
import { render, screen, act } from '@testing-library/react';
test('handles delayed state update', async () => {
jest.useFakeTimers();
render(<ComponentWithTimeout />);
// Fast-forward time and flush all pending timers
await act(async () => {
jest.runAllTimers();
});
expect(screen.getByText('Timeout complete')).toBeInTheDocument();
jest.useRealTimers();
});Wrap timer advances in act() to ensure React processes all updates.
Missing cleanup functions can cause state updates after component unmount:
// ❌ Bad - no cleanup, may update after unmount
useEffect(() => {
fetchData().then(data => setData(data));
}, []);
// ✅ Good - cleanup cancels pending updates
useEffect(() => {
let cancelled = false;
fetchData().then(data => {
if (!cancelled) {
setData(data);
}
});
return () => {
cancelled = true;
};
}, []);
// ✅ Also good - with AbortController
useEffect(() => {
const controller = new AbortController();
fetchData(controller.signal)
.then(data => setData(data))
.catch(err => {
if (err.name !== 'AbortError') throw err;
});
return () => controller.abort();
}, []);When mocking API calls or async functions, ensure they resolve synchronously in tests or are properly awaited:
import { render, screen, waitFor } from '@testing-library/react';
// Mock the API
jest.mock('../api', () => ({
fetchUser: jest.fn(),
}));
import { fetchUser } from '../api';
test('loads user data', async () => {
// Mock resolves immediately
fetchUser.mockResolvedValue({ name: 'John' });
render(<UserProfile userId="123" />);
// Wait for the data to load
expect(await screen.findByText('John')).toBeInTheDocument();
// Or use waitFor
await waitFor(() => {
expect(screen.getByText('John')).toBeInTheDocument();
});
});For rare cases where you need to trigger updates outside React Testing Library utilities:
import { render, act } from '@testing-library/react';
test('handles external state manager', async () => {
const { container } = render(<Component />);
// Manual state update outside React
await act(async () => {
externalStore.dispatch({ type: 'UPDATE' });
// Wait for any promises to resolve
await new Promise(resolve => setTimeout(resolve, 0));
});
expect(container.textContent).toContain('Updated');
});Only use this as a last resort - prefer React Testing Library's built-in utilities.
Advanced notes
The act() warning is actually a helpful signal from React's test renderer, not just noise to suppress. It indicates that your test isn't fully exercising the component's behavior. When you see this warning, it means there's asynchronous work happening that you're not testing. Suppressing the warning without addressing the root cause can lead to race conditions and flaky tests. In React 18 and later, the act() warning has been refined to reduce false positives, but genuine warnings should still be addressed. For complex components with multiple async operations, consider breaking them into smaller, more testable units. Third-party component libraries may trigger act() warnings due to their internal implementation - in these cases, ensure you're using the latest version of both the library and React Testing Library, as compatibility improvements are ongoing. The React team recommends treating act() warnings with the same seriousness as test failures, as they indicate incomplete test coverage of component behavior.
Frequently asked questions
- What does "Warning: An update to ForwardRef(Component) inside a test was not wrap" mean?
- This warning appears when React detects state updates or side effects happening in your test environment that weren't properly awaited or wrapped in the act() utility. React's testing utilities require that all component updates be wrapped in act() to ensure the test waits for updates to complete before making assertions. The warning is React's way of telling you that something asynchronous happened in your component that your test didn't account for.
- How do I fix "Warning: An update to ForwardRef(Component) inside a test was not wrap"?
- This React Testing Library warning occurs when component state updates happen in tests without proper async handling. The warning indicates untested asynchronous behavior in your components.
- What causes "Warning: An update to ForwardRef(Component) inside a test was not wrap"?
- Using fireEvent instead of userEvent for interactions that trigger async updates Not awaiting async operations with waitFor() or findBy queries State updates in useEffect hooks that run after component renders
- Is the "Warning: An update to ForwardRef(Component) inside a test was not wrap" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚React Official Documentation
- 📝
- 📖React Official Docs - Test Utilities: act()(Official Docs)
- 📖React Testing Library - waitFor API(Official Docs)
- 🐙
- 📝
- 📖Testing Library - user-event Introduction(Official Docs)