@aminnairi/react-router 2.2.0 → 3.0.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
@@ -2,6 +2,42 @@
2
2
 
3
3
  Type-safe router for the React library
4
4
 
5
+ ## Documentation
6
+
7
+ - [Requirements](#requirements)
8
+ - [Usage](#usage)
9
+ - [Project initialization](#project-initialization)
10
+ - [Dependencies installation](#dependencies-installation)
11
+ - [Library installation](#library-installation)
12
+ - [Setup](#setup)
13
+ - [Startup](#startup)
14
+ - [API](#api)
15
+ - [createPage](#createpage)
16
+ - [useNavigateToPage](#usenavigatetopage)
17
+ - [createRouter](#createrouter)
18
+ - [useIsActivePage](#useisactivepage)
19
+ - [useLocale](#uselocale)
20
+ - [usePrefix](#useprefix)
21
+ - [usePath](#usepath)
22
+ - [Features](#features)
23
+ - [TypeScript](#typescript)
24
+ - [No codegen](#no-codegen)
25
+ - [Simplicity](#simplicity)
26
+ - [Transition](#transition)
27
+ - [Error handling](#error-handling)
28
+ - [License](#license)
29
+ - [Changelogs](#changelogs)
30
+ - [Versions](#versions)
31
+ - [3.0.0](#300)
32
+ - [2.1.0](#210)
33
+ - [2.0.1](#201)
34
+ - [2.0.0](#200)
35
+ - [1.1.0](#110)
36
+ - [1.0.1](#101)
37
+ - [1.0.0](#100)
38
+ - [0.1.1](#011)
39
+ - [0.1.0](#010)
40
+
5
41
  ## Requirements
6
42
 
7
43
  - [Node](https://nodejs.org/)
@@ -52,13 +88,13 @@ touch src/router/fallback.tsx
52
88
  ```
53
89
 
54
90
  ```tsx
55
- import { useNavigateToPage } from "@aminnairi/react-router";
91
+ import { useNavigateToPage } from ".";
56
92
  import { home } from "./pages/home";
57
93
 
58
94
  export const Fallback = () => {
59
95
  const navigateToHomePage = useNavigateToPage(home);
60
96
 
61
- return <button onClick={() => navigateToHomePage({})}>Go back home</button>;
97
+ return <button onClick={navigateToHomePage}>Go back home</button>;
62
98
  };
63
99
  ```
64
100
 
@@ -68,7 +104,7 @@ touch src/router/issue.tsx
68
104
 
69
105
  ```tsx
70
106
  import { Fragment } from "react";
71
- import { useNavigateToPage } from "@aminnairi/react-router";
107
+ import { useNavigateToPage } from ".";
72
108
  import { home } from "./pages/home";
73
109
 
74
110
  export const Issue = () => {
@@ -77,7 +113,7 @@ export const Issue = () => {
77
113
  return (
78
114
  <Fragment>
79
115
  <h1>An issue occurred</h1>
80
- <button onClick={() => navigateToHomePage({})}>Go back home</button>
116
+ <button onClick={navigateToHomePage}>Go back home</button>
81
117
  </Fragment>
82
118
  );
83
119
  };
@@ -93,7 +129,7 @@ import { Fallback } from "./fallback";
93
129
  import { Issue } from "./issue";
94
130
  import { home } from "./pages/home";
95
131
 
96
- export const router = createRouter({
132
+ export const { RouterProvider, RouterView, useNavigateToPage } = createRouter({
97
133
  fallback: Fallback,
98
134
  issue: Issue,
99
135
  pages: [home],
@@ -105,10 +141,10 @@ touch src/App.tsx
105
141
  ```
106
142
 
107
143
  ```tsx
108
- import { router } from "./router";
144
+ import { RouterView } from "./router";
109
145
 
110
146
  export default function App() {
111
- return <router.View />;
147
+ return <RouterView />;
112
148
  }
113
149
  ```
114
150
 
@@ -119,7 +155,7 @@ touch src/main.tsx
119
155
  ```tsx
120
156
  import { StrictMode } from "react";
121
157
  import { createRoot } from "react-dom/client";
122
- import { router } from "./router";
158
+ import { RouterProvider } from "./router";
123
159
  import App from "./App";
124
160
 
125
161
  const rootElement = document.getElementById("root");
@@ -130,9 +166,9 @@ if (!rootElement) {
130
166
 
131
167
  createRoot(rootElement).render(
132
168
  <StrictMode>
133
- <router.Provider>
169
+ <RouterProvider>
134
170
  <App />
135
- </router.Provider>
171
+ </RouterProvider>
136
172
  </StrictMode>,
137
173
  );
138
174
  ```
@@ -213,9 +249,11 @@ createPage({
213
249
 
214
250
  You can navigate from one page from another.
215
251
 
252
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
253
+
216
254
  ```tsx
217
255
  import { Fragment } from "react";
218
- import { createPage, useNavigateToPage } from "@aminnairi/react-router";
256
+ import { createPage, createRouter } from "@aminnairi/react-router";
219
257
 
220
258
  const login = createPage({
221
259
  path: "/login",
@@ -232,12 +270,18 @@ const about = createPage({
232
270
  return (
233
271
  <Fragment>
234
272
  <h1>About Us</h1>
235
- <button onClick={() => navigateToLoginPage({})}>Login</button>
273
+ <button onClick={navigateToLoginPage}>Login</button>
236
274
  </Fragment>
237
275
  );
238
276
  },
239
277
  });
240
278
 
279
+ const { useNavigateToPage } = createRouter({
280
+ fallback: () => <h1>Not found</h1>,
281
+ issue: () => <h1>An error occurred</h1>,
282
+ pages: [login, about],
283
+ });
284
+
241
285
  createPage({
242
286
  path: "/",
243
287
  element: function Home() {
@@ -246,7 +290,7 @@ createPage({
246
290
  return (
247
291
  <Fragment>
248
292
  <h1>Home</h1>
249
- <button onClick={() => navigateToAboutPage({})}>About Us</button>
293
+ <button onClick={navigateToAboutPage}>About Us</button>
250
294
  </Fragment>
251
295
  );
252
296
  },
@@ -257,7 +301,7 @@ And you can of course navigate to pages that have dynamic parameters as well.
257
301
 
258
302
  ```tsx
259
303
  import { Fragment } from "react";
260
- import { createPage, useNavigateToPage } from "@aminnairi/react-router";
304
+ import { createPage, createRouter } from "@aminnairi/react-router";
261
305
 
262
306
  const user = createPage({
263
307
  path: "/users/:user",
@@ -266,6 +310,12 @@ const user = createPage({
266
310
  },
267
311
  });
268
312
 
313
+ const { useNavigateToPage } = createRouter({
314
+ fallback: () => <h1>Not found</h1>,
315
+ issue: () => <h1>An error occurred</h1>,
316
+ pages: [user],
317
+ });
318
+
269
319
  createPage({
270
320
  path: "/",
271
321
  element: function Home() {
@@ -299,7 +349,7 @@ const home = createPage({
299
349
  },
300
350
  });
301
351
 
302
- const router = createRouter({
352
+ const { RouterProvider, RouterView } = createRouter({
303
353
  fallback: () => <h1>Not found</h1>,
304
354
  issue: () => <h1>An error occurred</h1>,
305
355
  pages: [home],
@@ -320,7 +370,7 @@ const App = () => {
320
370
  <h1>App</h1>
321
371
  </header>
322
372
  <main>
323
- <router.View />
373
+ <RouterView />
324
374
  </main>
325
375
  <footer>Credit © Yourself 2025</footer>
326
376
  </Fragment>
@@ -329,18 +379,18 @@ const App = () => {
329
379
 
330
380
  root.render(
331
381
  <StrictMode>
332
- <router.Provider>
382
+ <RouterProvider>
333
383
  <App />
334
- </router.Provider>
384
+ </RouterProvider>
335
385
  </StrictMode>,
336
386
  );
337
387
  ```
338
388
 
339
389
  You can also activate the View Transition Web API if you want before each page renders. This is nice because by default, the browser already has some styling that allows for a smooth and simple transition between pages.
340
390
 
341
- All you have to do is to provide a `transition` function in the arguments of the `createRouter` function. This function receives the navigation direction (`"pushstate"` or `"popstate"`) and a `next` callback to render the next page.
391
+ All you have to do is to provide a `transition` function in the arguments of the `createRouter` function. This function receives the navigation direction (`"forward"` or `"backward"`) and a `next` callback to render the next page.
342
392
 
343
- This library also exports a `slideFadeTransition` that you can use out-of-the-box.
393
+ This library also exports several transitions that you can use out-of-the-box: `slideHorizontalTransition`, `slideVerticalTransition`, `crossFadeTransition`, and `scaleFadeTransition`.
344
394
 
345
395
  ```tsx
346
396
  import { Fragment, StrictMode } from "react";
@@ -348,7 +398,7 @@ import { createRoot } from "react-dom/client";
348
398
  import {
349
399
  createRouter,
350
400
  createPage,
351
- slideFadeTransition,
401
+ slideHorizontalTransition,
352
402
  } from "@aminnairi/react-router";
353
403
 
354
404
  const home = createPage({
@@ -358,8 +408,8 @@ const home = createPage({
358
408
  },
359
409
  });
360
410
 
361
- const router = createRouter({
362
- transition: slideFadeTransition,
411
+ const { RouterProvider, RouterView } = createRouter({
412
+ transition: slideHorizontalTransition,
363
413
  fallback: () => <h1>Not found</h1>,
364
414
  issue: () => <h1>An error occurred</h1>,
365
415
  pages: [home],
@@ -380,7 +430,7 @@ const App = () => {
380
430
  <h1>App</h1>
381
431
  </header>
382
432
  <main>
383
- <router.View />
433
+ <RouterView />
384
434
  </main>
385
435
  <footer>Credit © Yourself 2025</footer>
386
436
  </Fragment>
@@ -389,9 +439,9 @@ const App = () => {
389
439
 
390
440
  root.render(
391
441
  <StrictMode>
392
- <router.Provider>
442
+ <RouterProvider>
393
443
  <App />
394
- </router.Provider>
444
+ </RouterProvider>
395
445
  </StrictMode>,
396
446
  );
397
447
  ```
@@ -410,13 +460,13 @@ const home = createPage({
410
460
  },
411
461
  });
412
462
 
413
- const router = createRouter({
463
+ const { RouterProvider, RouterView } = createRouter({
414
464
  fallback: () => <h1>Not found</h1>,
415
- issue: ({ error, reset }) => (
465
+ issue: ({ error, resetError }) => (
416
466
  <Fragment>
417
467
  <h1>Error</h1>
418
468
  <p>{error.message}</p>
419
- <button onClick={reset}>Reset</button>
469
+ <button onClick={resetError}>Reset</button>
420
470
  </Fragment>
421
471
  ),
422
472
  pages: [home],
@@ -437,7 +487,7 @@ const App = () => {
437
487
  <h1>App</h1>
438
488
  </header>
439
489
  <main>
440
- <router.View />
490
+ <RouterView />
441
491
  </main>
442
492
  <footer>Credit © Yourself 2025</footer>
443
493
  </Fragment>
@@ -446,19 +496,19 @@ const App = () => {
446
496
 
447
497
  root.render(
448
498
  <StrictMode>
449
- <router.Provider>
499
+ <RouterProvider>
450
500
  <App />
451
- </router.Provider>
501
+ </RouterProvider>
452
502
  </StrictMode>,
453
503
  );
454
504
  ```
455
505
 
456
- You can also define this function from the outside by using the `createIssue` function.
506
+ You can also define the issue component from the outside.
457
507
 
458
508
  ```tsx
459
509
  import { Fragment, StrictMode } from "react";
460
510
  import { createRoot } from "react-dom/client";
461
- import { createRouter, createPage, createIssue } from "@aminnairi/react-router";
511
+ import { createRouter, createPage, IssueProps } from "@aminnairi/react-router";
462
512
 
463
513
  const home = createPage({
464
514
  path: "/",
@@ -471,15 +521,15 @@ const Fallback = () => {
471
521
  return <h1>Not found</h1>;
472
522
  };
473
523
 
474
- const Issue = createIssue(({ error, reset }) => (
524
+ const Issue = ({ error, resetError }: IssueProps) => (
475
525
  <Fragment>
476
526
  <h1>Error</h1>
477
527
  <p>{error.message}</p>
478
- <button onClick={reset}>Reset</button>
528
+ <button onClick={resetError}>Reset</button>
479
529
  </Fragment>
480
- ));
530
+ );
481
531
 
482
- const router = createRouter({
532
+ const { RouterProvider, RouterView } = createRouter({
483
533
  fallback: Fallback,
484
534
  issue: Issue,
485
535
  pages: [home],
@@ -500,7 +550,7 @@ const App = () => {
500
550
  <h1>App</h1>
501
551
  </header>
502
552
  <main>
503
- <router.View />
553
+ <RouterView />
504
554
  </main>
505
555
  <footer>Credit © Yourself 2025</footer>
506
556
  </Fragment>
@@ -509,9 +559,9 @@ const App = () => {
509
559
 
510
560
  root.render(
511
561
  <StrictMode>
512
- <router.Provider>
562
+ <RouterProvider>
513
563
  <App />
514
- </router.Provider>
564
+ </RouterProvider>
515
565
  </StrictMode>,
516
566
  );
517
567
  ```
@@ -526,8 +576,7 @@ import { createRoot } from "react-dom/client";
526
576
  import {
527
577
  createRouter,
528
578
  createPage,
529
- createIssue,
530
- useNavigateToPage,
579
+ IssueProps,
531
580
  } from "@aminnairi/react-router";
532
581
 
533
582
  const home = createPage({
@@ -543,20 +592,20 @@ const Fallback = () => {
543
592
  return (
544
593
  <Fragment>
545
594
  <h1>Not found</h1>
546
- <button onClick={() => navigateToHomePage({})}>Go Back Home</button>
595
+ <button onClick={navigateToHomePage}>Go Back Home</button>
547
596
  </Fragment>
548
597
  );
549
598
  };
550
599
 
551
- const Issue = createIssue(({ error, reset }) => (
600
+ const Issue = ({ error, resetError }: IssueProps) => (
552
601
  <Fragment>
553
602
  <h1>Error</h1>
554
603
  <p>{error.message}</p>
555
- <button onClick={reset}>Reset</button>
604
+ <button onClick={resetError}>Reset</button>
556
605
  </Fragment>
557
- ));
606
+ );
558
607
 
559
- const router = createRouter({
608
+ const { RouterProvider, RouterView, useNavigateToPage } = createRouter({
560
609
  prefix: "/portfolio",
561
610
  fallback: Fallback,
562
611
  issue: Issue,
@@ -578,7 +627,7 @@ const App = () => {
578
627
  <h1>App</h1>
579
628
  </header>
580
629
  <main>
581
- <router.View />
630
+ <RouterView />
582
631
  </main>
583
632
  <footer>Credit © Yourself 2025</footer>
584
633
  </Fragment>
@@ -587,9 +636,9 @@ const App = () => {
587
636
 
588
637
  root.render(
589
638
  <StrictMode>
590
- <router.Provider>
639
+ <RouterProvider>
591
640
  <App />
592
- </router.Provider>
641
+ </RouterProvider>
593
642
  </StrictMode>,
594
643
  );
595
644
  ```
@@ -600,9 +649,11 @@ Allow you to create a function that can then be called to navigate to another pa
600
649
 
601
650
  It accepts a page that has been created using `createPage`.
602
651
 
652
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
653
+
603
654
  ```tsx
604
655
  import { Fragment } from "react";
605
- import { createPage, useNavigateToPage } from "@aminnairi/react-router";
656
+ import { createPage, createRouter } from "@aminnairi/react-router";
606
657
 
607
658
  const home = createPage({
608
659
  path: "/",
@@ -611,6 +662,12 @@ const home = createPage({
611
662
  },
612
663
  });
613
664
 
665
+ const { useNavigateToPage } = createRouter({
666
+ fallback: () => <h1>Not found</h1>,
667
+ issue: () => <h1>An error occurred</h1>,
668
+ pages: [home],
669
+ });
670
+
614
671
  createPage({
615
672
  path: "/about",
616
673
  element: function About() {
@@ -619,7 +676,7 @@ createPage({
619
676
  return (
620
677
  <Fragment>
621
678
  <h1>About</h1>
622
- <button onClick={() => navigateToHomePage({})}>Home</button>
679
+ <button onClick={navigateToHomePage}>Home</button>
623
680
  </Fragment>
624
681
  );
625
682
  },
@@ -632,7 +689,7 @@ The parameters should always be provided as string, as they are the only data ty
632
689
 
633
690
  ```tsx
634
691
  import { Fragment } from "react";
635
- import { createPage, useNavigateToPage } from "@aminnairi/react-router";
692
+ import { createPage, createRouter } from "@aminnairi/react-router";
636
693
 
637
694
  const user = createPage({
638
695
  path: "/users/:user",
@@ -658,200 +715,190 @@ createPage({
658
715
  });
659
716
  ```
660
717
 
661
- ### useLink
718
+ ### useIsActivePage
662
719
 
663
- Allow you to navigate to another page using a JSX component instead of a callback as for the `useNavigateToPage` hook.
720
+ Allow you to check if a page is currently active.
664
721
 
665
- The created component is simply a `<a href="...">{children}</a>` under the hood which prevents the default behavior of the navigator which is to create a new HTTP request and to reload the page. The `href` attribute is computed from the page path and its parameters.
722
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
666
723
 
667
724
  ```tsx
668
- import { Fragment } from "react";
669
- import { createPage, useLink } from "@aminnairi/react-router";
725
+ // router/index.ts
726
+ import { createPage, createRouter } from "@aminnairi/react-router";
670
727
 
671
- const user = createPage({
672
- path: "/users/:user",
673
- element: function User({ parameters: { user } }) {
674
- return <h1>User#{user}</h1>;
728
+ export const home = createPage({
729
+ path: "/",
730
+ element: function Home() {
731
+ return <h1>Home</h1>;
675
732
  },
676
733
  });
677
734
 
678
- createPage({
735
+ export const about = createPage({
679
736
  path: "/about",
680
737
  element: function About() {
681
- const Link = useLink(user);
682
-
683
- return (
684
- <Fragment>
685
- <h1>About</h1>
686
- <Link parameters={{ user: "123" }}>User#123</Link>
687
- </Fragment>
688
- );
738
+ return <h1>About</h1>;
689
739
  },
690
740
  });
691
- ```
692
741
 
693
- You can also provide a custom render function as the second argument to fully control the rendered output. This is useful when you want to use a different component library or need more control over the markup.
742
+ export const { useIsActivePage } = createRouter({
743
+ fallback: () => <h1>Not found</h1>,
744
+ issue: () => <h1>An error occurred</h1>,
745
+ pages: [home, about],
746
+ });
747
+ ```
694
748
 
695
749
  ```tsx
750
+ // components/layout.tsx
696
751
  import { Fragment } from "react";
697
- import { createPage, useLink, UseLinkRenderFunction } from "@aminnairi/react-router";
752
+ import { useIsActivePage, useNavigateToPage } from "../router";
753
+ import { home, about } from "../router";
698
754
 
699
- const user = createPage({
700
- path: "/users/:user",
701
- element: function User({ parameters: { user } }) {
702
- return <h1>User#{user}</h1>;
703
- },
704
- });
755
+ export default function Layout() {
756
+ const isHomeActive = useIsActivePage(home);
757
+ const isAboutActive = useIsActivePage(about);
705
758
 
706
- createPage({
707
- path: "/about",
708
- element: function About() {
709
- const CustomLink = useLink(user, ({ path, onClick, children }) => {
710
- return (
711
- <button className="nav-button" onClick={onClick}>
712
- {children}
759
+ return (
760
+ <Fragment>
761
+ <nav>
762
+ <button style={{ fontWeight: isHomeActive ? "bold" : "normal" }}>
763
+ Home
713
764
  </button>
714
- );
715
- });
716
-
717
- return (
718
- <Fragment>
719
- <h1>About</h1>
720
- <CustomLink parameters={{ user: "123" }}>User#123</CustomLink>
721
- </Fragment>
722
- );
723
- },
724
- });
765
+ <button style={{ fontWeight: isAboutActive ? "bold" : "normal" }}>
766
+ About
767
+ </button>
768
+ </nav>
769
+ </Fragment>
770
+ );
771
+ }
725
772
  ```
726
773
 
727
- The render function receives an object with the following properties:
728
- - `path`: The computed URL path with parameters filled in
729
- - `onClick`: The click event handler that prevents default navigation and handles routing
730
- - `children`: The children passed to the link component
731
-
732
- ### useSearch
774
+ ### useLocale
733
775
 
734
- Allow you to get one or more search query from the URL.
776
+ Allow you to get and set the current locale for internationalization.
735
777
 
736
- This will return an instance of the `URLSearchParams` Web API so that you can use you existing knowledge to manipulate the search queries easily.
778
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
737
779
 
738
780
  ```tsx
739
- import { useMemo } from "react";
740
- import { createPage, useSearch } from "@aminnairi/react-router";
781
+ // router/index.ts
782
+ import { createPage, createRouter } from "@aminnairi/react-router";
741
783
 
742
- createPage({
743
- path: "/users",
784
+ export const home = createPage({
785
+ path: "/",
744
786
  element: function Home() {
745
- const search = useSearch();
746
- const sortedByDate = useMemo(() => search.get("sort-by") === "date", [search]);
747
-
748
- return (
749
- <h1>Users</h1>
750
- <p>Sorted by date: {sortedByDate ? "yes" : "no"}</p>
751
- );
752
- }
787
+ return <h1>Home</h1>;
788
+ },
753
789
  });
754
- ```
755
-
756
- You cannot set the search queries for now, this will be added in future release of this library.
757
-
758
- ### useHash
759
-
760
- Allow you to get the hash, also called fragment, from the URL which is everything after the `#` symbol.
761
790
 
762
- ```tsx
763
- import { createPage, useHash } from "@aminnairi/react-router";
764
-
765
- createPage({
766
- path: "/oauth/callback",
767
- element: function OauthCallback() {
768
- const token = useHash();
769
-
770
- return <h1>You token is {token}</h1>;
771
- },
791
+ export const { RouterProvider, RouterView, useLocale } = createRouter({
792
+ locales: ["en", "fr"],
793
+ fallback: () => <h1>Not found</h1>,
794
+ issue: () => <h1>An error occurred</h1>,
795
+ pages: [home],
772
796
  });
773
797
  ```
774
798
 
775
- ## Internal API
776
-
777
- ### doesRouteMatchPath
778
-
779
- Return a boolean in case a route matches a path. A route is a URI that looks something like `/users/:user/articles` and a path is the browser's location pathname that looks something like `/users/123/articles`.
780
-
781
- This function is mainly used in the internals of the `createRouter` and in most case should not be necessary.
782
-
783
- ```typescript
784
- import { doesRouteMatchPath } from "@aminnairi/react-router";
785
-
786
- doesRouteMatchPath("/", "/"); // true
787
-
788
- doesRouteMatchPath("/", "/about"); // false
799
+ ```tsx
800
+ // components/layout.tsx
801
+ import { Fragment } from "react";
802
+ import { useLocale } from "../router";
789
803
 
790
- doesRouteMatchPath("/users/:user", "/users/123"); // true
804
+ export default function Layout() {
805
+ const { locale, setLocale } = useLocale();
791
806
 
792
- doesRouteMatchPath("/users/:user", "/users/123/articles"); // false
807
+ return (
808
+ <Fragment>
809
+ <nav>
810
+ <p>Current locale: {locale ?? "none"}</p>
811
+ <button onClick={() => setLocale("en")}>English</button>
812
+ <button onClick={() => setLocale("fr")}>Français</button>
813
+ </nav>
814
+ </Fragment>
815
+ );
816
+ }
793
817
  ```
794
818
 
795
- You can also optionally provide a prefix.
819
+ ### usePrefix
796
820
 
797
- ```typescript
798
- import { doesRouteMatchPath } from "@aminnairi/react-router";
821
+ Allow you to get the current route prefix.
799
822
 
800
- doesRouteMatchPath("/", "/github", "/github"); // true
823
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
801
824
 
802
- doesRouteMatchPath("/", "/github/about", "/github"); // false
825
+ ```tsx
826
+ // router/index.ts
827
+ import { createPage, createRouter } from "@aminnairi/react-router";
803
828
 
804
- doesRouteMatchPath("/users/:user", "/github/users/123", "/github"); // true
829
+ export const home = createPage({
830
+ path: "/",
831
+ element: function Home() {
832
+ return <h1>Home</h1>;
833
+ },
834
+ });
805
835
 
806
- doesRouteMatchPath("/users/:user", "/github/users/123/articles", "/github"); // false
836
+ export const { RouterProvider, RouterView, usePrefix } = createRouter({
837
+ prefix: "/portfolio",
838
+ fallback: () => <h1>Not found</h1>,
839
+ issue: () => <h1>An error occurred</h1>,
840
+ pages: [home],
841
+ });
807
842
  ```
808
843
 
809
- ### getParameters
810
-
811
- Return an object in case a route matches a path, with its dynamic parameters as output. It returns a generic `object` type in case no dynamic parameters are found in the URI. Note that the parameters are always strings, if you need to, convert them to other types explicitely.
812
-
813
- This function is mainly used in the internals of the `createRouter` and in most case should not be necessary.
814
-
815
- ```typescript
816
- import { getParameters } from "@aminnairi/react-router";
817
-
818
- getParameters("/", "/"); // {}
819
-
820
- getParameters("/", "/about"); // {}
844
+ ```tsx
845
+ // components/layout.tsx
846
+ import { Fragment } from "react";
847
+ import { usePrefix } from "../router";
821
848
 
822
- getParameters("/users/:user", "/users/123"); // { user: "123" }
849
+ export default function Layout() {
850
+ const { prefix } = usePrefix();
823
851
 
824
- getParameters("/users/:user", "/users/123/articles"); // {}
852
+ return (
853
+ <Fragment>
854
+ <nav>
855
+ <p>Current prefix: {prefix ?? "none"}</p>
856
+ </nav>
857
+ </Fragment>
858
+ );
859
+ }
825
860
  ```
826
861
 
827
- You can also provide an optional prefix.
862
+ ### usePath
828
863
 
829
- ```typescript
830
- import { getParameters } from "@aminnairi/react-router";
864
+ Allow you to get the current path.
831
865
 
832
- getParameters("/", "/github", "/github"); // {}
866
+ Note: This hook is returned from `createRouter`, not imported directly from the library.
833
867
 
834
- getParameters("/", "/github/about", "/github"); // {}
868
+ ```tsx
869
+ // router/index.ts
870
+ import { createPage, createRouter } from "@aminnairi/react-router";
835
871
 
836
- getParameters("/users/:user", "/github/users/123", "/github"); // { user: "123" }
872
+ export const home = createPage({
873
+ path: "/",
874
+ element: function Home() {
875
+ return <h1>Home</h1>;
876
+ },
877
+ });
837
878
 
838
- getParameters("/users/:user", "/github/users/123/articles", "/github"); // {}
879
+ export const { RouterProvider, RouterView, usePath } = createRouter({
880
+ fallback: () => <h1>Not found</h1>,
881
+ issue: () => <h1>An error occurred</h1>,
882
+ pages: [home],
883
+ });
839
884
  ```
840
885
 
841
- ### sanitizePath
842
-
843
- Internal function that helps normalizing the URL by removing trailing and leading slashes as well as removing any duplicate and unecessary slashes.
844
-
845
- ```ts
846
- import { sanitizePath } from "@aminnairi/react-router";
847
-
848
- sanitizePath("/"); // "/"
849
-
850
- sanitizePath("users"); // "/users"
886
+ ```tsx
887
+ // components/layout.tsx
888
+ import { Fragment } from "react";
889
+ import { usePath } from "../router";
851
890
 
852
- sanitizePath("users/"); // "/users"
891
+ export default function Layout() {
892
+ const { path } = usePath();
853
893
 
854
- sanitizePath("users//123///articles"); // "/users/123/articles"
894
+ return (
895
+ <Fragment>
896
+ <nav>
897
+ <p>Current path: {path}</p>
898
+ </nav>
899
+ </Fragment>
900
+ );
901
+ }
855
902
  ```
856
903
 
857
904
  ## Features
@@ -874,7 +921,7 @@ This means that you can use this library with other popular solutions for handli
874
921
 
875
922
  ### Transition
876
923
 
877
- Support for the View Transition API is built-in and allows for painless and smooth view transition out-of-the-box. You can create your own transition animation, and the library also exports a `slideFadeTransition` ready to be used.
924
+ Support for the View Transition API is built-in and allows for painless and smooth view transition out-of-the-box. You can create your own transition animation, and the library also exports several transitions ready to be used: `slideHorizontalTransition`, `slideVerticalTransition`, `crossFadeTransition`, and `scaleFadeTransition`.
878
925
 
879
926
  ### Error handling
880
927
 
@@ -888,6 +935,8 @@ See [`LICENSE`](./LICENSE).
888
935
 
889
936
  ### Versions
890
937
 
938
+ - [`3.0.1`](#301)
939
+ - [`3.0.0`](#300)
891
940
  - [`2.1.0`](#210)
892
941
  - [`2.0.1`](#201)
893
942
  - [`2.0.0`](#200)
@@ -897,6 +946,47 @@ See [`LICENSE`](./LICENSE).
897
946
  - [`0.1.1`](#011)
898
947
  - [`0.1.0`](#010)
899
948
 
949
+ ### 3.0.1
950
+
951
+ #### Major changes
952
+
953
+ None.
954
+
955
+ #### Minor changes
956
+
957
+ None.
958
+
959
+ #### Bug & security fixes
960
+
961
+ - Fixed incorrect imports in documentation - hooks like `useNavigateToPage`, `useIsActivePage`, `useLocale`, `usePrefix`, and `usePath` are returned from `createRouter` and should not be imported directly from the package
962
+ - Fixed file structure in documentation setup guide to properly separate files and avoid duplicate code definitions
963
+ - Updated issue component examples to use exported `IssueProps` type instead of inline type definitions
964
+
965
+ ### 3.0.0
966
+
967
+ #### Major changes
968
+
969
+ - Full rewrite of the library implementation
970
+ - Added `locales` support for internationalization
971
+ - Added `useLocale` hook for locale management
972
+ - Added `usePrefix` and `usePath` hooks
973
+ - Renamed `slideFadeTransition` to `slideHorizontalTransition` and added new transitions: `scaleFadeTransition`, `crossFadeTransition`, `slideVerticalTransition`
974
+ - Changed API structure: `router.View` → `router.RouterView` and `router.Provider` → `RouterProvider`
975
+ - Renamed `reset` to `resetError` in `IssueProps`
976
+ - Renamed internal functions: `doesRouteMatchPath` → `matchPath`, `getParameters` → `matchParameters`
977
+ - Added `Uri` class for URL parsing
978
+ - Changed `createRouter` return value structure
979
+ - Removed `useLink`, `useSearch`, and `useHash` hooks and `UseLinkRenderFunction` type
980
+
981
+ #### Minor changes
982
+
983
+ - Added better URL normalization with `normalize` function
984
+ - Improved error handling with `ErrorBoundary` component
985
+
986
+ #### Bug & security fixes
987
+
988
+ - None.
989
+
900
990
  ### 2.1.0
901
991
 
902
992
  #### Major changes