npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rhf-sync-url

v1.0.4

Published

Sync React Hook Form state with URL Query Params automatically.

Readme

rhf-sync-url

A lightweight React hook that automatically synchronizes React Hook Form state with URL query parameters. Perfect for creating shareable form states, maintaining form state on page refresh, and building better UX with persistent form filters.

Features

  • Automatic Synchronization: Form values are automatically synced with URL query parameters
  • Bidirectional Sync: URL → Form on mount and URL changes (browser navigation), Form → URL on form changes
  • Debounced Updates: Configurable debounce to prevent excessive URL updates
  • Framework Agnostic: Works with React Router, Next.js, and any router that provides URL search params
  • TypeScript Support: Fully typed with TypeScript generics
  • Lightweight: Minimal dependencies, only requires React and React Hook Form
  • Flexible: Supports complex values (objects, arrays) via base64-encoded JSON serialization
  • Secure: Prototype pollution protection and safe JSON parsing
  • URL Length Protection: Configurable maximum URL length with warnings

⚠️ Security Warning

This package is designed for non-sensitive form data only.

DO NOT use this package for forms containing:

  • Passwords or authentication credentials
  • Personal Identifiable Information (PII) - SSN, credit card numbers, etc.
  • Financial information
  • Medical records
  • API keys or tokens
  • Any sensitive data subject to privacy regulations

URLs are visible in:

  • Browser history
  • Server logs
  • Referrer headers (when navigating to external sites)
  • Browser address bar
  • Shared links
  • Browser extensions
  • Network monitoring tools

Always use the excludeFields option to prevent sensitive fields from being synced to URLs.

Installation

npm install rhf-sync-url
yarn add rhf-sync-url
pnpm add rhf-sync-url

Requirements

  • React >= 16.8.0
  • React Hook Form >= 7.0.0

Usage

Usage with React Router

import { useSearchParams } from "react-router-dom";
import { useForm } from "react-hook-form";
import { useSyncUrl } from "rhf-sync-url";

export const MyForm = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const { control, reset } = useForm();

  useSyncUrl({
    control,
    reset,
    adapter: {
      searchParams,
      setSearchParams,
    },
  });

  return <form>...</form>;
};

Usage with Next.js (App Router)

import { useSearchParams, useRouter, usePathname } from "next/navigation";
import { useForm } from "react-hook-form";
import { useSyncUrl } from "rhf-sync-url";

export const MyForm = () => {
  const searchParams = useSearchParams();
  const router = useRouter();
  const pathname = usePathname();
  const { control, reset } = useForm();

  useSyncUrl({
    control,
    reset,
    adapter: {
      searchParams,
      setSearchParams: (newParams) => {
        router.replace(`${pathname}?${newParams.toString()}`);
      },
    },
  });

  return <form>...</form>;
};

Usage with Next.js (Pages Router)

import { useRouter } from 'next/router';
import { useForm } from 'react-hook-form';
import { useSyncUrl } from 'rhf-sync-url';

export const MyForm = () => {
  const router = useRouter();
  const { control, reset } = useForm();

  useSyncUrl({
    control,
    reset,
    adapter: {
      searchParams: new URLSearchParams(router.query as Record<string, string>),
      setSearchParams: (newParams) => {
        router.replace({
          pathname: router.pathname,
          query: Object.fromEntries(newParams),
        }, undefined, { shallow: true });
      },
    }
  });

  return <form>...</form>;
};

Custom Options

You can customize the debounce delay, maximum URL length, and exclude sensitive fields:

useSyncUrl({
  control,
  reset,
  adapter: {
    searchParams,
    setSearchParams,
  },
  debounce: 1000, // Wait 1 second before updating URL (default: 500ms)
  maxUrlLength: 1500, // Maximum URL length before warning (default: 2000)
  excludeFields: ["password", "ssn", "creditCard"], // Fields to never sync to URL
});

Excluding Sensitive Fields

Always exclude sensitive fields to prevent them from appearing in URLs:

useSyncUrl({
  control,
  reset,
  adapter: {
    searchParams,
    setSearchParams,
  },
  excludeFields: [
    "password",
    "confirmPassword",
    "ssn",
    "creditCard",
    "apiKey",
    "token",
  ],
});

The hook will:

  • Never sync excluded fields to the URL
  • Remove excluded fields from the URL if they exist
  • Skip excluded fields when restoring form values from URL
  • Warn in development if field names suggest sensitive data (even if not explicitly excluded)

API Reference

useSyncUrl<T>(options)

A React hook that synchronizes React Hook Form state with URL query parameters bidirectionally.

Type Parameters

  • T (optional): The type of your form data. Defaults to Record<string, unknown>

Parameters

  • options (UseSyncUrlOptions<T>)
    • control (Control<T>): The control object from useForm<T>() hook
    • reset (UseFormReset<T>): The reset function from useForm<T>() hook
    • adapter (RouterAdapter): An object containing:
      • searchParams (URLSearchParams): Current URL search parameters
      • setSearchParams ((params: URLSearchParams) => void): Function to update URL search parameters
    • debounce (number, optional): Debounce delay in milliseconds (default: 500)
    • maxUrlLength (number, optional): Maximum URL length before warning (default: 2000)
    • excludeFields (string[], optional): Field names to exclude from URL sync (default: [])
      • Important: Always exclude sensitive fields like passwords, SSN, credit cards, etc.

