Skip to content

feat(react-router): Add useHistoryState hook for type-safe history state management #3967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

naoya7076
Copy link

@naoya7076 naoya7076 commented Apr 9, 2025

Remaining tasks

useHistoryState

  • Enable useHistoryState to access validated state from match object

Dev tool update

Currently displaying all states, but some are unnecessary.

  • Filter out states starting with __.

Conflict resolution

Resolve the conflict in packages/react-router/src/router.ts.

Unit test fix

Review and fix packages/react-router/tests/useHistoryState.test.tsx.

Overview

This PR introduces the new useHistoryState hook and related functionality to enable type-safe history state management in TanStack Router.

#975 (comment)

Background

In the current implementation of TanStack Router, to pass type-safe state (History State) between routes that isn't part of the path or params, we need to define types globally. This leads to two main issues:

  1. Type pollution - types appear in places where they aren't needed
  2. Interface bloat - as more states are added, the global interface grows excessively

For example:

declare module '@tanstack/react-router' {
  interface HistoryState {
    user?: MyUserType;
    project?: MyProjectType;
  }
}

Additionally, when migrating from react-router to TanStack Router, developers want to be able to handle state in a type-safe manner, similar to how they could with react-router:
#284

const history = useHistory();
history.replace({ pathname: '/', state: { name: 'Lily' }});
...
const location = useLocation();
// do something with location.state...

This PR addresses these issues by adding route-specific, type-safe history state management.

Changes

  • Added useHistoryState hook export in the main package index
  • Extended the FileRoute class to support state validation with TStateValidator generic parameter
  • Created a full example in examples/react/basic-history-state demonstrating usage patterns
  • The example shows how to:
    • Define state schemas using Zod
    • Pass state during navigation with Link and navigate
    • Retrieve state in different ways (direct route access, from parameter, selection, non-strict mode)
    • Handle optional properties like message in state objects

Key features of useHistoryState

  • Type-safe access to history state passed during navigation
  • Support for default values and optional properties
  • Ability to select specific state properties
  • Non-strict mode for accessing raw state object

Example usage

Users can define state validation on routes:

validateState: (input) =>
  z.object({
    color: z.enum(['white', 'red', 'green', 'blue']).default('white'),
    visited: z.boolean().default(false),
    message: z.string().optional(),
  }).parse(input),

Then use it in components:

// From route directly
const state = postRoute.useHistoryState()

// With select to extract specific values
const color = useHistoryState({
  from: '/posts/post',
  select: (state) => state.color
})

This commit introduces a new `validateState` function to handle state validation within the router. It supports various validation methods, including standard validation and parsing, and integrates with route options to validate state during routing.
This commit introduces the TStateValidator type across various routing components, enhancing the validation capabilities for state management. The changes include updates to the createFileRoute and Route classes, as well as adjustments in related files to support the new state validation feature.
This commit introduces the `useHistoryState` hook and related types across various routing components, improving state management capabilities. Updates include modifications to the Route and createRoute functions to support the new state handling feature.
This commit introduces a new section in the BaseTanStackRouterDevtoolsPanel to display state parameters when available.
This commit introduces the `useHistoryState` hook along with its associated types, enhancing the state management capabilities within the router. The new implementation allows for more flexible state handling and selection options, improving overall functionality.
This commit introduces new TypeScript types related to the `useHistoryState` hook, improving type safety and flexibility in state management within the router. The new types facilitate better state selection and handling options, enhancing the overall functionality of the routing system.
…roved state selection

This commit refactoring the state selection logic to utilize `useRouterState`.
…resolution

This commit modifies the `ResolveUseHistoryState` and ` UseHistoryStateResult` types
…ryState

This commit updates the `useHistoryState` hook to utilize `useLocation` for state management instead of `useRouterState`, improving the clarity and efficiency of state selection logic.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant