clear-react-router 1.6.3 → 1.6.5

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
@@ -20,7 +20,7 @@ It provides first-class support for:
20
20
  - **Navigation Blocking** - Prevent accidental navigation with `useBlocker`
21
21
  - **Smooth Animations** - Page transitions with fade effect (customizable duration)
22
22
  - **Static Layout** — Keep navbar, footer, and other elements outside the router to avoid unnecessary re-renders
23
- - **Type-safe Redirects** - Redirect from beforeLoad hook
23
+ - **Programmatic Redirects** - Redirect from beforeLoad hook
24
24
  - **Cache invalidation** - Manual route invalidation
25
25
  - **Prefetching** - Preload data on hover for instant navigation
26
26
  - **Lazy Loading** - Code-split your routes with dynamic imports for optimal performance
@@ -55,11 +55,11 @@ The root component that provides routing context to the application. Place stati
55
55
 
56
56
  | Prop | Type | Default | Description |
57
57
  |------|------|---------|-------------|
58
- | `routeList` | `RouteItem[]` | required | Array of route configurations |
58
+ | `routes` | `RouteItem[]` | required | Array of route configurations |
59
59
  | `context` | `object` | `{}` | Initial context (user, theme, etc.) |
60
60
  | `children` | `ReactNode` | required | App content (must include `<Router />`) |
61
61
 
