├── .github
└── workflows
│ ├── node.js.yml
│ └── size.yml
├── .gitignore
├── .prettierignore
├── LICENSE
├── README.md
├── index.d.ts
├── mangle.json
├── match
├── index.d.ts
├── package.json
└── src
│ ├── cjs.js
│ └── index.js
├── package-lock.json
├── package.json
├── src
├── cjs.js
├── index.js
└── util.js
└── test
├── .babelrc
├── custom-history.test.js
├── dist.test.js
├── dom.test.js
├── index.test.js
├── match.tsx
├── router.tsx
├── tsconfig.json
├── util.test.js
└── utils
└── assert-clone-of.js
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - '**'
7 | push:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | build_test:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/setup-node@v1
17 | with:
18 | node-version: '14.x'
19 | - uses: actions/cache@v2
20 | with:
21 | path: ~/.npm
22 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
23 | restore-keys: |
24 | ${{ runner.os }}-node-
25 | - run: npm ci
26 | - run: npm run test
27 |
--------------------------------------------------------------------------------
/.github/workflows/size.yml:
--------------------------------------------------------------------------------
1 | name: Compressed Size
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - uses: preactjs/compressed-size-action@v2
13 | with:
14 | pattern: "./{dist,match}/*.{js,mjs}"
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /coverage
3 | node_modules
4 | /npm-debug.log
5 | .DS_Store
6 | /match/*.js
7 | /match/*.mjs
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.md
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jason Miller
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # preact-router
2 |
3 | [](https://www.npmjs.com/package/preact-router)
4 | [](https://github.com/preactjs/preact-router/actions/workflows/node.js.yml)
5 |
6 | > [!WARNING]
7 | > `preact-router` unfortunately no longer sees active development! It's completely stable and so you can rely upon it for all existing apps, but for newer ones, we'd recommend using [`preact-iso`](https://github.com/preactjs/preact-iso) for your routing needs instead. It offers a very similar API while integrating a bit better Suspense and lazy loading, with potentially more useful hooks. Thanks to all the contributors and users over the years!
8 |
9 | Connect your [Preact](https://github.com/preactjs/preact) components up to that address bar.
10 |
11 | `preact-router` provides a `` component that conditionally renders its children when the URL matches their `path`. It also automatically wires up `` elements to the router.
12 |
13 | > 💁 **Note:** This is not a preact-compatible version of React Router. `preact-router` is a simple URL wiring and does no orchestration for you.
14 | >
15 | > If you're looking for more complex solutions like nested routes and view composition, [react-router](https://github.com/ReactTraining/react-router) works great with preact as long as you alias in [preact/compat](https://preactjs.com/guide/v10/getting-started#aliasing-react-to-preact).
16 |
17 | #### [See a Real-world Example :arrow_right:](https://jsfiddle.net/developit/qc73v9va/)
18 |
19 | ---
20 |
21 | ### Usage Example
22 |
23 | ```js
24 | import Router from 'preact-router';
25 | import { h, render } from 'preact';
26 | /** @jsx h */
27 |
28 | const Main = () => (
29 |
30 |
31 |
32 | // Advanced is an optional query
33 |
34 |
35 | );
36 |
37 | render(, document.body);
38 | ```
39 |
40 | If there is an error rendering the destination route, a 404 will be displayed.
41 |
42 | #### Caveats
43 |
44 | Because the `path` and `default` props are used by the router, it's best to avoid using those props for your component(s) as they will conflict.
45 |
46 | ### Handling URLS
47 |
48 | :information_desk_person: Pages are just regular components that get mounted when you navigate to a certain URL.
49 | Any URL parameters get passed to the component as `props`.
50 |
51 | Defining what component(s) to load for a given URL is easy and declarative.
52 | Querystring and `:parameter` values are passed to the matched component as props.
53 | Parameters can be made optional by adding a `?`, or turned into a wildcard match by adding `*` (zero or more characters) or `+` (one or more characters):
54 |
55 | ```js
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | ```
66 |
67 | ### Lazy Loading
68 |
69 | Lazy loading (code splitting) with `preact-router` can be implemented easily using the [AsyncRoute](https://www.npmjs.com/package/preact-async-route) module:
70 |
71 | ```js
72 | import AsyncRoute from 'preact-async-route';
73 |
74 |
75 | import('./friends').then(module => module.default)}
78 | />
79 | import('./friend').then(module => module.default)}
82 | loading={() =>
loading...
}
83 | />
84 | ;
85 | ```
86 |
87 | ### Active Matching & Links
88 |
89 | `preact-router` includes an add-on module called `match` that lets you wire your components up to Router changes.
90 |
91 | Here's a demo of ``, which invokes the function you pass it (as its only child) in response to any routing:
92 |
93 | ```js
94 | import Router from 'preact-router';
95 | import Match from 'preact-router/match';
96 |
97 | render(
98 |
99 | {({ matches, path, url }) =>
{url}
}
100 |
101 |
demo fallback route
102 |
103 |
104 | );
105 |
106 | // another example: render only if at a given URL:
107 |
108 | render(
109 |
110 | {({ matches }) => matches &&
You are Home!
}
111 |
112 |
113 | );
114 | ```
115 |
116 | `` is just a normal link, but it automatically adds and removes an "active" classname to itself based on whether it matches the current URL.
117 |
118 | ```js
119 | import { Router } from 'preact-router';
120 | import { Link } from 'preact-router/match';
121 |
122 | render(
123 |
124 |
135 |
136 |
this is a demo route that always matches
137 |
138 |
139 | );
140 | ```
141 |
142 | ### Default Link Behavior
143 |
144 | Sometimes it's necessary to bypass preact-router's link handling and let the browser perform routing on its own.
145 |
146 | This can be accomplished by adding a `data-native` boolean attribute to any link:
147 |
148 | ```html
149 | Foo
150 | ```
151 |
152 | ### Detecting Route Changes
153 |
154 | The `Router` notifies you when a change event occurs for a route with the `onChange` callback:
155 |
156 | ```js
157 | import { render, Component } from 'preact';
158 | import { Router, route } from 'preact-router';
159 |
160 | class App extends Component {
161 | // some method that returns a promise
162 | isAuthenticated() {}
163 |
164 | handleRoute = async e => {
165 | switch (e.url) {
166 | case '/profile':
167 | const isAuthed = await this.isAuthenticated();
168 | if (!isAuthed) route('/', true);
169 | break;
170 | }
171 | };
172 |
173 | render() {
174 | return (
175 |
176 |
177 |
178 |
179 | );
180 | }
181 | }
182 | ```
183 |
184 | ### Redirects
185 |
186 | Can easily be implemented with a custom `Redirect` component;
187 |
188 | ```js
189 | import { Component } from 'preact';
190 | import { route } from 'preact-router';
191 |
192 | export default class Redirect extends Component {
193 | componentWillMount() {
194 | route(this.props.to, true);
195 | }
196 |
197 | render() {
198 | return null;
199 | }
200 | }
201 | ```
202 |
203 | Now to create a redirect within your application, you can add this `Redirect` component to your router;
204 |
205 | ```js
206 |
207 |
208 |
209 |
210 | ```
211 |
212 | ### Custom History
213 |
214 | It's possible to use alternative history bindings, like `/#!/hash-history`:
215 |
216 | ```js
217 | import { h } from 'preact';
218 | import Router from 'preact-router';
219 | import { createHashHistory } from 'history';
220 |
221 | const Main = () => (
222 |
223 |
224 |
225 |
226 |
227 | );
228 |
229 | render(, document.body);
230 | ```
231 |
232 | ### Programmatically Triggering Route
233 |
234 | It's possible to programmatically trigger a route to a page (like `window.location = '/page-2'`)
235 |
236 | ```js
237 | import { route } from 'preact-router';
238 |
239 | route('/page-2'); // appends a history entry
240 |
241 | route('/page-3', true); // replaces the current history entry
242 | ```
243 |
244 | ### Nested Routers
245 |
246 | The `` is a self-contained component that renders based on the page URL. When nested a Router inside of another Router, the inner Router does not share or observe the outer's URL or matches. Instead, inner routes must include the full path to be matched against the page's URL:
247 |
248 | ```js
249 | import { h, render } from 'preact';
250 | import Router from 'preact-router';
251 |
252 | function Profile(props) {
253 | // `props.rest` is the rest of the URL after "/profile/"
254 | return (
255 |