{
  "$type": "site.standard.document",
  "content": {
    "$type": "at.markpub.markdown",
    "flavor": "gfm",
    "text": {
      "markdown": "If you're building a search page with Next.js and React Hook Form, you may want\nto sync the form values with the URL query parameters. This way, users can share\nthe search results with others or bookmark the page.\n\nThis hook will sync any form value changes with the URL query parameters and\nallow you to use the URL query parameters as your source of truth for fetching data.\n\nIt will also sync any changes to the URL query parameters with the form values.\nFor example, the back and forward browser buttons will work as expected.\n\n## Dependencies\n\n- [React](https://reactjs.org)\n- [React Hook Form](https://react-hook-form.com)\n- [Next.js](https://nextjs.org)\n- [lodash/isEqual](https://lodash.com/docs/4.17.15#isEqual) - or any other deep\n  equality check function\n- [qs](https://www.npmjs.com/package/qs)\n\n## The Hook\n\n```js\nimport isEqual from 'lodash/isEqual'\nimport { useRouter } from 'next/router'\nimport qs from 'qs'\nimport { useEffect } from 'react'\nimport { useForm } from 'react-hook-form'\n\nexport const useSearchForm = () => {\n  const { query, push, beforePopState } = useRouter()\n  const methods = useForm({\n    defaultValues: query,\n  })\n  const { watch, reset } = methods\n  const values = watch()\n\n  useEffect(() => {\n    // this is useful if you have any parameters, e.g. pagination that are\n    // controlled via links and not a search form\n    const ignoreParams = ['skip']\n\n    const newQuery = Object.fromEntries(\n      Object.entries(values)\n        // remove any ignored params\n        .filter(([key]) => !ignoreParams.includes(key))\n        // remove any empty values from the query as they're\n        // not needed in the URL\n        .filter(([, value]) =>\n          Array.isArray(value) ? value.length > 0 : value\n        )\n        // when an array value only has one item, it won't\n        // be an array in the router query, so we need to\n        // convert it back to a single value so it can be\n        // compared correctly\n        .map(([key, value]) => {\n          if (\n            !Array.isArray(query[key]) &&\n            Array.isArray(value) &&\n            value.length === 1\n          ) {\n            return [key, value.at(0)]\n          }\n\n          return [key, value]\n        })\n    )\n\n    // if query without ignored params is equal to newQuery,\n    // then we don't need to push the new query to the history\n    const queryWithoutIgnoredParams = Object.fromEntries(\n      Object.entries(query).filter(([key]) => !ignoreParams.includes(key))\n    )\n    if (!isEqual(queryWithoutIgnoredParams, newQuery)) {\n      void push({ query: newQuery }, undefined, {\n        shallow: true,\n      })\n    }\n  }, [values, query, push])\n\n  useEffect(() => {\n    // back button handling, to avoid getting into a loop where\n    // react-hook-form and the url are not perfectly in sync,\n    // we reset the form to the previous state. When the url\n    // is updated, the form already reflects the \"new\" state.\n    beforePopState(({ url }) => {\n      const base = document.location.protocol + '//' + document.location.host\n      const query = new URL(base + url).searchParams.toString()\n      reset(qs.parse(query))\n      return true\n    })\n  }, [beforePopState, reset])\n\n  return [query, methods]\n}\n```"
    }
  },
  "description": "A quick React hook for syncing React Hook Form with Next.js URL query parameters.",
  "path": "/blog/nextjs-url-sync-with-react-hook-form",
  "publishedAt": "2023-02-25T23:07:00.000Z",
  "site": "at://did:plc:up24cb5cw2fhaijo2sztkw3a/site.standard.publication/3mpeiqsvemc6y",
  "tags": [
    "javascript",
    "react",
    "forms"
  ],
  "textContent": "If you're building a search page with Next.js and React Hook Form, you may want\nto sync the form values with the URL query parameters. This way, users can share\nthe search results with others or bookmark the page.\n\nThis hook will sync any form value changes with the URL query parameters and\nallow you to use the URL query parameters as your source of truth for fetching data.\n\nIt will also sync any changes to the URL query parameters with the form values.\nFor example, the back and forward browser buttons will work as expected.\n\nDependencies\n\nReact\nReact Hook Form\nNext.js\nlodash/isEqual - or any other deep\n  equality check function\nqs\n\nThe Hook",
  "title": "React Hook Form & Next.js URL Sync",
  "updatedAt": "2026-06-28T21:54:11.291Z"
}