This error occurs when you try to use React Testing Library query methods (getByText, getByRole, etc.) without first rendering a component. These queries require a DOM tree to search within, which only exists after calling render(). The fix involves ensuring all queries are called after render() and using the screen object correctly.
React Testing Library query methods like getByText, getByRole, queryByText, and findByText are designed to search within a rendered DOM tree. When you call these methods without first rendering a React component using the render() function, there is no DOM tree to search, causing this error. The Testing Library ecosystem follows a specific workflow: first render your component, then query the resulting DOM. This pattern ensures tests resemble how users interact with your application and helps avoid testing implementation details. This error is particularly common for developers transitioning from Enzyme or other testing libraries that have different APIs, or when trying to use queries in test setup functions (beforeEach, beforeAll) without proper rendering.
The render() function must be called before any query methods. It creates the DOM tree that queries search within.
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
test('should render component', () => {
// First render the component
render(<MyComponent />);
// Now queries will work
const button = screen.getByRole('button', { name: /submit/i });
expect(button).toBeInTheDocument();
});Important: render() should be called inside each test or in a beforeEach hook that runs before each test.
Prefer using the screen object imported from @testing-library/react rather than destructuring queries from render(). This is the recommended pattern and avoids common pitfalls.
// ✅ Recommended pattern
import { render, screen } from '@testing-library/react';
test('example', () => {
render(<MyComponent />);
const element = screen.getByText(/hello/i);
expect(element).toBeInTheDocument();
});
// ❌ Avoid this pattern (can lead to issues)
import { render } from '@testing-library/react';
test('example', () => {
const { getByText } = render(<MyComponent />);
const element = getByText(/hello/i); // Works but not recommended
});The screen object is pre-bound to document.body and works consistently across your test suite.
Ensure queries are not placed in describe blocks, beforeAll, or beforeEach hooks (unless render is called there first). Queries should be inside test/it blocks after render.
// ❌ Wrong - query in beforeEach without render
describe('MyComponent', () => {
beforeEach(() => {
const button = screen.getByRole('button'); // Error! No render yet
});
test('example', () => {
// ...
});
});
// ✅ Correct - render in beforeEach, query in test
describe('MyComponent', () => {
beforeEach(() => {
render(<MyComponent />); // Render first
});
test('should have button', () => {
const button = screen.getByRole('button'); // Works!
expect(button).toBeInTheDocument();
});
});
// ✅ Also correct - render in each test
describe('MyComponent', () => {
test('should have button', () => {
render(<MyComponent />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
});
});If you need to query elements that appear asynchronously (after state updates, API calls, etc.), use findBy* queries which return Promises and automatically wait.
import { render, screen } from '@testing-library/react';
test('async element', async () => {
render(<AsyncComponent />);
// findBy* waits for element to appear (default timeout: 1000ms)
const asyncElement = await screen.findByText(/loaded/i);
expect(asyncElement).toBeInTheDocument();
// You can also use waitFor for more complex async scenarios
await waitFor(() => {
expect(screen.getByText(/loaded/i)).toBeInTheDocument();
});
});Note: findBy* queries still require render() to be called first.
If you're still getting the error, verify that your component renders without throwing errors. Use the debug() utility or add console logs.
import { render, screen } from '@testing-library/react';
test('debug render', () => {
// Debug what gets rendered
const { debug } = render(<MyComponent />);
debug(); // Prints the rendered DOM to console
// Check if component rendered at all
console.log(document.body.innerHTML);
// Now queries should work
const element = screen.getByText(/content/i);
});
// For more detailed debugging, increase verbosity
process.env.DEBUG_PRINT_LIMIT = 15000;Also check for any errors in your component that might prevent rendering.
If you're testing utility functions, custom hooks, or other non-UI code, don't use Testing Library UI queries. Instead, test them directly or use renderHook for hooks.
// Testing a custom hook - use renderHook
import { renderHook } from '@testing-library/react';
import { useCounter } from './useCounter';
test('custom hook', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
});
// Testing a utility function - test directly
import { formatDate } from './utils';
test('utility function', () => {
expect(formatDate('2023-01-01')).toBe('January 1, 2023');
});Testing Library queries are specifically for testing rendered UI components.
### Understanding the Testing Library Philosophy
React Testing Library is built on the principle of testing components the way users interact with them. The requirement to render before querying enforces this pattern:
1. User-centric testing: Users don't interact with React component instances; they interact with rendered DOM elements.
2. Implementation detail avoidance: By querying the DOM, you avoid testing internal component state or methods.
3. Accessibility focus: Queries like getByRole encourage accessible markup.
### Technical Details
Under the hood, render() uses:
- ReactDOM.render (React 17 and earlier)
- React 18's createRoot (with concurrent features)
- jsdom or happy-dom as the DOM environment in Jest
The resulting DOM tree is attached to document.body, which is what screen queries search.
### Common Pitfalls
1. Test isolation: Each test should render fresh. Avoid sharing rendered components between tests.
2. Cleanup: Use cleanup() or configure test framework to auto-cleanup to prevent test pollution.
3. Async rendering: For React 18 concurrent features, use await act() or findBy* queries.
4. Custom render: If using providers (Redux, Router), create a custom render function.
### Migration from Enzyme
If migrating from Enzyme, remember:
- Enzyme's wrapper.find() works on React elements
- Testing Library queries work on DOM nodes
- The mental model shift is from "component instance" to "rendered output"
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite