How to fix Container type is not a DOM element, string provided instead in React

ReactINTERMEDIATEMEDIUMUpdated Dec 11, 2025· 122 views

This error occurs in React Testing Library when you pass a string instead of a DOM element as the container argument to query functions. It happens when using incorrect syntax with screen queries, within() helper, or container-based queries. The fix involves understanding the different query patterns and ensuring you pass actual DOM elements, not CSS selector strings.

Quick fix

Fixes Container type is not a DOM element, string provided instead

// ✅ Correct - screen queries are already bound to document.body
const element = screen.getByText('Submit');
const button = screen.getByRole('button', { name: 'Save' });

// ❌ Incorrect - don't pass container to screen queries
const element = screen.getByText(document.body, 'Submit');
const button = screen.getByRole('#app', 'button', { name: 'Save' });
  1. Verify you're using screen for document.body queries
  2. Pass DOM elements, not strings, to container-based queries
  3. Use within() correctly with DOM elements
  4. Check your render() method usage
react — error
// ✅ Correct - screen queries are already bound to document.body
const element = screen.getByText('Submit');
const button = screen.getByRole('button', { name: 'Save' });
// ❌ Incorrect - don't pass container to screen queries
const element = screen.getByText(document.body, 'Submit');
const button = screen.getByRole('#app', 'button', { name: 'Save' });
Container type is not a DOM element, string provided instead

What this error means

The "Container type is not a DOM element, string provided instead" error is thrown by React Testing Library when you pass a string (like a CSS selector) where a DOM element is expected. Testing Library's query functions are designed to work with actual DOM nodes, not selector strings, to encourage testing from the user's perspective. This error typically occurs in three scenarios: 1. **Screen queries with container argument**: Trying to pass a container to screen.* queries (which are already bound to document.body) 2. **Container-based queries with strings**: Passing a CSS selector string instead of a DOM element to getByText(container, ...) style queries 3. **within() helper with strings**: Using within() with a string selector instead of a DOM element The philosophy behind Testing Library is to test components as users interact with them - users don't see CSS selectors, they see text, labels, and roles. By requiring DOM elements, the library encourages semantic, accessible queries.

Typical symptoms

  • Tests fail with the error message "Container type is not a DOM element, string provided instead"
  • Queries return null or throw errors even when elements should be present
  • Using screen.getBy... or container.getBy... with a string selector instead of a DOM element
  • Confusion between Testing Library queries and native DOM methods like querySelector
  • Error occurs when migrating from enzyme or other testing libraries that accept selector strings
  • Tests fail after refactoring query syntax

Common causes

  1. Passing a CSS selector string (e.g., '#app') directly to a Testing Library query instead of a DOM element
  2. Using the wrong syntax: getByText(container, 'text') vs getByText('text') when container is already bound
  3. Mistaking screen queries for container-bound queries that require a DOM element first argument
  4. Attempting to use within() with a string instead of a DOM element
  5. Incorrectly migrating from enzyme or other testing libraries that accept selector strings
  6. Confusing Testing Library's getBy* with document.querySelector or similar DOM methods
  7. Passing document.querySelector('#app') result without checking if it's null

How to fix "Container type is not a DOM element, string provided instead"

1Verify you're using screen for document.body queries

When querying the entire document, use the screen object which is pre-bound to document.body. Don't pass a container argument:

javascript
// ✅ Correct - screen queries are already bound to document.body
const element = screen.getByText('Submit');
const button = screen.getByRole('button', { name: 'Save' });

// ❌ Incorrect - don't pass container to screen queries
const element = screen.getByText(document.body, 'Submit');
const button = screen.getByRole('#app', 'button', { name: 'Save' });

The screen object is imported from @testing-library/react and provides all queries pre-bound to document.body.

2Pass DOM elements, not strings, to container-based queries

If you need to query within a specific container, first get the DOM element, then pass it to the query:

javascript
import { getByText } from '@testing-library/dom';

// ✅ Correct - pass DOM element
const container = document.querySelector('#app');
if (container) {
  const element = getByText(container, 'Submit');
}

// ❌ Incorrect - passing string selector
const element = getByText('#app', 'Submit'); // Error: string instead of DOM element

// ✅ Also correct with render() return value
const { container } = render(<MyComponent />);
const element = getByText(container, 'Submit');

Always ensure you have a valid DOM element before passing it to container-based queries.

3Use within() correctly with DOM elements

The within() helper requires a DOM element, not a string:

javascript
import { within } from '@testing-library/dom';

// ✅ Correct - pass DOM element to within()
const container = document.querySelector('#app');
const { getByText } = within(container);
const element = getByText('Submit');

// ✅ Also correct with render() result
const { getByText } = within(container); // container from render()

// ❌ Incorrect - passing string to within()
const { getByText } = within('#app'); // Error: string passed

// ✅ Using within with screen for scoped queries
const modal = screen.getByRole('dialog');
const { getByText: getByTextInModal } = within(modal);
const button = getByTextInModal('Close');

within() creates a scoped query object bound to a specific container.

4Check your render() method usage

When using render() from React Testing Library, it returns an object with container and other utilities. Use them properly:

javascript
import { render } from '@testing-library/react';