62
- ```
62
+ ```tsx
63
63
  function App() {
64
64
  return (
65
65
  <RouterProvider routeList={routes}>
@@ -83,12 +83,13 @@ Renders the current route's component. Must be placed inside `<RouterProvider>`.
83
83
  | `animationDuration` | `number` | `optional` | Animation duration in milliseconds (browser default is used if not set) |
84
84
  | `spinner` | `boolean \| undefined` | `true` | Show a small spinner in the corner while loading data (only when `isAnimated` is enabled) |
85
85
  | `preserveScroll` | `boolean \| undefined` | `true` | Save and restore scroll position when navigating between pages |
86
- | `showFallbackIfAnimated` | `boolean \| undefined` | `false` | Show `loaderFallback` even when `isAnimated` is `true` (instead of spinner) |
86
+ | `showFallbackOnAnimation` | `boolean \| undefined` | `false` | Show `loaderFallback` even when `isAnimated` is `true` (instead of spinner) |
87
87
  | `prefetch` | `'hover' \| 'render' \| 'viewport' \| 'none'` | `'hover'` | Default prefetch strategy for all `<Link>` components |
88
88
  | `hoverPrefetchDelay` | `number` | `150` | Delay in milliseconds before prefetching on hover (only for `'hover'` strategy) |
89
+ | `errorBoundary` | `ComponentType<{ children: ReactNode }>` | `undefined` | Custom error boundary component for catching render errors in route components |
89
90
 
90
- ```
91
- <RouterProvider routeList={routes}>
91
+ ```tsx
92
+ <RouterProvider routes={routes}>
92
93
  <Navbar />
93
94
  <Router spinner={false} isAnimated /> {/* disable the spinner */}
94
95
  </RouterProvider>
@@ -118,11 +119,11 @@ Component for client-side navigation with prefetch support.
118
119
 
119
120
  **Example:**
120
121
 
121
- ```
122
+ ```tsx
122
123
  import { RouterProvider, Router, Link } from 'clear-react-router';
123
124
 
124
125
  // Global prefetch: hover with 100ms delay
125
- <RouterProvider routeList={routes} prefetch="hover" hoverPrefetchDelay={100}>
126
+ <RouterProvider routes={routes} prefetch="hover" hoverPrefetchDelay={100}>
126
127
  <Router />
127
128
  </RouterProvider>
128
129
 
@@ -144,7 +145,7 @@ Function provided to `beforeLoad` for programmatic redirection.
144
145
 
145
146
  **Type:** `(arg: Location | string) => Promise<void>`
146
147
 
147
- ```
148
+ ```tsx
148
149
  import type { createRouter } from 'clear-react-router';
149
150
 
150
151
  const routes = createRouter([
@@ -188,7 +189,9 @@ const routes = createRouter([
188
189
 
189
190
  The `loader`, `beforeLoad`, and `afterLoad` hooks receive `params` (extracted from the URL) and `context` as arguments. This allows you to handle route-specific logic directly in the route configuration, keeping your components focused on rendering.
190
191
 
191
- ```
192
+ ```tsx
193
+ import type { createRouter } from 'clear-react-router';
194
+
192
195
  const routes = createRouter([
193
196
  {
194
197
  path: '/user/:userId',
@@ -216,13 +219,30 @@ const routes = createRouter([
216
219
  ]);
217
220
  ```
218
221
 
222
+ ### Error Boundaries
223
+
224
+ You can provide a custom error boundary to catch rendering errors in route components. This is useful for preventing the entire app from crashing when a specific route fails to render.
225
+
226
+ ```tsx
227
+ import { RouterProvider, Router } from 'clear-react-router';
228
+ import { routes } from './routes';
229
+ import { ErrorBoundary } from './components/ErrorBoundary';
230
+
231
+ const App = () => (
232
+ <RouterProvider routes={routes}>
233
+ <Router errorBoundary={ErrorBoundary} />
234
+ </RouterProvider>
235
+ );
236
+ ```
237
+ **Note:** The `errorBoundary` prop only catches render-time errors in route components. It does not catch errors in `loader` or `beforeLoad` — those are handled by the router's `errorElement` mechanism.
238
+
219
239
  ## Hooks
220
240
 
221
241
  ### `useNavigate()`
222
242
 
223
243
  Returns function to navigate programmatically. Accepts a string (pathname), an object with `pathname`, `search`, and `state`, or `-1` to go back.
224
244
 
225
- ```
245
+ ```tsx
226
246
  const navigate = useNavigate();
227
247
 
228
248
  navigate('/about'); // string
@@ -232,7 +252,7 @@ navigate(-1); // go back
232
252
 
233
253
  **Note:** Navigation state can be accessed via `useLocation()`:
234
254
 
235
- ```
255
+ ```tsx
236
256
  const navigate = useNavigate();
237
257
  navigate({ pathname: '/profile', state: { userId: 123 } });
238
258
 
@@ -245,7 +265,7 @@ console.log(state); // { userId: 123 }
245
265
 
246
266
  Returns route parameters object.
247
267
 
248
- ```
268
+ ```tsx
249
269
  const params = useParams<{ userId: string }>();
250
270
  // URL: /user/123 → params.userId === '123'
251
271
  ```
@@ -253,7 +273,7 @@ const params = useParams<{ userId: string }>();
253
273
  ### `useLocation()`
254
274
 
255
275
  Returns current location `{ pathname, search, state }`.
256
- ```
276
+ ```tsx
257
277
  const { pathname, search, state } = useLocation();
258
278
  ```
259
279
 
@@ -269,7 +289,7 @@ Returns the cached data loaded by the current route's `loader`, along with any e
269
289
  | `loaderError` | `Error \| null` | Error from the `loader` (if any) |
270
290
  | `beforeLoadError` | `Error \| null` | Error from the `beforeLoad` hook (if any) |
271
291
 
272
- ```
292
+ ```tsx
273
293
  const UserProfile = () => {
274
294
  const { data, loaderError, beforeLoadError } = useLoaderState<User>();
275
295
  ```
@@ -285,7 +305,7 @@ const UserProfile = () => {
285
305
  }
286
306
  ```
287
307
 
288
- ### `useInvalidate`
308
+ ### `useInvalidate()`
289
309
 
290
310
  Returns a function that marks route data as stale and re-runs the route lifecycle.
291
311
 
@@ -362,7 +382,7 @@ Blocks navigation when callback returns `true`.
362
382
  | `process()` | `() => void` | Confirm navigation and proceed |
363
383
  | `reset()` | `() => void` | Cancel navigation |
364
384
 
365
- ```
385
+ ```tsx
366
386
  const { state, process, reset } = useBlocker(() => hasUnsavedChanges);
367
387
 
368
388
  useEffect(() => {
@@ -389,7 +409,7 @@ Executes a callback when the page is about to be closed or reloaded. Perfect for
389
409
 
390
410
  **Note:** This hook does not show a browser confirmation dialog. It silently executes the callback, allowing you to save user data in the background before the page closes.
391
411
 
392
- ```
412
+ ```tsx
393
413
  const [text, setText] = useState('');
394
414
  const onSave = useCallback(() => {
395
415
  localStorage.setItem('draft', text);
@@ -404,7 +424,7 @@ useBeforeUnload(text ? onSave : undefined);
404
424
 
405
425
  A flexible hook for working with typed query parameters. You provide an adapter object with `parse` and `serialize` functions, and it returns the parsed value and a setter.
406
426
 
407
- ```
427
+ ```tsx
408
428
  import { useQueryParam, adapter } from 'clear-react-router';
409
429
 
410
430
  const ProductPage = () => {
@@ -468,7 +488,7 @@ type Adapter<T> = {
468
488
  ### Using Zod Schemas
469
489
  `useQueryParam` works seamlessly with Zod for complex validation:
470
490
 
471
- ```
491
+ ```tsx
472
492
  import { z } from 'zod';
473
493
  import { useQueryParam, adapter } from 'clear-react-router';
474
494
 
@@ -500,7 +520,7 @@ function ProductFilter() {
500
520
  ### Custom Adapters
501
521
  You can write your own adapter for any format:
502
522
 
503
- ```
523
+ ```tsx
504
524
  // Custom adapter for comma-separated values
505
525
  const csvAdapter = {
506
526
  parse: (params: string[]): string[] => {
@@ -519,7 +539,7 @@ const TagsFilter() {
519
539
  ### `useRouterContext()`
520
540
 
521
541
  Returns the router context object and a function to update it. Useful for accessing or modifying global state (like user authentication, theme, etc.) from anywhere in your app.
522
- ```
542
+ ```tsx
523
543
  const { setContext, context } = useRouterContext();
524
544
  const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
525
545
  ```
@@ -528,7 +548,7 @@ const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
528
548
 
529
549
  Returns an object for working with URL query parameters. Supports reading and setting both single values and arrays.
530
550
 
531
- ```
551
+ ```tsx
532
552
  import { useSearchParams } from 'clear-react-router';
533
553
 
534
554
  function ProductFilter() {
@@ -566,7 +586,6 @@ function ProductFilter() {
566
586
 
567
587
  - **Array support** — `getSearchParams` returns `string[]` when multiple values exist for the same key
568
588
  - **Functional updates** — Update parameters based on previous state without losing other params
569
- - **Type-safe** — Proper TypeScript support with overloads
570
589
  - **Stable reference** — `setSearchParams` reference is stable and safe to use in `useEffect`
571
590
 
572
591
  > **Note:** `getSearchParams` returns `string` for single values, `string[]` for multiple values, and `''` if the key is not found.
@@ -586,7 +605,7 @@ Returns an array of pathnames representing the user's actual navigation history.
586
605
  ## Lazy Loading
587
606
 
588
607
  Clear Router supports code-splitting out of the box. Simply pass a function that returns a dynamic import:
589
- ```
608
+ ```tsx
590
609
  {
591
610
  path: '/heavy-page',
592
611
  element: () => import('./pages/HeavyComponent'),
@@ -1,2 +1,2 @@
1
1
  import { RouterProps } from '../types/global';
2
- export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll, showFallbackIfAnimated, prefetch, hoverPrefetchDelay, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
2
+ export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -2,8 +2,8 @@ import { ReactNode } from 'react';
2
2
  import { RouteItem } from '../types/global';
3
3
  type RouteProviderProps = {
4
4
  children: ReactNode;
5
- routeList: RouteItem[];
5
+ routes: RouteItem[];
6
6
  context?: Record<string, unknown>;
7
7
  };
8
- export declare const RouterProvider: ({ children, routeList, context: initialContext }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const RouterProvider: ({ children, routes, context: initialContext }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
9
9
  export {};
@@ -1,7 +1,7 @@
1
1
  import { RouterProps } from '../types/global';
2
2
  declare class RouterConfig {
3
3
  isAnimated: boolean;
4
- showFallbackIfAnimated: boolean;
4
+ showFallbackOnAnimation: boolean;
5
5
  prefetch: RouterProps['prefetch'];
6
6
  hoverPrefetchDelay: number;
7
7
  configure(config: Partial<RouterConfig>): void;
@@ -1,7 +1,7 @@
1
1
  import { type Dispatch, RefObject, type SetStateAction } from 'react';
2
2
  import { BlockerState, LoaderState, Location, RevalidateCacheArgs, RouteItem, RouteItemData, UpdateBlockedRouteProps } from '../types/global';
3
3
  type UseHandleNavigation = {
4
- routeList: RouteItem[];
4
+ routes: RouteItem[];
5
5
  context: Record<string, unknown>;
6
6
  revalidateCache(arg: RevalidateCacheArgs): Promise<unknown>;
7
7
  setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
@@ -12,13 +12,13 @@ type UseHandleNavigation = {
12
12
  loaderStateRef: RefObject<LoaderState>;
13
13
  clearTimestamp(path: string): void;
14
14
  };
15
- export declare const useHandleNavigation: ({ routeList, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp, }: UseHandleNavigation) => {
15
+ export declare const useHandleNavigation: ({ routes, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp, }: UseHandleNavigation) => {
16
16
  blockerState: BlockerState;
17
17
  updateLocation: (nextLocation: Location) => Promise<void>;
18
18
  updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
19
19
  routeItemData: RouteItemData;
20
20
  restoreScroll: () => void;
21
- currentLoaderFallback: import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement) | undefined;
21
+ currentLoaderFallback: (import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | undefined;
22
22
  isLoading: boolean;
23
23
  loaderState: LoaderState;
24
24
  invalidate: (pathname?: string) => Promise<void>;
@@ -1,11 +1,11 @@
1
1
  import { type Dispatch, type SetStateAction } from 'react';
2
2
  import type { LoaderState, RevalidateCacheArgs, RouteItem } from '../types/global';
3
3
  type UseLoaderParams = {
4
- routeList: RouteItem[];
4
+ routes: RouteItem[];
5
5
  context: Record<string, unknown>;
6
6
  setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
7
7
  };
8
- export declare const useLoader: ({ routeList, context, setContext }: UseLoaderParams) => {
8
+ export declare const useLoader: ({ routes, context, setContext }: UseLoaderParams) => {
9
9
  prefetchLoader: (pathname: string) => Promise<void>;
10
10
  revalidateCache: ({ routeItem, pathname }: RevalidateCacheArgs) => Promise<unknown>;
11
11
  isCacheItemFresh: ({ routeItem, pathname }: {
package/dist/index.d.ts CHANGED
@@ -14,4 +14,4 @@ export { useSearchParams } from './hooks/useSearchParams';
14
14
  export { useHistoricalTrail } from './hooks/useHistoricalTrail';
15
15
  export { adapter } from './utils/adapter';
16
16
  export { createRouter } from './utils/utils';
17
- export type { RouteItem, BlockerState, Location, AdapterType } from './types/global';
17
+ export type { RouteItem, BlockerState, Location, AdapterType, RouterProps } from './types/global';
package/dist/index.js CHANGED
@@ -125,7 +125,7 @@ function _defineProperty(e, r, t) {
125
125
  var RouterConfig = class {
126
126
  constructor() {
127
127
  _defineProperty(this, "isAnimated", false);
128
- _defineProperty(this, "showFallbackIfAnimated", false);
128
+ _defineProperty(this, "showFallbackOnAnimation", false);
129
129
  _defineProperty(this, "prefetch", "hover");
130
130
  _defineProperty(this, "hoverPrefetchDelay", 150);
131
131
  }
@@ -188,8 +188,8 @@ var comparePaths = (el, pathname) => {
188
188
  //#endregion
189
189
  //#region hooks/useHandleNavigation.ts
190
190
  var ALL_LOCATIONS = "*";
191
- var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp }) => {
192
- const { isAnimated, showFallbackIfAnimated: showFallback } = routerConfig;
191
+ var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp }) => {
192
+ const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
193
193
  const [isLoading, setIsLoading] = useState(false);
194
194
  const [blockedRoute, setBlockedRoute] = useState({
195
195
  from: "",
@@ -240,7 +240,7 @@ var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, is
240
240
  }, [navigation, isAnimated]);
241
241
  const invalidate = useCallback(async (pathname = routeItemData.location.pathname) => {
242
242
  if (typeof pathname !== "string") return;
243
- const routeItem = routeList.find((el) => comparePaths(el, pathname));
243
+ const routeItem = routes.find((el) => comparePaths(el, pathname));
244
244
  const resultParams = getParamsObject({
245
245
  params: routeItem?.params,
246
246
  pathname
@@ -274,14 +274,14 @@ var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, is
274
274
  loaderStateRef,
275
275
  revalidateCache,
276
276
  routeItemData.location.pathname,
277
- routeList,
277
+ routes,
278
278
  setContext
279
279
  ]);
280
280
  const navigationHandler = useCallback(async (nextLocation) => {
281
281
  navigationSeq.current = navigationSeq.current + 1;
282
282
  const seq = navigationSeq.current;
283
283
  loaderStateRef.current = emptyLoaderState;
284
- const nextItem = routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, nextLocation.pathname));
284
+ const nextItem = routes.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, nextLocation.pathname));
285
285
  const params = getParamsObject({
286
286
  params: nextItem?.params,
287
287
  pathname: nextLocation.pathname
@@ -337,7 +337,7 @@ var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, is
337
337
  }, [
338
338
  context,
339
339
  revalidateCache,
340
- routeList,
340
+ routes,
341
341
  transitionedNavigation,
342
342
  setContext,
343
343
  isCacheItemFresh,
@@ -407,7 +407,7 @@ var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, is
407
407
  };
408
408
  //#endregion
409
409
  //#region hooks/useLoader.ts
410
- var useLoader = ({ routeList, context, setContext }) => {
410
+ var useLoader = ({ routes, context, setContext }) => {
411
411
  const timestampMapRef = useRef(/* @__PURE__ */ new Map());
412
412
  const loaderMapRef = useRef({});
413
413
  const loaderStateRef = useRef(emptyLoaderState);
@@ -467,12 +467,12 @@ var useLoader = ({ routeList, context, setContext }) => {
467
467
  ]);
468
468
  return {
469
469
  prefetchLoader: useCallback(async (pathname) => {
470
- const item = routeList.find((el) => comparePaths(el, pathname));
470
+ const item = routes.find((el) => comparePaths(el, pathname));
471
471
  if (item) await revalidateCache({
472
472
  routeItem: item,
473
473
  pathname
474
474
  });
475
- }, [revalidateCache, routeList]),
475
+ }, [revalidateCache, routes]),
476
476
  revalidateCache,
477
477
  isCacheItemFresh,
478
478
  loaderStateRef,
@@ -483,15 +483,15 @@ var useLoader = ({ routeList, context, setContext }) => {
483
483
  };
484
484
  //#endregion
485
485
  //#region components/RouterProvider.tsx
486
- var RouterProvider = ({ children, routeList, context: initialContext = {} }) => {
486
+ var RouterProvider = ({ children, routes, context: initialContext = {} }) => {
487
487
  const [context, setContext] = useState(initialContext);
488
488
  const { prefetchLoader, revalidateCache, isCacheItemFresh, loaderStateRef, clearTimestamp } = useLoader({
489
- routeList,
489
+ routes,
490
490
  context,
491
491
  setContext
492
492
  });
493
493
  const { blockerState, updateLocation, updateBlockedRoute, routeItemData, restoreScroll, currentLoaderFallback, isLoading, loaderState, invalidate } = useHandleNavigation({
494
- routeList,
494
+ routes,
495
495
  context,
496
496
  setContext,
497
497
  revalidateCache,
@@ -590,6 +590,11 @@ var usePreserveScroll = (preserveScroll) => {
590
590
  ]);
591
591
  };
592
592
  //#endregion
593
+ //#region hooks/useSetRouterConfig.ts
594
+ var useSetRouterConfig = (routerProps) => {
595
+ useEffect(() => routerConfig.configure(routerProps), [routerProps]);
596
+ };
597
+ //#endregion
593
598
  //#region components/Spinner.tsx
594
599
  var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cr-spinner" });
595
600
  //#endregion
@@ -599,32 +604,28 @@ var renderElement = (Component) => {
599
604
  return typeof Component === "function" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {}) : Component;
600
605
  };
601
606
  //#endregion
602
- //#region hooks/useSetRouterConfig.ts
603
- var useSetRouterConfig = (routerProps) => {
604
- useEffect(() => routerConfig.configure(routerProps), [routerProps]);
605
- };
606
- //#endregion
607
607
  //#region components/Router.tsx
608
- var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackIfAnimated = false, prefetch = "hover", hoverPrefetchDelay = 150 }) => {
609
- const { routeItemData: { routeItem }, loaderState, currentLoaderFallback, isLoading } = useNavigationState();
608
+ var EmptyBoundary = ({ children }) => children;
609
+ var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary }) => {
610
+ const { routeItemData: { routeItem, location }, loaderState, currentLoaderFallback, isLoading } = useNavigationState();
610
611
  usePreserveScroll(preserveScroll);
611
612
  useApplyCustomAnimation(animationDuration);
612
613
  useSetRouterConfig({
613
614
  isAnimated,
614
- showFallbackIfAnimated,
615
+ showFallbackOnAnimation,
615
616
  prefetch,
616
617
  hoverPrefetchDelay
617
618
  });
618
619
  const showErrorElement = !isLoading && Boolean(loaderState.loaderError || loaderState.beforeLoadError);
619
620
  const showSpinner = spinner && isAnimated && isLoading;
620
621
  const loadingContent = !showErrorElement && isLoading;
621
- if ((showFallbackIfAnimated || !isAnimated) && loadingContent) return renderElement(currentLoaderFallback);
622
- if (!showFallbackIfAnimated && isAnimated && loadingContent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {});
622
+ if ((showFallbackOnAnimation || !isAnimated) && loadingContent) return renderElement(currentLoaderFallback);
623
+ if (!showFallbackOnAnimation && isAnimated && loadingContent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {});
623
624
  if (!routeItem) return null;
624
625
  if (showErrorElement) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [renderElement(routeItem.errorElement), showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})] });
625
626
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
626
627
  style: { viewTransitionName: "page" },
627
- children: [renderElement(routeItem.element) || null, showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
628
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBoundary, { children: renderElement(routeItem.element) }, location.pathname), showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
628
629
  });
629
630
  };
630
631
  //#endregion
@@ -710,11 +711,11 @@ var Link = ({ children, to, prefetch: prefetchLink, hoverPrefetchDelay }) => {
710
711
  //#endregion
711
712
  //#region hooks/useParams.ts
712
713
  var useParams = () => {
713
- const { routeItemData: { routeItem } } = useNavigationState();
714
+ const { routeItemData: { routeItem, location: { pathname } } } = useNavigationState();
714
715
  if (!routeItem) return void 0;
715
716
  return getParamsObject({
716
717
  params: routeItem?.params,
717
- pathname: location.pathname
718
+ pathname
718
719
  });
719
720
  };
720
721
  //#endregion
@@ -1,18 +1,19 @@
1
- import type { ComponentType, Dispatch, ReactElement, SetStateAction } from 'react';
1
+ import type { ComponentType, Dispatch, ReactElement, ReactNode, SetStateAction } from 'react';
2
2
  export type LazyComponent = () => Promise<{
3
3
  default: ComponentType<unknown>;
4
4
  }>;
5
+ type Element = (() => ReactElement) | ReactElement;
5
6
  export type ClientRouteItem = {
6
7
  path: string;
7
- element: (() => ReactElement) | ReactElement | LazyComponent;
8
+ element: Element | LazyComponent;
8
9
  loader?(arg: {
9
10
  params: Record<string, string>;
10
11
  context: Record<string, unknown>;
11
12
  setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
12
13
  }): Promise<unknown>;
13
- loaderFallback?: (() => ReactElement) | ReactElement;
14
- errorElement?: (() => ReactElement) | ReactElement;
15
- fallback?: (() => ReactElement) | ReactElement;
14
+ loaderFallback?: Element;
15
+ errorElement?: Element;
16
+ fallback?: Element;
16
17
  children?: ClientRouteItem[];
17
18
  staleTime?: number;
18
19
  beforeLoad?: (arg: {
@@ -28,7 +29,7 @@ export type ClientRouteItem = {
28
29
  }) => Promise<void>;
29
30
  };
30
31
  export type RouteItem = ClientRouteItem & {
31
- element: (() => ReactElement) | ReactElement;
32
+ element: Element;
32
33
  params?: {
33
34
  key: string;
34
35
  value: string;
@@ -67,7 +68,11 @@ export type RouterProps = {
67
68
  animationDuration?: number;
68
69
  spinner?: boolean;
69
70
  preserveScroll?: boolean;
70
- showFallbackIfAnimated?: boolean;
71
+ showFallbackOnAnimation?: boolean;
71
72
  prefetch?: 'hover' | 'render' | 'viewport' | 'none';
72
73
  hoverPrefetchDelay?: number;
74
+ errorBoundary?: ComponentType<{
75
+ children: ReactNode;
76
+ }>;
73
77
  };
78
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-react-router",
3
- "version": "1.6.3",
3
+ "version": "1.6.5",
4
4
  "description": "A lightweight, type-safe routing library for React applications",
5
5
  "author": "Andrew Bubnov",
6
6
  "scripts": {