Announcing nuqs version 2

nuqs 2

Opening up to other React frameworks

François Best

@fortysevenfx

Tuesday 22 October 2024


nuqs@2.0.0 is available, install it now:

pnpm add nuqs@latest

It’s packing a lot of exciting features & improvements, including:


Hello, React! 👋 ⚛️

nuqs started as a Next.js-only hook, and v2 brings compatibility for other React frameworks.

No code change is necessary in components that use nuqs hooks, making them universal across all supported frameworks.

The only new requirement is to wrap your React tree with an Adapter for your framework.

Testing

One of the major pain points with nuqs v1 was testing components that used its hooks.

Nuqs V2 comes with a built-in testing adapter that mocks URL behaviours, allowing you to test your components in isolation, outside of any framework runtime.

You can use it with any unit testing framework that renders React components (we recommend Vitest & Testing Library).

counter-button.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { NuqsTestingAdapter, type UrlUpdateEvent } from 'nuqs/adapters/testing'
import { describe, expect, it, vi } from 'vitest'
import { CounterButton } from './counter-button'
 
it('should increment the count when clicked', async () => {
  const user = userEvent.setup()
  const onUrlUpdate = vi.fn<[UrlUpdateEvent]>()
  render(<CounterButton />, {
    // 1. Setup the test by passing initial search params / querystring:
    wrapper: ({ children }) => (
      <NuqsTestingAdapter searchParams="?count=42" onUrlUpdate={onUrlUpdate}>
        {children}
      </NuqsTestingAdapter>
    )
  })
  // 2. Act
  const button = screen.getByRole('button')
  await user.click(button)
  // 3. Assert changes in the state and in the (mocked) URL
  expect(button).toHaveTextContent('count is 43')
  expect(onUrlUpdate).toHaveBeenCalledOnce()
  expect(onUrlUpdate.mock.calls[0][0].queryString).toBe('?count=43')
  expect(onUrlUpdate.mock.calls[0][0].searchParams.get('count')).toBe('43')
  expect(onUrlUpdate.mock.calls[0][0].options.history).toBe('push')
})

The adapter conforms to the setup / act / assert testing strategy, allowing you to set the initial URL, then letting your test framework perform actions on your component, and asserting on how the URL was changed as a result.

Breaking changes & migration

The biggest breaking change is the introduction of adapters. Another one is related to deprecated APIs.

The next-usequerystate package that started this journey is no longer updated. All updates are now published under the nuqs package name.

Read the complete migration guide to update your application.

What’s next?

We have a lot of ideas for the future of nuqs, including:

Thanks

A huge thank you to sponsors, contributors and people who raised issues and discussions on GitHub and X/Twitter, you are the growing community that drives this project forward, and I couldn’t be happier with the response.

Sponsors

Contributors

Huge thanks to @andreisocaciu, @tordans, @prasannamestha, @Talent30, @neefrehman, @chbg, @dopry, @weisisheng, @hugotiger, @iuriizaporozhets, @rikbrown, @mateogianolio, @timheerwagen, @psdmsft, and @psdewar for helping!