@@ -3,13 +3,147 @@ title: Migration guide to v2
3
3
description : How to update your code to use nuqs@2.0.0
4
4
---
5
5
6
- ## Support moved to ` next@>=14.1.2 `
6
+ Here's a summary of the breaking changes in ` nuqs@2.0.0 ` :
7
+
8
+ - [ Enable support for other React frameworks] ( #adapters )
9
+ - [ ESM-only package] ( #esm-only )
10
+ - [ Deprecated exports have been removed] ( #deprecated-exports )
11
+ - [ Renamed ` nuqs/parsers ` to ` nuqs/server ` ] ( #renamed-nuqs-parsers-to-nuqs-server )
12
+ - [ Debug printout detection] ( #debug-printout-detection )
13
+
14
+ ## Adapters
15
+
16
+ The biggest change is that ` nuqs@2.0.0 ` now supports other React frameworks,
17
+ providing type-safe URL state for all.
18
+
19
+ You will need to wrap your app with the appropriate ** adapter** for your framework,
20
+ to let the hooks know how to interact with its router.
21
+
22
+ Adapters are currently available for:
23
+ - Next.js (app & pages routers)
24
+ - React (Vite, Astro, etc.)
25
+ - Testing environments (Vitest, Jest, etc.)
26
+
27
+ More adapters will be released soon (React Router, Remix etc.), PRs are welcome!
28
+
29
+ ### Next.js
30
+
31
+ <Callout title = " Minimum required version: next@>=14.1.2" >
7
32
8
33
Early versions of Next.js 14 were in flux with regards to shallow routing,
9
34
only from 14.1.2 is it stable enough to not require ugly hacks.
10
35
11
36
See #423 for context and a table of supported versions.
12
37
38
+ </Callout >
39
+
40
+ #### App router
41
+
42
+ ``` tsx
43
+ // src/app/layout.tsx
44
+ import { NuqsAdapter } from ' nuqs/adapters/next/app'
45
+ import { type ReactNode } from ' react'
46
+
47
+ export default function RootLayout({
48
+ children
49
+ }: {
50
+ children: ReactNode
51
+ }) {
52
+ return (
53
+ <html >
54
+ <body >
55
+ <NuqsAdapter >{ children } </NuqsAdapter >
56
+ </body >
57
+ </html >
58
+ )
59
+ }
60
+ ```
61
+
62
+ #### Pages router
63
+
64
+ ``` tsx
65
+ // src/pages/_app.tsx
66
+ import type { AppProps } from ' next/app'
67
+ import { NuqsAdapter } from ' nuqs/adapters/next/pages'
68
+
69
+ export default function MyApp({ Component , pageProps }: AppProps ) {
70
+ return (
71
+ <NuqsAdapter >
72
+ <Component { ... pageProps } />
73
+ </NuqsAdapter >
74
+ )
75
+ }
76
+ ```
77
+
78
+ <details >
79
+ <summary >
80
+
81
+ #### Next.js (unified)
82
+
83
+ </summary >
84
+
85
+ If your Next.js app uses both the app _ and_ pages routers, you can use
86
+ the unified adapter if it needs to be mounted in either routers, at the cost
87
+ of a slightly larger bundle size (~ 100B).
88
+
89
+ ``` tsx
90
+ import { NuqsAdapter } from ' nuqs/adapters/next'
91
+ ```
92
+
93
+ </details >
94
+
95
+ ### React (with Vite)
96
+
97
+ ``` tsx
98
+ import { NuqsAdapter } from ' nuqs/adapters/react'
99
+
100
+ createRoot (document .getElementById (' root' )! ).render (
101
+ <NuqsAdapter >
102
+ <App />
103
+ </NuqsAdapter >
104
+ )
105
+ ```
106
+
107
+ ### Testing adapter
108
+
109
+ Until now, unit-testing nuqs components was a hassle, as it required mocking
110
+ Next.js internals, causing abstraction leaks.
111
+
112
+ You can now wrap your components to test with the ` NuqsTestingAdapter ` , which
113
+ provides setup & assertion helpers for your tests.
114
+
115
+ Here's an example with Vitest & Testing Library:
116
+
117
+ ``` tsx
118
+ import { render , screen } from ' @testing-library/react'
119
+ import userEvent from ' @testing-library/user-event'
120
+ import { NuqsTestingAdapter , type UrlUpdateEvent } from ' nuqs/adapters/testing'
121
+ import { describe , expect , it , vi } from ' vitest'
122
+ import { CounterButton } from ' ./counter-button'
123
+
124
+ it (' should increment the count when clicked' , async () => {
125
+ const user = userEvent .setup ()
126
+ const onUrlUpdate = vi .fn <[UrlUpdateEvent ]>()
127
+ render (<CounterButton />, {
128
+ // Setup the test by passing initial search params / querystring:
129
+ wrapper : ({ children }) => (
130
+ <NuqsTestingAdapter searchParams = " ?count=1" onUrlUpdate = { onUrlUpdate } >
131
+ { children }
132
+ </NuqsTestingAdapter >
133
+ )
134
+ })
135
+ // Act
136
+ const button = screen .getByRole (' button' )
137
+ await user .click (button )
138
+ // Assert changes in the state and in the (mocked) URL
139
+ expect (button ).toHaveTextContent (' count is 2' )
140
+ expect (onUrlUpdate ).toHaveBeenCalledOnce ()
141
+ expect (onUrlUpdate .mock .calls [0 ][0 ].queryString ).toBe (' ?count=2' )
142
+ expect (onUrlUpdate .mock .calls [0 ][0 ].searchParams .get (' count' )).toBe (' 2' )
143
+ expect (onUrlUpdate .mock .calls [0 ][0 ].options .history ).toBe (' push' )
144
+ })
145
+ ```
146
+
13
147
## ESM only
14
148
15
149
` nuqs@2.0.0 ` is now an [ ESM-only] ( https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c )
0 commit comments