This error occurs when React Testing Library's text matching queries (getByText, findByText) fail because the actual text content in the DOM does not match the expected text pattern. It often happens due to whitespace differences, text split across elements, or asynchronous content updates.
The "Text content does not match" error indicates that React Testing Library cannot find an element with the specified text content. This happens when the text query (using exact string, regex, or function) does not match any element's textContent in the rendered DOM. The error message shows both the expected text pattern and the actual text received, helping identify mismatches in whitespace, formatting, or element structure.
The most common cause is whitespace mismatches. Use .trim() on expected strings or include whitespace in your query. You can also use regex with \s* to match optional whitespace.
// Problem: trailing space in DOM
<div>Login </div>
// Fails: exact match
screen.getByText("Login");
// Solution 1: trim text content
const element = screen.getByText((content, element) => {
return element.textContent.trim() === "Login";
});
// Solution 2: include whitespace in query
screen.getByText("Login ");
// Solution 3: use regex with optional whitespace
screen.getByText(/^Login\s*$/);When text is broken by HTML elements, getByText looks at individual text nodes. Use a custom text matcher function or query by partial text.
// Problem: text split by <span>
<div>2<span>-</span>3</div>
// Fails: looks for "2-3" in a single text node
screen.getByText("2-3");
// Solution 1: query by partial text
screen.getByText("2");
screen.getByText("3");
// Solution 2: custom text matcher function
screen.getByText((content, element) => {
return element.textContent === "2-3";
});
// Solution 3: getByTextContent helper function
function getByTextContent(text) {
return screen.getByText((content, element) => {
return element.textContent === text;
});
}
const element = getByTextContent("2-3");If text content changes after initial render (loading states, API responses), use findByText instead of getByText. findByText returns a Promise and waits for matching text to appear.
// Using getByText (fails if text not immediately present)
// screen.getByText("Loading...");
// Using findByText (waits for text)
const loadingElement = await screen.findByText("Loading...");
// With custom timeout
const element = await screen.findByText("Data loaded", {}, { timeout: 5000 });
// For text that appears then disappears (like loading states)
await waitFor(() => {
expect(screen.queryByText("Loading...")).not.toBeInTheDocument();
});Regular expressions provide more flexible text matching. Use them for case-insensitive matches, partial matches, or patterns with dynamic content.
// Case insensitive matching
screen.getByText(/login/i);
// Partial text matching
screen.getByText(/Log/); // matches "Login", "Logout", "Logger"
// Match dynamic content (like counts)
screen.getByText(/Items: \d+/); // matches "Items: 5", "Items: 10"
// Match with optional whitespace
screen.getByText(/^\s*Login\s*$/);
// Complex pattern matching
screen.getByText(/User:\s*\w+\s*\(ID: \d+\)/);Use screen.debug() to see the exact DOM structure and text nodes. This helps identify whitespace issues, element splitting, or unexpected text content.
import { screen } from '@testing-library/react';
// Render your component
render(<MyComponent />);
// Print entire DOM to console
screen.debug();
// Print specific element
const element = screen.getByTestId('my-element');
screen.debug(element);
// Check textContent directly
console.log('Text content:', element.textContent);
console.log('Inner HTML:', element.innerHTML);
// Use logTestingPlaygroundURL for visual debugging
screen.logTestingPlaygroundURL();For complex text matching scenarios, create a helper function to normalize text content before comparison. This handles whitespace, case, and special characters consistently.
function normalizeText(text) {
return text
.trim()
.replace(/\s+/g, ' ') // collapse multiple spaces
.replace(/\n/g, ' ') // replace newlines with spaces
.toLowerCase(); // optional: for case-insensitive
}
// Use in custom matcher
screen.getByText((content, element) => {
const normalizedActual = normalizeText(element.textContent);
const normalizedExpected = normalizeText("Expected Text");
return normalizedActual === normalizedExpected;
});
// Or as a reusable query
function getByNormalizedText(expectedText) {
return screen.getByText((content, element) => {
return normalizeText(element.textContent) === normalizeText(expectedText);
});
}
const element = getByNormalizedText(" Login "); // handles extra spacesReact Testing Library's text matching uses the DOM textContent property, which includes text from all descendant nodes. However, it matches against individual text nodes when using string queries. The getByText function has three signatures: string (exact match), regex (pattern match), and function (custom matcher). For complex text scenarios, custom matcher functions provide the most control. Note that textContent includes hidden text (from display: none elements) but not text from aria-hidden="true" elements. When testing internationalized applications, consider text normalization for different languages and character sets.
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