react-shallow-renderer review
react-shallow-renderer 16.15.0 executes one React component and returns the React element that component produced without mounting its children or creating a DOM. Tests can inspect output props, rerender with new inputs, drive a returned callback, or reach a class instance. The release supports React 16, 17, and 18 through its peer range. It deliberately omits refs, effects, committed host behavior, and child integration, making it a compatibility dependency for older structural tests rather than a current default.
Our react-shallow-renderer 16.15.0 browser build measured 23.3 KB minified and 7.2 KB gzipped, yet it cannot mount children, refs, or effects and its peer range ends at React 18. Retain it for legacy shallow assertions; start new component tests with behavior-focused tooling.
We installed it
| Install | ✓ · 1.2s | 6 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | 7.2 KB | gzipped (23.3 KB minified), bundled with esbuild |
| Types | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does react-shallow-renderer install cleanly?
Yes. In a fresh container with an empty cache, npm install react-shallow-renderer finished in 1 seconds, leaving 6 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does react-shallow-renderer add to a browser bundle?
7.2 KB gzipped (23.3 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does react-shallow-renderer work with both ESM and CommonJS?
Yes. Both import 'react-shallow-renderer' and require('react-shallow-renderer') worked in Node 22 in our run. The package is published as CommonJS.
Does react-shallow-renderer include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
react-shallow-renderer or @testing-library/react: which should you use?
@testing-library/react: Use it for React web tests that query rendered DOM and exercise visible behavior. Our react-shallow-renderer 16.15.0 browser build measured 23.3 KB minified and 7.2 KB gzipped, yet it cannot mount children, refs, or effects and its peer range ends at React 18.
When should you not use react-shallow-renderer?
You use React 19: the published peer dependency stops at React 18 and the implementation reads React private internals
Use it if
- A React 16 through 18 suite already asserts the exact one-level element structure returned by components
- A pure component needs a fast DOM-free structural test and child execution would obscure the assertion
- Legacy class tests require `getMountedInstance()` during a gradual move away from instance-level testing
- A compatible React Native suite only inspects emitted child types and props, not native host behavior
- You use React 19: the published peer dependency stops at React 18 and the implementation reads React private internals
- The test should cover user behavior, context integration, accessibility, effects, layout, focus, or real event propagation
- The component relies on refs or effect hooks: the README says refs are unsupported and effects do not run in shallow output
- You expect `componentDidMount` or `componentDidUpdate`: shallow rendering does not commit a host tree for those lifecycles
- You want maintained testing guidance: the last package release was in 2022 and the README still recommends Enzyme's shallow API
Setup reality
We installed react-shallow-renderer 16.15.0 in our sandbox in 1.2 seconds. It left 6 packages and 1 MB on disk, and npm audit found 0 known vulnerabilities. The package is 156 KB unpacked, declares 2 direct dependencies and 1 peer dependency, and uses MIT. React itself must satisfy ^16.0.0 || ^17.0.0 || ^18.0.0; React 19 is outside the published contract.
The package entry is CommonJS and has no exports map. require() and ESM import worked under Node 22, but no TypeScript declarations were found. Our browser build measured 23.3 KB minified and 7.2 KB gzipped. The package supplies no runner, assertion library, JSX transform, or DOM. Keep it in devDependencies and let Jest, Vitest, Babel, or your existing test toolchain compile the component code.
Usage is synchronous: construct ShallowRenderer, render one custom component, then call getRenderOutput(). Passing a host element such as <div /> as the root is invalid. Child components remain unexecuted React elements. There are no semantic queries or event simulation helpers; calling an output prop invokes a function directly and does not reproduce browser propagation, default actions, focus changes, or accessibility behavior.
State hooks and class updates have partial models, while effect hooks are no-ops and refs are unsupported. componentWillUnmount can run for a class when unmount() is called, but mount and update commit lifecycles do not. The source depends on React's private internals and React 18-era element knowledge. Do not force the peer range beside React 19 merely because npm can be overridden.
Patterns
Inspect one function component render-component
import ShallowRenderer from 'react-shallow-renderer';
const renderer = new ShallowRenderer();
renderer.render(<Greeting name="Ada" />);
const output = renderer.getRenderOutput();
expect(output.type).toBe('h1');The root must be a custom component. Passing a host element such as `<h1>` directly throws.
Check props on the returned host element assert-props
renderer.render(<Button disabled label="Save" />);
const output = renderer.getRenderOutput();
expect(output.type).toBe('button');
expect(output.props.disabled).toBe(true);The output is a React element object; no button was created in a browser or native host tree.
Verify props passed to one child inspect-child
renderer.render(<Profile userId="u-42" />);
const child = renderer.getRenderOutput().props.children;
expect(child.type).toBe(Avatar);
expect(child.props.userId).toBe('u-42');Avatar is not executed, so this assertion cannot detect a failure inside that child.
Normalize several returned children inspect-children
const children = React.Children.toArray(
renderer.getRenderOutput().props.children
);
expect(children[0].type).toBe(SearchButton);The renderer has no find or semantic query API. Walk the returned element shape or use Testing Library.
Update the same component with new props rerender-props
renderer.render(<Status online={false} />);
expect(renderer.getRenderOutput().props.children).toBe('Offline');
renderer.render(<Status online />);
expect(renderer.getRenderOutput().props.children).toBe('Online');Reusing the same renderer and component type follows its update path; changing the root type resets state.
Call an emitted handler directly invoke-handler
const onSave = vi.fn();
renderer.render(<SaveButton onSave={onSave} />);
renderer.getRenderOutput().props.onClick();
expect(onSave).toHaveBeenCalledOnce();This direct call skips event propagation, default browser behavior, focus management, and accessibility checks.
Observe a useState transition update-hook-state
renderer.render(<Counter />);
renderer.getRenderOutput().props.onClick();
expect(renderer.getRenderOutput().props.children).toBe(1);State dispatch is modeled synchronously. `useEffect`, `useLayoutEffect`, and `useInsertionEffect` do not execute.
Call a method on a legacy class access-class-instance
renderer.render(<Counter />);
renderer.getMountedInstance().increment();
expect(renderer.getRenderOutput().props.children).toBe(1);`getMountedInstance()` returns null for function components. Keep this pattern only while migrating class-focused tests.
Check a conditional empty render assert-null
renderer.render(<Notice visible={false} />);
expect(renderer.getRenderOutput()).toBeNull();`getRenderOutput()` returns the component's value directly, including null, a string, or a React element.
Run class cleanup on unmount unmount-class
renderer.render(<Subscription id="news" />);
renderer.unmount();
expect(cleanup).toHaveBeenCalledOnce();A class `componentWillUnmount` can run, while mount and update commit lifecycles are intentionally skipped.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @testing-library/react | npm | Use it for React web tests that query rendered DOM and exercise visible behavior. |
| react-test-renderer | npm | Use it only when a host-independent rendered tree is required and its React version is supported. |
| enzyme | npm | Use it only to maintain an existing Enzyme suite whose adapter and React version are already fixed. |
More testing guides
pytest · chai · jsdom · vitest · playwright · coverage · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

