clear-react-router 1.7.9 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,23 +38,6 @@ It provides first-class support for:
38
38
 
39
39
  ## API
40
40
 
41
- ### `createRouter(routes)`
42
-
43
- Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic params, builds nested paths.
44
-
45
- | Property | Type | Description |
46
- |----------|------|-------------|
47
- | `path` | `string` | Route path, e.g., `/user/:userId` |
48
- | `element` | `ReactElement \| () => ReactElement \| LazyComponent` | Component to render |
49
- | `beforeLoad` | `({ params, context, redirect, setContext }) => Promise<unknown> \| undefined \| void` | Auth checks and redirects. Can update context via `setContext`. `redirect` is provided by the router |
50
- | `loader` | `({ params, context, setContext }) => Promise<unknown>` | Fetch data using route params and context. Can update context via `setContext` |
51
- | `afterLoad` | `({ params, context, setContext }) => Promise<void>` | Analytics, side effects after data is loaded. Can update context via `setContext` |
52
- | `fallback` | `ReactElement \| () => ReactElement` | Loading fallback (for lazy loading) |
53
- | `loaderFallback` | `ReactElement \| () => ReactElement` | Loading fallback for the route's `loader`. Overrides the global `defaultLoaderFallback` set in `Router` |
54
- | `errorElement` | `ReactElement \| () => ReactElement` | Error fallback for the route. Overrides the global `defaultErrorElement` set in `Router` |
55
- | `staleTime` | `number` | Time in ms before cached data is considered stale and re-fetched in the background. If not provided, data never expires (cached forever) |
56
- | `actions` | `({ params, context, invalidate, setContext }) => Record<string, (formData: FormData) => unknown \| Promise<unknown>>` | Defines route actions for data mutations. Actions receive `FormData`, can update context via `setContext`, and can refresh loader data using the router-provided `invalidate`. |
57
-
58
41
  ### `Router`
59
42
 
60
43
  | Prop | Type | Default | Description |
@@ -64,6 +47,7 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
64
47
  | `animationDuration` | `number` | `optional` | Animation duration in milliseconds (browser default is used if not set) |
65
48
  | `defaultLoaderFallback` | `ReactElement \| () => ReactElement` | `optional` | Default loading fallback for every route loader |
66
49
  | `defaultErrorElement` | `ReactElement \| () => ReactElement` | `optional` | Default error fallback for every route |
50
+ | `defaultRetry` | `number \| { count: number; delay: number }` | `optional` | Default cache revalidation retry policy for all routes |
67
51
  | `beforeLoad` | `({ params, context, redirect, setContext }) => Promise<unknown> \| undefined \| void` | `undefined` | Runs before every navigation. Useful for authentication, analytics, or updating shared context. |
68
52
  | `afterLoad` | `({ params, context, setContext }) => Promise<void>` | `undefined` | Runs after every successful navigation. Useful for analytics, page tracking, or other global side effects. |
69
53
  | `spinner` | `boolean \| undefined` | `true` | Show a small spinner in the corner while loading data (only when `isAnimated` is enabled) |
@@ -82,9 +66,26 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
82
66
  <Router routes={routes} spinner={false} isAnimated /> {/* disable the spinner */}
83
67
  </div>
84
68
  ```
85
-
86
69
  > **Note:** When `isAnimated` is enabled, `loaderFallback` is not shown. Instead, a small spinner appears (if `spinner={true}`). On the initial page load, however, the route's loaderFallback is rendered if available.
87
70
 
71
+ ### `createRouter(routes)`
72
+
73
+ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic params, builds nested paths.
74
+
75
+ | Property | Type | Description |
76
+ |----------|------|-------------|
77
+ | `path` | `string` | Route path, e.g., `/user/:userId` |
78
+ | `element` | `ReactElement \| () => ReactElement \| LazyComponent` | Component to render |
79
+ | `beforeLoad` | `({ params, context, redirect, setContext }) => Promise<unknown> \| undefined \| void` | Auth checks and redirects. Can update context via `setContext`. `redirect` is provided by the router |
80
+ | `loader` | `({ params, context, setContext }) => Promise<unknown>` | Fetch data using route params and context. Can update context via `setContext` |
81
+ | `afterLoad` | `({ params, context, setContext }) => Promise<void>` | Analytics, side effects after data is loaded. Can update context via `setContext` |
82
+ | `fallback` | `ReactElement \| () => ReactElement` | Loading fallback (for lazy loading) |
83
+ | `loaderFallback` | `ReactElement \| () => ReactElement` | Loading fallback for the route's `loader`. Overrides the global `defaultLoaderFallback` set in `Router` |
84
+ | `retry` | `number \| { count: number; delay: number }` | `optional` | Overrides the global cache revalidation retry policy for this route |
85
+ | `errorElement` | `ReactElement \| () => ReactElement` | Error fallback for the route. Overrides the global `defaultErrorElement` set in `Router` |
86
+ | `staleTime` | `number` | Time in ms before cached data is considered stale and re-fetched in the background. If not provided, data never expires (cached forever) |
87
+ | `actions` | `({ params, context, invalidate, setContext }) => Record<string, (formData: FormData) => unknown \| Promise<unknown>>` | Defines route actions for data mutations. Actions receive `FormData`, can update context via `setContext`, and can refresh loader data using the router-provided `invalidate`. |
88
+
88
89
  ### `Link`
89
90
 
90
91
  Component for client-side navigation with prefetch support.
@@ -125,6 +126,68 @@ import { Router, Link } from 'clear-react-router';
125
126
  ```
