npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ver0/react-hooks-testing

v1.0.3

Published

Test your react hooks with ease!

Readme

NPM Version NPM Downloads Dependents (via libraries.io), scoped npm package GitHub Actions Workflow Status Codecov NPM Type Definitions

Features

  • ✅ Supports React 19.
  • ✅ Server-Side Rendering (SSR) support.
  • ✅ Easy-to-use API.
  • ✅ Errors reporting, even for SSR.
  • ✅ Compatible with any testing framework.

Why this package exists?

In May 2022, @testing-library/react-hooks was announced as deprecated, leaving its users locked to React 17. The so-called "merge" with @testing-library/react was never fully realized, as the provided renderHook offers only a fraction of the functionality that @testing-library/react-hooks had, and it completely lacks SSR support.

I had hoped that the release of React 19 and server components would bring changes, but nothing has improved despite React 19 being in RC for almost a year before its release. When asking about SSR support, the response is often to resolve the issues independently.

This package was created to meet the needs of the hooks library I have been developing. I encourage everyone to contribute and help make hooks testing simple again. This package offers improved typings compared to @testing-library/react-hooks, is testing-framework agnostic, supports SSR, and is easy to use.

Dependencies and compatibility

This package aims to be compatible only with the current major version of React. Additionally, it encourages the use of newer React APIs, such as completely ditching synchronous rendering and support of synchronous act behavior, as React documentation states it will be deprecated in future releases.

How to use

Since the library is designed to be testing-framework agnostic, it does not have any automatic setup and therefore requires a tiny bit of pre-configuration to work with your testing framework. I'm using vitest and will provide examples for it, but all known frameworks have similar setup functionality.

As this library is tested through testing other hooks, following configuration is applied to this repository and can be used as an example.

Setup

After adding @ver0/react-hooks-testing to your dev-dependencies, create a file with the following content and add it to your test runner configuration as a global setup file.

import {hooksCleanup} from '@ver0/react-hooks-testing';
import {afterEach} from 'vitest';

afterEach(hooksCleanup);
import {defineConfig} from 'vitest/config';

export default defineConfig({
	test: {
		dir: './src',
		setupFiles: ['./src/tests/setup/react-hooks.test.ts'],
	},
});

Or, if you don't have many hooks to test, add it directly to your test files.

All this code does - unmounts all rendered hooks after each test, so you don't have to worry about it yourself.

Testing client-side hooks

renderHook function made close to @testing-library/react API-wise, but with several differences, that are dictated by act function.

The act function provided by this library is similar to the one from react, but with stricter typings. It only allows async functions to be passed to it, ensuring it always returns a promise, which should be awaited. This is in line with the React documentation, which states that synchronous act will be deprecated in future releases.

Additionally, this act function bypasses the console error regarding the testing environment configuration. Since you're using this library to test hooks, you're already in a testing environment, making this error redundant.

import {act, renderHook} from '@ver0/react-hooks-testing';
import {useState} from 'react';
import {expect, test} from 'vitest';
import {expectResultValue} from './test-helpers.test.js';

test('should use setState value', async () => {
	const {result} = await renderHook(() => useState('foo'));

	const value = expectResultValue(result);
	expect(value[0]).toBe('foo');

	await act(async () => {
		value[1]('bar');
	});

	expect(expectResultValue(result)[0]).toBe('bar');
});

As you can see in above example - renderHook function is asynchronous, reason for that is React's request to perform any rendering within the act function.

In contrast to @testing-library/react, result object has the following type definition:

/**
 * Represents the result of rendering a hook.
 */
export type ResultValue<T> =
	| {
			readonly value: undefined;
			readonly error: Error;
	  }
	| {
			readonly value: T;
			readonly error: undefined;
	  };

/**
 * Represents the last rendered hook result and all previous results.
 */
export type ResultValues<T> = ResultValue<T> & {
	readonly all: ResultValue<T>[];
};

The render results history is accessible via the all property, which contains every rendering result as well as the value of the latest render. Unlike @testing-library/react, which collects results during the effect phase, this library populates the results during the render phase - it allows to ensure tested hook results correctness throughout each render.

Another notable difference is the error property. This property captures any error thrown during the hook’s rendering, thanks to an Error Boundary component wrapped around the hook’s harness component.

Each result object is immutable and has either a value or an error property set. The hook's result object follows the same principle. Although the value and error properties are implemented as getters and always exist, the values they return correspond to the most recent render result from the all array.

Though it is possible for both values to become undefined simultaneously, when a hook returns undefined as a valid value, it is developer's responsibility to check error prior to using the value. The other theoretical possibility is undefined being thrown, but that is considered a bad practice and thus not handled specifically.

Otherwise, the API is similar to @testing-library/react. Note that the waitForNextUpdate function is not provided, as modern testing frameworks include their own waitFor function, which serves the same purpose.

Type-Safety Helper

While the discriminated union type provides strong type safety, manually checking for errors in every test can be annoyingly verbose. To ease usage while ensuring both type-safety and test coverage, you can introduce a helper function:

import type {ResultValue} from '@ver0/react-hooks-testing';
import {expect} from 'vitest';

/**
 * Helper to assert that a hook result is successful and extract its value in a type-safe way.
 */
export function expectResultValue<T>(result: ResultValue<T>) {
	expect(result.error).toBeUndefined();

	if (result.error) {
		throw new Error('result has unexpected error');
	}

	return result.value;
}

This helper both asserts that no error occurred and narrows the TypeScript type, allowing you to work with result.value directly, as demonstrated in the example above.

Testing server-side hooks

The primary purpose of this package is to provide out-of-the-box SSR (Server-Side Rendering) support through the renderHookServer function. This function works similarly to renderHook but includes a few key differences:

  • Initial Render: The initial render is performed using React's renderToString function.
  • Hydration: The hook's render result includes an extra hydrate function that hydrates the hook after the initial render.
  • DOM Root Creation: The client-side DOM root is created only when the hydrate function is called, facilitating testing in a server environment.
  • Rerenders: Rerenders are only possible after hydration.

Otherwise, usage is similar to renderHook, so dedicated examples are not provided. For usage examples, refer to the *.ssr.test.ts files in this repository.