# state-in-url A React hook library for storing complex state objects in browser URLs with full TypeScript type preservation. ## What It Does state-in-url synchronizes React state with URL query parameters, enabling: - State sharing between unrelated components without prop drilling or context - State persistence across page reloads via URL - Shareable links that preserve application state - Type-safe state management with full TypeScript inference ## Package Information - **NPM:** https://www.npmjs.com/package/state-in-url - **GitHub:** https://github.com/asmyshlyaev177/state-in-url - **Website:** https://state-in-url.dev - **Main Documentation:** https://raw.githubusercontent.com/asmyshlyaev177/state-in-url/refs/heads/master/README.md ## Supported Frameworks | Framework | Versions | Documentation | |-----------|----------|---------------| | Next.js | v14-v15 | https://raw.githubusercontent.com/asmyshlyaev177/state-in-url/refs/heads/master/packages/urlstate/next/useUrlState/README.md | | React Router | v7 | https://raw.githubusercontent.com/asmyshlyaev177/state-in-url/refs/heads/master/packages/urlstate/react-router/useUrlState/README.md | | React Router | v6 | https://raw.githubusercontent.com/asmyshlyaev177/state-in-url/refs/heads/master/packages/urlstate/react-router6/useUrlState/README.md | | Remix | v2 | https://raw.githubusercontent.com/asmyshlyaev177/state-in-url/refs/heads/master/packages/urlstate/remix/useUrlState/README.md | ## Installation ```bash npm install state-in-url ``` ## Import Paths by Framework ```typescript // Next.js import { useUrlState } from "state-in-url/next"; // React Router v7 import { useUrlState } from "state-in-url/react-router"; // React Router v6 import { useUrlState } from "state-in-url/react-router6"; // Remix import { useUrlState } from "state-in-url/remix"; ``` ## Core API: useUrlState Hook The primary hook for state management synchronized with URL query parameters. **Returns:** - `urlState`: Current state object with full type inference - `setState`: Updates state immediately (batched URL sync) - `setUrl`: Syncs state to URL immediately ## Basic Usage Example ```typescript // 1. Define state type using 'type', not 'interface' type FormState = { searchQuery: string; isFiltered: boolean; page: number; }; // 2. Create initial state as static const (never from props/functions) const initialState: FormState = { searchQuery: "", isFiltered: false, page: 1 }; function SearchComponent() { const { urlState, setState, setUrl } = useUrlState(initialState); return (
{/* Read state - fully typed */}

Current search: {urlState.searchQuery}

Page: {urlState.page}

{/* Update state + URL immediately */} {/* Update state instantly, sync URL on blur */} setState({ searchQuery: e.target.value })} onBlur={() => setUrl()} /> {/* Reset to initial state */}
); } ``` ## Advanced Usage Patterns ### Functional Updates ```typescript // Using previous state setUrl(prev => ({ ...prev, page: prev.page + 1 })); // Accessing initial state setUrl((prev, initial) => ({ ...prev, ...initial })); setState((prev, initial) => initial); // full reset ``` ### Separate State Updates and URL Sync For frequently updated inputs, update state immediately and sync URL separately: ```typescript setState({ query: e.target.value })} // instant onBlur={() => setUrl()} // sync to URL when done /> ``` ### Next.js Server Components Pass `searchParams` to avoid hydration errors: ```typescript export default function Page({ searchParams }: { searchParams: Record }) { const { urlState, setUrl } = useUrlState(initialState, { searchParams }); // ... } ``` ## Recommended Pattern: Centralized State Create custom hooks in separate files for better organization: ```typescript // hooks/useSearchState.ts import { useUrlState } from "state-in-url/next"; type SearchState = { query: string; filters: string[]; sortBy: "name" | "date"; }; const initialState: SearchState = { query: "", filters: [], sortBy: "name" }; export function useSearchState() { return useUrlState(initialState); } ``` ```typescript // Any component import { useSearchState } from "@/hooks/useSearchState"; function SearchResults() { const { urlState, setUrl } = useSearchState(); // State is automatically shared across all components using this hook } ``` ## Critical Constraints and Limitations 1. **Type Definition:** Always use `type`, never `interface` 2. **Initial State:** Must be a static const, never from props/functions/destructuring 3. **Serialization:** Only JSON-serializable values (no functions, class instances, etc.) 4. **Security:** Never store sensitive data (API keys, tokens, passwords) 5. **Multiple Hooks:** Can use multiple hooks if top-level keys don't overlap 6. **URL Preservation:** Unrelated query parameters are preserved 7. **Throttling:** Updates are throttled for performance ## State Updates Behavior - `setState()`: Updates state immediately, URL sync expected to be done manually - `setUrl()`: Syncs state to URL immediately - Both support functional updates with access to previous and initial state - URL updates trigger state synchronization across all components using the same hook ## Type Safety All state operations have full TypeScript type inference: - `urlState` matches the shape of your initial state type - `setState` and `setUrl` validate against your type - Nested object properties are fully typed ## Documentation Resources - Main README with comprehensive examples: GitHub repository - Framework-specific guides: See documentation links per framework above - JSDoc comments: Available in IDE for all exported functions