126
127
  **Important**: prefetch="render" should be used sparingly, as it preloads data immediately when the link is rendered, which may cause unnecessary network requests.
127
128
 
129
+ ## Retry
130
+
131
+ Sometimes a request may fail because of a temporary network issue or a short-lived server problem. Instead of immediately rendering the error state, you can configure the router to automatically retry loading route data.
132
+
133
+ ### Route-level retry
134
+
135
+ ```tsx
136
+ {
137
+ path: '/posts',
138
+ loader: loadPosts,
139
+ retry: 3,
140
+ }
141
+ ```
142
+
143
+ `retry: 3` means the router will make up to **3 additional attempts** after the initial failed request (up to **4 attempts** in total).
144
+
145
+ You can also specify a delay between attempts:
146
+
147
+ ```tsx
148
+ {
149
+ path: '/posts',
150
+ loader: loadPosts,
151
+ retry: {
152
+ count: 3,
153
+ delay: 500,
154
+ },
155
+ }
156
+ ```
157
+
158
+ ### Global retry
159
+
160
+ To apply the same retry policy to all routes, use `defaultRetry`:
161
+
162
+ ```tsx
163
+ <Router routes={routes} defaultRetry={2} />
164
+ ```
165
+
166
+ or with a delay:
167
+
168
+ ```tsx
169
+ <Router routes={routes} defaultRetry={{ count: 2, delay: 500 }} />
170
+ ```
171
+
172
+ A route-level `retry` always overrides `defaultRetry`.
173
+
174
+ ### How it works
175
+
176
+ Unlike many routing libraries, retry is **not limited to the initial loader execution**.
177
+
178
+ The retry policy is applied to the router's **cache revalidation mechanism**, so it automatically works for every operation that reloads route data, including:
179
+
180
+ * Initial route loading
181
+ * Cache revalidation
182
+ * `invalidate()`
183
+ * `prefetch()`
184
+
185
+ This ensures consistent behavior regardless of how the data is being refreshed.
186
+
187
+ ### Why?
188
+
189
+ The router treats the route loader as the single source of truth for route data. Since every data refresh goes through the same cache revalidation pipeline, retry is configured once and automatically applies everywhere without any additional code.
190
+
128
191
  ### `redirect`
129
192
 
130
193
  Function provided to `beforeLoad` for programmatic redirection.
@@ -456,10 +519,11 @@ await invalidate('/post/[id]/comment/[id]');
456
519
 
457
520
  #### Including child routes
458
521
 
459
- To revalidate a single route together with its cached child routes, pass the `withChildren` option:
522
+ To revalidate routes together with their cached child routes, pass the `withChildren` option:
460
523
 
461
524
  ```tsx
462
- await invalidate('/posts', { withChildren: true }); // a route, not a route list
525
+ await invalidate('/posts', { withChildren: true });
526
+ await invalidate(['/posts', '/users'], { withChildren: true });
463
527
  ```
464
528
 
465
529
  This will recursively revalidate cached routes inside the route tree.
package/dist/cell.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- declare class Cell<T> {
1
+ export declare class Cell<T> {
2
2
  private _value;
3
3
  constructor(_value: T);
4
4
  get value(): T;
5
5
  set(action: T | ((prev: T) => T)): void;
6
6
  }
7
- export declare const loaderStateRef: Cell<import("./types/global").LoaderState>;
7
+ export declare const loaderStateRef: Cell<import("./types").LoaderState>;
8
8
  export declare const prevPathnameRef: Cell<string>;
9
9
  export declare const timestampMap: Map<string, number>;
10
- export {};
@@ -1,5 +1,5 @@
1
1
  import { type ReactElement, type MouseEvent, type CSSProperties } from 'react';
2
- import { RouterProps } from '../types/global';
2
+ import { RouterProps } from '../types';
3
3
  type LinkProps = {
4
4
  to: string;
5
5
  children: ReactElement<{
@@ -1,2 +1,2 @@
1
- import { RouterProps } from '../types/global';
2
- export declare const Router: ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
1
+ import { RouterProps } from '../types';
2
+ export declare const Router: ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement, defaultRetry, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,4 +1,4 @@
1
- import { ClientRouteItem, RouterProps } from '../types/global';
1
+ import { ClientRouteItem, RouterProps } from '../types';
2
2
  declare class RouterConfig {
3
3
  routes: RouterProps['routes'];
4
4
  prefetch: RouterProps['prefetch'];
@@ -7,6 +7,7 @@ declare class RouterConfig {
7
7
  hoverPrefetchDelay: number;
8
8
  beforeLoad?: ClientRouteItem['beforeLoad'];
9
9
  afterLoad?: ClientRouteItem['afterLoad'];
10
+ defaultRetry?: RouterProps['defaultRetry'];
10
11
  configure(config: Partial<RouterConfig>): void;
11
12
  }
12
13
  export declare const routerConfig: RouterConfig;
@@ -1,3 +1,3 @@
1
- import type { LoaderState } from './types/global.ts';
1
+ import type { LoaderState } from './types';
2
2
  export declare const emptyLoaderState: LoaderState;
3
3
  export declare const STANDARD_PREFETCH_DELAY = 150;
@@ -1,11 +1,10 @@
1
1
  type SetStateAction<T> = ((prevState: T) => T) | T;
2
2
  type Listener<T> = (state: T, prevState: T) => void;
3
- type Store<T> = {
3
+ export type Store<T> = {
4
4
  subscribe: (listener: Listener<T>) => () => void;
5
5
  getState: () => T;
6
6
  setState: (action: SetStateAction<T>) => void;
7
7
  };
8
8
  export declare const create: <T>(initialState: T) => Store<T>;
9
9
  export declare const useGlobalState: <T>({ subscribe, getState, setState }: Store<T>) => readonly [T, (action: SetStateAction<T>) => void];
10
- export declare const createState: <T>(initialState: T) => () => readonly [T, (action: SetStateAction<T>) => void];
11
10
  export {};
@@ -1,6 +1 @@
1
- type Options = Partial<{
2
- onSuccess: (args: unknown) => void;
3
- onError: (args: unknown) => void;
4
- }> | undefined;
5
- export declare const useAction: (action: string, options?: Options) => (formData: FormData) => Promise<void>;
6
- export {};
1
+ export declare const useAction: (action: string, options?: import("../types").Options) => (arg: FormData) => Promise<void>;
@@ -1,4 +1,4 @@
1
- import { BlockerState } from '../types/global';
1
+ import { BlockerState } from '../types';
2
2
  type UseBlockerReturnValue = {
3
3
  state: BlockerState;
4
4
  process(): void;
@@ -1,2 +1 @@
1
- import { invalidate } from '../runtime/invalidate';
2
- export declare const useInvalidate: () => typeof invalidate;
1
+ export declare const useInvalidate: () => (pathList?: string | string[], options?: import("../types").InvalidateOptions) => Promise<void>;
@@ -1,2 +1,2 @@
1
- import { LoaderState } from '../types/global';
1
+ import { LoaderState } from '../types';
2
2
  export declare const useLoaderState: <T>() => LoaderState<T>;
@@ -1,2 +1 @@
1
- import type { Location } from '../types/global';
2
- export declare const useNavigate: () => (arg: Location | string | -1) => Promise<void>;
1
+ export declare const useNavigate: () => (arg: import("..").Location | string | -1) => Promise<void>;
@@ -1,2 +1,2 @@
1
- import { AdapterType } from '../types/global';
2
- export declare function useQueryParam<T>(field: string, adapter: AdapterType<T>, defaultValue?: T): [T, (arg: T | null) => void];
1
+ import { Adapter } from '../types';
2
+ export declare function useQueryParam<T>(field: string, adapter: Adapter<T>, defaultValue?: T): [T, (arg: T | null) => void];
@@ -1 +1,2 @@
1
- export declare const useSetInitialContext: (initialContext?: Record<string, unknown>) => void;
1
+ import { RouterProps } from '../types';
2
+ export declare const useSetInitialContext: (initialContext?: RouterProps["context"]) => void;
@@ -1,2 +1,2 @@
1
- import { RouterProps } from '../types/global';
1
+ import { RouterProps } from '../types';
2
2
  export declare const useSetRouterConfig: (routerProps: RouterProps) => void;
package/dist/index.d.ts CHANGED
@@ -14,4 +14,4 @@ export { useSearchParams } from './hooks/useSearchParams';
14
14
  export { useFormContext } from './hooks/useFormContext';
15
15
  export { adapter } from './utils/adapter';
16
16
  export { createRouter } from './utils/utils';
17
- export type { RouteItem, BlockerState, Location, AdapterType, RouterProps } from './types/global';
17
+ export type { RouteItem, BlockerState, Location, Adapter, RouterProps } from './types';