<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lei Wang</title>
    <description>The latest articles on DEV Community by Lei Wang (@otiswanglei).</description>
    <link>https://dev.to/otiswanglei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4135726%2F99e7556d-56e8-448f-b643-ca2c9fe14ee9.png</url>
      <title>DEV Community: Lei Wang</title>
      <link>https://dev.to/otiswanglei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/otiswanglei"/>
    <language>en</language>
    <item>
      <title>Why I Built @standard-search-params/react Around Standard Schema</title>
      <dc:creator>Lei Wang</dc:creator>
      <pubDate>Mon, 21 Sep 2026 12:13:43 +0000</pubDate>
      <link>https://dev.to/otiswanglei/why-i-built-standard-search-paramsreact-around-standard-schema-39lc</link>
      <guid>https://dev.to/otiswanglei/why-i-built-standard-search-paramsreact-around-standard-schema-39lc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever written code like this to handle URL search params in React?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;q&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's simple, but writing this over and over as your app grows gets tedious. And the fallback behavior for an invalid value (like &lt;code&gt;page=abc&lt;/code&gt;) tends to differ depending on who wrote the code.&lt;/p&gt;

&lt;p&gt;A number of libraries already tackle this by combining a validation library like Zod with URL params. &lt;code&gt;@standard-search-params/react&lt;/code&gt; takes the same approach, but it's built around a spec called &lt;strong&gt;Standard Schema&lt;/strong&gt; instead of any single validation library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @standard-search-params/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is Standard Schema?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/standard-schema/standard-schema" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt; is a spec that lets different validation libraries — Zod, Valibot, ArkType, and others — expose a common interface. It's intentionally minimal: a validator just needs a &lt;code&gt;~standard&lt;/code&gt; property with a shared &lt;code&gt;validate()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;What makes this useful is that &lt;strong&gt;library authors no longer need to build separate integrations for Zod, Valibot, and so on&lt;/strong&gt;. Build one tool against Standard Schema, and it works the same way regardless of which validator your consumers happen to use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@standard-search-params/react&lt;/code&gt; is built so it works with any Standard Schema-compliant validator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useStandardSearchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@standard-search-params/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SearchResults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validatedSearchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isSearchParamsReady&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nf"&gt;useStandardSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSearchParamsReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;validatedSearchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather use Valibot, swap &lt;code&gt;z.coerce.number()&lt;/code&gt; for &lt;code&gt;v.pipe(v.string(), v.transform(Number))&lt;/code&gt; — the hook itself doesn't change at all. You can even mix Zod and Valibot in the same schema object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valibot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why per-field schemas instead of one big object schema?
&lt;/h2&gt;

&lt;p&gt;Most validation hooks for URL params validate the whole query string against a single object schema. That approach has a weakness: &lt;strong&gt;one invalid field can invalidate everything.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;?page=2&amp;amp;q=hello&amp;amp;sort=bad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;sort&lt;/code&gt; holds an unexpected value here and you're validating the whole object as one schema, a perfectly valid &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; can get thrown out along with it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@standard-search-params/react&lt;/code&gt; avoids this by taking a &lt;strong&gt;plain object mapping each param name to its own schema&lt;/strong&gt;, validated independently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;          &lt;span class="c1"&gt;// { page: '2', q: 'hello', sort: 'bad' }  (raw strings)&lt;/span&gt;
&lt;span class="nx"&gt;validatedSearchParams&lt;/span&gt; &lt;span class="c1"&gt;// { page: 2, q: 'hello' }                  (parsed, sort dropped)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;code&gt;sort&lt;/code&gt; gets excluded from &lt;code&gt;validatedSearchParams&lt;/code&gt;; &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; come through fine. One broken param not taking down the rest matters more than it sounds once you're dealing with stale bookmarks or hand-edited URLs in a real product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-side only, on purpose
&lt;/h2&gt;

&lt;p&gt;This hook reads &lt;code&gt;window.location.search&lt;/code&gt;, so it &lt;strong&gt;never runs during server rendering&lt;/strong&gt;. That's an intentional design choice, not an oversight.&lt;/p&gt;

&lt;p&gt;It's safe to render inside an SSR framework (no crash, no hydration mismatch): &lt;code&gt;isSearchParamsReady&lt;/code&gt; stays &lt;code&gt;false&lt;/code&gt; through the server render and the first client render, then flips to &lt;code&gt;true&lt;/code&gt; once the client has read and validated the URL. There's necessarily a brief "not loaded yet" moment before that, though — that part can't be avoided.&lt;/p&gt;

&lt;p&gt;If you need validated search params as part of the initial server-rendered output (e.g. a Next.js Server Component, which receives &lt;code&gt;searchParams&lt;/code&gt; as a plain prop), skip the hook and validate that object directly with your schema instead. This hook is meant for &lt;strong&gt;client-rendered apps (SPAs) or client components&lt;/strong&gt; that read params after mount.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling browser back/forward
&lt;/h2&gt;

&lt;p&gt;v0.2.0 added two ways to react to the URL changing after mount.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;browser back/forward buttons&lt;/strong&gt;, pass &lt;code&gt;listenToPopstate&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validatedSearchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStandardSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;listenToPopstate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's &lt;code&gt;false&lt;/code&gt; by default. That's intentional — the hook's core design is "read the URL once, on mount," and I didn't want to change that behavior silently for existing users. Automatic re-validation is something you opt into, not something that happens by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SPA route pushes&lt;/strong&gt; (&lt;code&gt;router.push()&lt;/code&gt;, &lt;code&gt;navigate()&lt;/code&gt;) don't fire &lt;code&gt;popstate&lt;/code&gt;, so there's a separate &lt;code&gt;refresh()&lt;/code&gt; function for that case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Next.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validatedSearchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStandardSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A small safeguard for development
&lt;/h2&gt;

&lt;p&gt;This hook reads the URL &lt;strong&gt;once, on mount.&lt;/strong&gt; That means an inline schema object (a fresh reference every render) is totally fine — only the keys present on the very first render are ever read. But it also means there's a footgun if &lt;strong&gt;the set of schema keys itself&lt;/strong&gt; needs to change at runtime — for example, validating extra params only for admin users.&lt;/p&gt;

&lt;p&gt;To catch this early, there's a dev-only &lt;code&gt;console.warn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useStandardSearchParams: schema keys changed after mount
(was [page], now [page,q]), but this hook only reads the URL once,
on mount, so the new keys won't be read. If the set of keys genuinely
needs to change at runtime, remount the component (e.g. with a `key`
prop) — memoizing the schema object does not cause a re-parse.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the message says, the fix is a &lt;code&gt;key&lt;/code&gt; prop to force a remount — not &lt;code&gt;useMemo&lt;/code&gt;. Memoizing the schema stabilizes its reference, but it does nothing to trigger a re-parse, so the warning spells out the actual fix rather than pointing toward an intuitive-but-wrong one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@standard-search-params/react&lt;/code&gt; is a small library built around a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard Schema support, so it works with Zod, Valibot, ArkType, or whatever you're already using&lt;/li&gt;
&lt;li&gt;Per-field validation, so one broken param doesn't take the rest down with it&lt;/li&gt;
&lt;li&gt;Client-side only, clearly documented rather than papered over&lt;/li&gt;
&lt;li&gt;Browser-back and SPA-router support, both opt-in on top of a safe default
I'd rather keep the feature set small and the behavior predictable than pile on options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback and issues are always welcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@standard-search-params/react" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@standard-search-params/react&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/otiswanglei/standard-search-params" rel="noopener noreferrer"&gt;https://github.com/otiswanglei/standard-search-params&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