// ✅ Correct usage
const { container, getByText } = render(<MyComponent />);

// Option 1: Use bound queries (container is already bound)
const element = getByText('Submit');

// Option 2: Use container for scoped queries
const innerElement = getByText(container, 'Submit');

// Option 3: Use screen for document queries
const screenElement = screen.getByText('Global element');

// ❌ Common mistake - passing container string
const wrong = getByText('#root', 'Submit'); // Error

// ✅ Proper pattern with multiple containers
const { container: container1 } = render(<Component1 />);
const { container: container2 } = render(<Component2 />);
const element1 = getByText(container1, 'Text 1');
const element2 = getByText(container2, 'Text 2');
5Understand the difference between query types

Testing Library provides three types of queries with different patterns:

1. **screen.* queries** - Bound to document.body, no container needed

javascript
import { screen } from '@testing-library/react';
   const element = screen.getByText('Text');

2. **getBy*(container, ...) queries** - Require container as first argument

javascript
import { getByText } from '@testing-library/dom';
   const element = getByText(container, 'Text');

3. **within(container).* queries** - Create scoped query object

javascript
import { within } from '@testing-library/dom';
   const { getByText } = within(container);
   const element = getByText('Text');

Key differences:
- screen.*: No container argument, searches entire document
- getBy*(container, ...): First argument must be DOM element
- within(container).*: Creates new query object bound to container

Always ensure you're using the right pattern for your use case.

6Debug and fix common patterns

Here are common problematic patterns and their fixes:

javascript
// ❌ Problem: String selector with getByText
const element = getByText('#app', 'Submit');

// ✅ Fix: Get DOM element first
const container = document.querySelector('#app');
const element = getByText(container, 'Submit');

// ❌ Problem: Passing container to screen query
const element = screen.getByText(container, 'Submit');

// ✅ Fix: Use either screen or container-based query
const element = screen.getByText('Submit'); // OR
const element = getByText(container, 'Submit');

// ❌ Problem: within() with string
const { getByText } = within('#modal');

// ✅ Fix: Get DOM element first
const modal = document.querySelector('#modal');
const { getByText } = within(modal);

// ❌ Problem: Null container from querySelector
const container = document.querySelector('.not-exist');
const element = getByText(container, 'Text'); // container is null!

// ✅ Fix: Check for null
const container = document.querySelector('.not-exist');
if (container) {
  const element = getByText(container, 'Text');
}

Advanced notes

### Testing Library Philosophy

This error stems from Testing Library's core philosophy: test from the user's perspective. Users don't interact with CSS selectors, class names, or IDs - they interact with text, labels, roles, and visible UI elements. By requiring DOM elements instead of selector strings, Testing Library encourages:

1. Semantic queries: Find elements by their accessible names (getByRole, getByLabelText)
2. Accessibility-focused testing: Ensure your UI is accessible to all users
3. Implementation-agnostic tests: Tests don't break when CSS classes change
4. User-centric verification: Verify what users actually see and interact with

### When to Use Different Query Patterns

**Use screen.* when:**
- Testing the entire rendered output
- Elements are rendered directly to document.body
- You don't need to scope queries to a specific container

Use container-based queries when:
- You need to query within a specific DOM element
- Testing portal content or nested components
- Working with multiple independent render calls

Use within() when:
- You need multiple queries within the same container
- Creating reusable test utilities for specific components
- Improving test readability with scoped queries

### Migration from Other Libraries

If migrating from enzyme or libraries that use selector strings:

1. Replace wrapper.find('.selector') with screen.getByText('Text') or getByRole
2. Replace container.querySelector('.class') with getByText(container, 'Text')
3. Use getByTestId for stable element references when needed
4. Consider whether you're testing implementation details vs user behavior

### Performance Considerations

- screen queries: Search entire document, can be slower for large pages
- container queries: Limited to specific element, faster for scoped searches
- within(): Creates new query object, useful for repeated queries on same container

### Common Related Errors

- "Unable to find an element by: [text]" - Element doesn't exist in the container
- "Found multiple elements with the text" - Use more specific queries
- "TestingLibraryElementError" - General query error, check query syntax

Frequently asked questions

What does "Container type is not a DOM element, string provided instead" mean?
The "Container type is not a DOM element, string provided instead" error is thrown by React Testing Library when you pass a string (like a CSS selector) where a DOM element is expected. Testing Library's query functions are designed to work with actual DOM nodes, not selector strings, to encourage testing from the user's perspective. This error typically occurs in three scenarios: 1.
How do I fix "Container type is not a DOM element, string provided instead"?
This error occurs in React Testing Library when you pass a string instead of a DOM element as the container argument to query functions. It happens when using incorrect syntax with screen queries, within() helper, or container-based queries. The fix involves understanding the different query patterns and ensuring you pass actual DOM elements, not CSS selector strings.
What causes "Container type is not a DOM element, string provided instead"?
Passing a CSS selector string (e.g., '#app') directly to a Testing Library query instead of a DOM element Using the wrong syntax: getByText(container, 'text') vs getByText('text') when container is already bound Mistaking screen queries for container-bound queries that require a DOM element first argument
Is the "Container type is not a DOM element, string provided instead" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading