Skip to main content
Since nuqs 2, you can unit-test components that use useQueryState(s){:ts} hooks without needing to mock anything, by using a dedicated testing adapter that will facilitate setting up your tests (with initial search params) and asserting on URL changes when acting on your components.

NuqsTestingAdapter

The NuqsTestingAdapter{:ts} component provides a test environment for components using nuqs hooks. It simulates the URL state management without requiring a real router.

Basic setup

Wrap your component under test with the NuqsTestingAdapter{:ts}:

Using withNuqsTestingAdapter

For testing libraries that support wrapper functions, use withNuqsTestingAdapter{:ts}:

Testing with Vitest

Here is a complete example using Vitest and Testing Library:
counter-button.test.tsx

Testing with Jest

Since nuqs 2 is an ESM-only package, there are a few hoops you need to jump through to make it work with Jest. This is extracted from the Jest ESM guide.
  1. Add the following options to your jest.config.ts file:
jest.config.ts
  1. Change your test command to include the --experimental-vm-modules flag:
package.json
Adapt accordingly for Windows with cross-env.

API Reference

searchParams

The initial search params to use for the test. These can be a query string, a URLSearchParams object or a record object with string values.

onUrlUpdate

A function that will be called when the URL is updated by the component. It receives an object with:
  • searchParams{:ts}: the new search params as an instance of URLSearchParams{:ts}
  • queryString{:ts}: the new rendered query string (for convenience)
  • options{:ts}: the options used to update the URL

hasMemory

By default, the testing adapter is immutable, meaning it will always use the initial search params as a base for URL updates. This encourages testing units of behaviour in a single test. To make it behave like framework adapters (which do store the updates in the URL), set hasMemory: true{:ts}, so subsequent updates build up on the previous state:
This memory is per-adapter instance, and so is isolated between tests, but shared for components under the same adapter.

Advanced options

  • rateLimitFactor{:ts}: By default, rate limiting is disabled when testing, as it can lead to unexpected behaviours. Setting this to 1 will enable rate limiting with the same factor as in production.
  • resetUrlUpdateQueueOnMount{:ts}: clear the URL update queue before running the test. This is true{:ts} by default to isolate tests, but you can set it to false{:ts} to keep the URL update queue between renders and match the production behaviour more closely.
  • autoResetQueueOnUpdate{:ts}: automatically reset the update queue after each URL update. Defaults to true{:ts}.

Testing custom parsers

If you create custom parsers with createParser{:ts}, you will likely want to test them. Parsers should:
  1. Define pure functions for parse{:ts}, serialize{:ts}, and eq{:ts}.
  2. Be bijective: parse(serialize(x)) === x{:ts} and serialize(parse(x)) === x{:ts}.
To help test bijectivity, you can use helpers defined in nuqs/testing:

Testing parser functions

The helper functions perform the following checks: isParserBijective(parser, serialized, input){:ts} Tests that a parser is bijective by:
  • Serializing the input and comparing to expected serialized value
  • Parsing the serialized value and comparing to expected input value
  • Using the parser’s eq{:ts} function (if provided) for value comparison
testSerializeThenParse(parser, input){:ts} Tests one direction: serialize the input, then parse it back and verify it matches the original input. testParseThenSerialize(parser, serialized){:ts} Tests the other direction: parse the serialized string, then serialize it back and verify it matches the original string.

Example: Testing a custom hex parser

hex-color.test.ts
See issue #259 for more testing-related discussions.