Returns

void - This hook doesn't return a value. It synchronizes form state with URL automatically.

Behavior

  1. On Mount: Reads query parameters from the URL and restores form values (excluding excludeFields)
  2. On URL Changes: When URL changes externally (browser back/forward, manual navigation), form values are updated (excluding excludeFields)
  3. On Form Changes: Updates the URL query parameters with current form values (debounced, excluding excludeFields)
  4. Empty Values: Automatically removes query parameters when form values are empty, null, or undefined
  5. Complex Values: Objects and arrays are serialized as base64-encoded JSON in the URL (primitives remain as plain strings)
  6. Security:
    • Prototype pollution protection and safe JSON parsing are built-in
    • Excluded fields are never synced to URL and are removed if they exist
    • Development warnings for potentially sensitive field names

How It Works

Initial Hydration (URL → Form)

On the first render, the hook:

  1. Reads all query parameters from the URL
  2. Attempts to decode base64-encoded JSON values (for objects/arrays) or uses plain strings (for primitives)
  3. Falls back to plain JSON parsing for backward compatibility
  4. Sanitizes parsed objects to prevent prototype pollution
  5. Resets the form with the restored values

Bidirectional Sync

URL → Form: When the URL changes (browser navigation, back/forward button, or external updates), the hook detects the change and updates the form values accordingly.

Form → URL: When form values change (after initial hydration), the hook:

  1. Debounces the updates to prevent excessive URL changes
  2. Serializes values (base64-encoded JSON for objects/arrays, plain strings for primitives)
  3. Updates the URL query parameters
  4. Preserves unrelated URL parameters
  5. Warns if URL length exceeds the maximum

Value Serialization

  • Primitives (string, number, boolean): Converted to plain strings (no encoding)
  • Objects and Arrays: JSON stringified, then base64-encoded for cleaner URLs
  • Empty Values (null, undefined, empty string): Removed from URL
  • Special Objects (Date, RegExp, etc.): Converted to strings (not JSON)
  • Backward Compatibility: The hook can decode both base64-encoded JSON (new format) and plain JSON strings (legacy format)

Security Features

  • Field Exclusion: Use excludeFields to prevent sensitive data from being synced to URLs
  • Prototype Pollution Protection: Dangerous keys (__proto__, constructor, prototype) are automatically stripped from parsed objects
  • Safe JSON Parsing: Validates parsed values and rejects non-plain objects
  • Development Warnings: Warns in development mode if field names suggest sensitive data (e.g., "password", "ssn", "token")
  • Error Handling: Gracefully handles circular references and serialization errors

Examples

Search Form with Filters

import { useSearchParams } from "react-router-dom";
import { useForm } from "react-hook-form";
import { useSyncUrl } from "rhf-sync-url";

export const SearchForm = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const { control, register, handleSubmit } = useForm({
    defaultValues: {
      query: "",
      category: "all",
      tags: [],
    },
  });

  const { control, reset } = useForm({
    defaultValues: {
      query: "",
      category: "all",
      tags: [],
    },
  });

  useSyncUrl({
    control,
    reset,
    adapter: {
      searchParams,
      setSearchParams,
    },
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("query")} placeholder="Search..." />
      <select {...register("category")}>
        <option value="all">All</option>
        <option value="tech">Tech</option>
        <option value="design">Design</option>
      </select>
      {/* Form fields */}
    </form>
  );
};

TypeScript

The library is fully typed with TypeScript generics. Import types if needed:

import { useSyncUrl, RouterAdapter } from "rhf-sync-url";

// Define your form data type
interface FormData {
  name: string;
  age: number;
  tags: string[];
}

// Use with type safety
const { control, reset } = useForm<FormData>({
  defaultValues: {
    name: "",
    age: 0,
    tags: [],
  },
});

useSyncUrl<FormData>({
  control,
  reset,
  adapter: {
    searchParams,
    setSearchParams,
  },
});

Available Types

  • useSyncUrl<T>: The main hook with generic type parameter
  • RouterAdapter: Interface for router adapter implementation

Important Notes

Security Considerations

  • Always use excludeFields: Explicitly exclude sensitive fields like passwords, SSN, credit cards, API keys, etc.
  • Never sync sensitive data: Even with exclusion, avoid using this package for forms that primarily contain sensitive data
  • Validate on the server: Always validate form data on the server side, never trust client-side data
  • Use HTTPS: Always use HTTPS in production to prevent URL parameter interception
  • Review field names: The hook warns in development if field names suggest sensitive data, but you should always review your forms
  • Test thoroughly: Verify excluded fields are never in URLs, even if they exist in the URL before exclusion is added

URL Length Limits

  • Browsers typically support 2000-8000 character URLs, but 2000 is safer
  • The hook warns when URLs exceed the configured maximum
  • For very large forms, consider reducing the amount of data synced to the URL

Best Practices

  1. Define default values: Always provide defaultValues in useForm() for proper type inference
  2. Type your forms: Use TypeScript generics for type safety
  3. Test thoroughly: Test with browser navigation (back/forward buttons)
  4. Monitor URL length: Adjust maxUrlLength based on your use case

Contributing

Contributions are welcome! Please feel free to create issues and submit Pull Requests in case you find any scope for improvement or vulnerabilities.

Author

Abhi1264