state-in-url

repository·master·Indexed 19 days ago

https://github.com/asmyshlyaev177/state-in-url

A lightweight library for synchronizing application state with URL query parameters, preserving types and structure with TS validation. It provides a React.useState-like API for Next.js (v14-15), react-router (v6-7), and remix (v2). Key features include the useUrlState hook for framework-specific synchronization, useUrlEncode for state shape management, and utility functions like encodeState and decodeState for handling complex JSON-serializable objects in the browser URL.

Tokens
32.5K
Snippets
97
Records
128
Agent score
60%

What's inside state-in-url

  1. What is state-in-url and when to use it?

    master

    Overview

    state-in-url allows you to store any user state in query parameters, effectively using the browser URL as a JSON store. It maintains data types and structures (e.g., numbers remain numbers, dates remain dates, and it supports nested objects and arrays) while providing static TypeScript validation.

    Key Features

    • Simple API: Similar to React.useState, requiring no providers or reducers.
    • TypeScript Support: Provides static validation and autocompletion based on your state definitions.
    • Complex Data: Supports nested objects, arrays, and dates.
    • Default Values: Allows defining fallback values if the parameter is missing from the URL.
    • Framework Support: Includes specialized hooks for Next.js and react-router, with utility functions for other JS frameworks.
    • Lightweight: Zero dependencies and under 2KB.

    Use Cases

    • Storing unsaved form data or page filters in the URL.
    • Synchronizing React state with the URL (Deep Linking).
    • Synchronizing data between unrelated client components without using a global state manager.
    • Persisting state across page reloads via the URL.
    • Sharing application state via a single URL.
  2. Overview of state-in-url

    master

    state-in-url allows you to store application state in URL query parameters as if they were JSON. It maintains data types (e.g., numbers remain numbers, dates remain dates) and supports complex structures like nested objects and arrays.

    Key Benefits:

    • Deep Linking: Easily share URLs that contain specific application states (filters, form data, etc.).
    • Type Safety: Provides static TypeScript validation and autocomplete for your state objects.
    • Framework Support: Includes specialized hooks for Next.js and react-router, with helpers for other environments.
    • Lightweight: Zero dependencies and under 2KB.
    • Performance: Optimized for minimal re-renders and fast encoding/decoding (approx. 1ms for large objects).
  3. Key features of state-in-url

    master

    Core Capabilities

    • Simplicity: API similar to React.useState with no complex providers or reducers required.
    • TypeScript Support: Provides static type validation and IDE autocompletion for state structures.
    • Complex Data Types: Supports nested objects, dates, and arrays via JSON-like encoding in the URL.
    • Default Values: Allows defining fallback values when URL parameters are missing.
    • Performance: Extremely fast encoding/decoding (approx. 1ms) with minimal re-renders.
    • SSR Support: Compatible with Server-Side Rendering, including Next.js 14 and 15.
    • Framework Agnostic: Provides hooks for Next.js and react-router, plus helpers for pure JS environments.
  4. Explore state-in-url documentation modules

    master

    The state-in-url library provides several specialized tools for managing application state via URL parameters. Depending on your framework and requirements, you can use the following modules:

    • Next.js integration: Use the useUrlState hook for seamless state management in Next.js applications.
    • React integration: Use the useUrlEncode hook for general React applications.
    • Object encoding: Use the encodeState function to transform complex objects into URL-friendly strings.
    • Single value encoding: Use the encoder for managing single query values.
    • Constants: Access core library constants for consistent state handling.
  5. Security: What to store in the URL

    master

    When using state-in-url, follow the "no sensitive data in URL" rule:

    • DO NOT STORE: True secrets such as authentication tokens, API keys, passwords, or Personally Identifiable Information (PII) like emails or SSNs.
    • SAFE TO STORE: Entity IDs (e.g., jobId, memberId, channelId) that reference public or semi-public database rows. These are functionally equivalent to IDs used in route paths.
  6. Use `useSharedState` for framework-agnostic shared state

    master

    Use useSharedState (imported from the top-level state-in-url package) as a lightweight, cross-component state primitive when you explicitly do not want the state to sync with the URL.

    It acts as a replacement for Context.Provider, Redux, or Zustand without requiring any setup. State is shared between any components that pass the exact same module-scoped default-state object (sharing is based on object identity, not deep equality).

    When to use useSharedState instead of useUrlState:

    • The state is sensitive (e.g., not suitable for URL visibility).
    • The state is ephemeral (e.g., temporary UI toggles).
    • The state is too large to fit in a URL.
    • You explicitly want to opt-out of URL synchronization.
    import { useSharedState } from 'state-in-url';
    
    // The state object must be defined in a module scope to ensure identity-based sharing
    export const MY_SHARED_STATE = { count: 0 };
    
    function Component() {
      const { state, setState } = useSharedState(MY_SHARED_STATE);
      // ...
    }
  7. Important constraints and best practices

    master

    When using state-in-url, keep the following limitations and best practices in mind:

    Constraints

    • Serializable Values Only: You can only pass values that are JSON-serializable. Function, BigInt, Symbol, and ArrayBuffer are not supported.
    • URL Length Limit: Vercel servers limit header sizes (including query strings) to 14KB. Keep your URL state under approximately 5,000 words to avoid errors.
    • Next.js Compatibility: Currently tested with Next.js 14/15 using the App Router. Support for the Pages Router is planned but not currently implemented.

    Best Practices

    • Define state as constants: Define your initial state shapes as constants.
    • Use TypeScript: Leverage TypeScript for enhanced type safety and autocompletion.
    • Security: Never store sensitive information (SSNs, API keys, etc.) in URL parameters.
    • Reusable Hooks: Create specialized hooks for specific state slices (e.g., useUserState) to promote reusability across your application.
  8. Understand the timing of `useUrlState` setters

    master

    The useUrlState hook provides three different ways to update state, each with distinct timing and side effects. Understanding these is critical for reconciling instant UI feedback with asynchronous URL updates.

    SetterWhat updatesWhen
    setState(value)Internal state onlySynchronous
    setUrl(value)Internal state + URLState syncs immediately; URL updates on the next tick (throttled)
    setUrl()Flush current state to URLURL updates on the next tick (diff-checked; no-op if equal)

    State updates always trigger an immediate re-render. URL writes are coalesced using an internal global timer, meaning a burst of setUrl calls will result in only one actual URL update.

  9. When NOT to use `useSharedState`

    master

    Do not use useSharedState for the following use cases:

    • URL-shareable data: If the data should be part of the URL (e.g., pagination, filters), use state-in-url/feature-state-hook (useUrlState) instead.
    • Server Cache: For managing server-side data like lists or paginated queries, use @tanstack/react-query or swr.
    • Persistent User Preferences: For data that must persist across browser sessions (e.g., dark mode settings), use localStorage or a dedicated settings API.
  10. How to manage Drawer and Modal states via URL

    master

    You can control the visibility of drawers or modals by using an ID field in your state.

    Pattern: Use an empty string ('') as the default value for the ID.

    • Closed state: The ID is an empty string. The URL remains clean (no parameter present).
    • Open state: The ID contains the entity's identifier. The parameter appears in the URL.

    To close the UI, call setUrl with the original default state object, which clears all related parameters in one call.

    type MembersState = { memberId: string; tab: 'profile' | 'activity' };
    const MEMBERS_STATE: MembersState = { memberId: '', tab: 'profile' };
    
    // To open
    const open = (id: string) => setUrl({ memberId: id, tab: 'profile' });
    
    // To close (returns all fields to default, removing them from URL)
    const close = () => setUrl({ ...MEMBERS_STATE });
  11. Core API and Hooks

    master

    The library provides several ways to manage state depending on your environment and requirements:

    • useUrlState: The primary hook for synchronizing state with the URL. It is optimized for Next.js App Router, React Router (v6/v7), and Remix (v2).
    • useSharedState: A framework-agnostic primitive for cross-component state sharing that does not sync to the URL. Use this when you need shared state but do not want to modify the URL.
    • useUrlEncode / encodeState / decodeState: Framework-agnostic utility helpers for manual state serialization and deserialization.

    Important Constraints:

    • State must be JSON-serializable. Do not attempt to store non-serializable values (like functions or class instances).
    • When using useUrlState, ensure you use type instead of interface for TypeScript definitions to avoid compatibility issues.
  12. Important limitations and constraints

    master

    When using state-in-url, keep the following technical constraints in mind:

    1. Serialization: Only serializable values can be passed. Function, BigInt, Symbol, and types like ArrayBuffer are not supported. Any content that can be serialized to JSON will work.
    2. URL Length (Vercel): Vercel imposes a header size limit (including query strings) of 14KB. To avoid errors, keep your URL state under approximately 5000 characters.
    3. Next.js Compatibility: The library has been tested with next.js 14/15 using the App Router. There are currently no plans to support the Pages Router.