├── .browserslistrc
├── .eslintignore
├── .eslintrc
├── .github
├── lock.yml
└── workflows
│ ├── format.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── README.md
├── api-reference.md
├── blocking-transitions.md
├── getting-started.md
├── images
│ └── block.png
├── installation.md
└── navigation.md
├── fixtures
├── block-library
│ ├── index.html
│ └── index.js
├── block-vanilla
│ ├── index.html
│ └── index.js
├── hash-click.html
├── hash-history-length.html
└── unpkg-test.html
├── package-lock.json
├── package.json
├── packages
└── history
│ ├── .eslintrc
│ ├── __tests__
│ ├── .eslintrc
│ ├── TestSequences
│ │ ├── BackButtonTransitionHook.js
│ │ ├── BlockEverything.js
│ │ ├── BlockPopWithoutListening.js
│ │ ├── EncodedReservedCharacters.js
│ │ ├── GoBack.js
│ │ ├── GoForward.js
│ │ ├── InitialLocationDefaultKey.js
│ │ ├── InitialLocationHasKey.js
│ │ ├── Listen.js
│ │ ├── PushMissingPathname.js
│ │ ├── PushNewLocation.js
│ │ ├── PushRelativePathname.js
│ │ ├── PushRelativePathnameWarning.js
│ │ ├── PushSamePath.js
│ │ ├── PushState.js
│ │ ├── ReplaceNewLocation.js
│ │ ├── ReplaceSamePath.js
│ │ ├── ReplaceState.js
│ │ └── utils.js
│ ├── browser-test.js
│ ├── create-path-test.js
│ ├── hash-base-test.js
│ ├── hash-test.js
│ └── memory-test.js
│ ├── browser.ts
│ ├── hash.ts
│ ├── index.ts
│ ├── node-main.js
│ └── package.json
├── scripts
├── build.js
├── karma.conf.js
├── publish.js
├── rollup
│ └── history.config.js
├── test.js
├── tests.webpack.js
└── version.js
├── tsconfig.json
└── types
└── global.d.ts
/.browserslistrc:
--------------------------------------------------------------------------------
1 | # Browsers we support
2 | > 0.5%
3 | Chrome >= 73
4 | ChromeAndroid >= 75
5 | Firefox >= 67
6 | Edge >= 17
7 | IE 11
8 | Safari >= 12.1
9 | iOS >= 11.3
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build
2 | /fixtures
3 |
4 | node_modules/
5 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "rules": {
4 | "import/no-anonymous-default-export": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.github/lock.yml:
--------------------------------------------------------------------------------
1 | # Configuration for lock-threads - https://github.com/dessant/lock-threads
2 |
3 | # Number of days of inactivity before a closed issue or pull request is locked
4 | daysUntilLock: 60
5 |
6 | # Issues and pull requests with these labels will not be locked. Set to `[]` to disable
7 | exemptLabels: []
8 |
9 | # Label to add before locking, such as `outdated`. Set to `false` to disable
10 | lockLabel: false
11 |
12 | # Comment to post before locking. Set to `false` to disable
13 | lockComment: false
14 | # Limit to only `issues` or `pulls`
15 | # only: issues
16 |
17 | # Optionally, specify configuration settings just for `issues` or `pulls`
18 | # issues:
19 | # exemptLabels:
20 | # - help-wanted
21 | # lockLabel: outdated
22 |
23 | # pulls:
24 | # daysUntilLock: 30
25 |
--------------------------------------------------------------------------------
/.github/workflows/format.yml:
--------------------------------------------------------------------------------
1 | name: Format
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | format:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Cancel Previous Runs
14 | uses: styfle/cancel-workflow-action@0.9.1
15 |
16 | - name: Checkout Repository
17 | uses: actions/checkout@v2
18 |
19 | - name: Use Node.js
20 | uses: actions/setup-node@v2
21 | with:
22 | node-version: 14
23 |
24 | - name: Install dependencies
25 | run: yarn install --frozen-lockfile
26 |
27 | - name: Format
28 | run: npm run format --if-present
29 |
30 | - name: Commit
31 | run: |
32 | git config --local user.email "github-actions@remix.run"
33 | git config --local user.name "Remix Run Bot"
34 |
35 | git add .
36 | if [ -z "$(git status --porcelain)" ]; then
37 | echo "💿 no formatting changed"
38 | exit 0
39 | fi
40 | git commit -m "chore: format" -m "formatted $GITHUB_SHA"
41 | git push
42 | echo "💿 pushed formatting changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)"
43 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 | on:
3 | release:
4 | types: [published]
5 |
6 | jobs:
7 | release:
8 | if: github.repository == 'remix-run/history'
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | matrix:
13 | node-version: [14.x]
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 |
23 | # https://github.com/actions/setup-node/issues/213#issuecomment-833724757
24 | - name: Install npm v7
25 | run: npm i -g npm@7 --registry=https://registry.npmjs.org
26 |
27 | - name: Install dependencies
28 | run: npm ci
29 |
30 | - name: Build
31 | run: npm run build
32 |
33 | - name: Test
34 | run: npm run test & npm run size
35 |
36 | - name: Publish
37 | env:
38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
39 | run: |
40 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
41 | node scripts/publish.js
42 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | test:
6 | runs-on: ubuntu-latest
7 |
8 | strategy:
9 | matrix:
10 | node-version: [14.x]
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 |
15 | - name: Use Node.js ${{ matrix.node-version }}
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: ${{ matrix.node-version }}
19 |
20 | # https://github.com/actions/setup-node/issues/213#issuecomment-833724757
21 | - name: Install npm v7
22 | run: npm i -g npm@7 --registry=https://registry.npmjs.org
23 |
24 | - name: Install dependencies
25 | run: npm ci
26 |
27 | - name: Build
28 | run: npm run build
29 |
30 | - name: Test
31 | run: npm run test & npm run size
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /fixtures/*/history.js
3 |
4 | node_modules/
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | package-lock.json
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | All development happens [on GitHub](https://github.com/remix-run/history). When [creating a pull request](https://help.github.com/articles/creating-a-pull-request/), please make sure that all of the following apply:
2 |
3 | - If you're adding a new feature or "fixing" a bug, please go into detail about your use case and why you think you need that feature or that bug fixed. This library has lots and lots of dependents, and we can't afford to make changes lightly without understanding why.
4 | - The tests pass. The test suite will automatically run when you create the PR. All you need to do is wait a few minutes to see the result in the PR.
5 | - Each commit in your PR should describe a significant piece of work. Work that was done in one commit and then undone later should be squashed into a single commit.
6 |
7 | If your PR fails to meet these guidelines, it may be closed without much of an explanation besides a link to this document.
8 |
9 | Thank you for your contribution!
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) React Training 2016-2020
4 | Copyright (c) Remix Software 2020-2021
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # history · [![npm package][npm-badge]][npm]
2 |
3 | [npm-badge]: https://img.shields.io/npm/v/history.svg?style=flat-square
4 | [npm]: https://www.npmjs.org/package/history
5 |
6 | The history library lets you easily manage session history anywhere JavaScript runs. A `history` object abstracts away the differences in various environments and provides a minimal API that lets you manage the history stack, navigate, and persist state between sessions.
7 |
8 | ## Documentation
9 |
10 | Documentation for version 5 can be found in the [docs](docs) directory. This is the current stable release. Version 5 is used in React Router version 6.
11 |
12 | Documentation for version 4 can be found [on the v4 branch](https://github.com/remix-run/history/tree/v4/docs). Version 4 is used in React Router versions 4 and 5.
13 |
14 | ## Changes
15 |
16 | To see the changes that were made in a given release, please lookup the tag on [the releases page](https://github.com/remix-run/history/releases).
17 |
18 | For changes released in version 4.6.3 and earlier, please see [the `CHANGES.md` file](https://github.com/remix-run/history/blob/845d690c5576c7f55ecbe14babe0092e8e5bc2bb/CHANGES.md).
19 |
20 | ## Development
21 |
22 | Development of the current stable release, version 5, happens on [the `main` branch](https://github.com/remix-run/history/tree/main). Please keep in mind that this branch may include some work that has not yet been published as part of an official release. However, since `main` is always stable, you should feel free to build your own working release straight from `main` at any time.
23 |
24 | If you're interested in helping out, please read [our contributing guidelines](CONTRIBUTING.md).
25 |
26 | ## About
27 |
28 | `history` is developed and maintained by [Remix](https://remix.run). If you're interested in learning more about what React can do for your company, please [get in touch](mailto:hello@remix.run)!
29 |
30 | ## Thanks
31 |
32 | A big thank-you to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to run our build in real browsers.
33 |
34 | Also, thanks to [Dan Shaw](https://www.npmjs.com/~dshaw) for letting us use the `history` npm package name. Thanks, Dan!
35 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | Welcome to the history docs!
2 |
3 | The history library lets you easily manage session history anywhere JavaScript
4 | runs. A `history` object abstracts away the differences in various environments
5 | and provides a minimal API that lets you manage the history stack, navigate, and
6 | persist state between sessions.
7 |
8 | The library is very small, so there are really just a few files here for you to
9 | browse.
10 |
11 | If this is your first time here, we'd recommend you start with the [Getting
12 | Started](getting-started.md) guide.
13 |
14 | You may also be interested in reading one of the following guides:
15 |
16 | - [Installation](installation.md)
17 | - [Navigation](navigation.md)
18 | - [Blocking Transitions](blocking-transitions.md)
19 |
20 | An [API Reference](api-reference.md) is also available.
21 |
--------------------------------------------------------------------------------
/docs/api-reference.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # history API Reference
4 |
5 | This is the API reference for [the history JavaScript library](https://github.com/remix-run/history).
6 |
7 | The history library provides an API for tracking application history using [location](#location) objects that contain URLs and state. This reference includes type signatures and return values for the interfaces in the library. Please read the [getting started guide](getting-started.md) if you're looking for explanations about how to use the library to accomplish a specific task.
8 |
9 |
10 |
11 | ## Overview
12 |
13 |
14 |
15 | ### Environments
16 |
17 | The history library includes support for three different "environments", or modes of operation.
18 |
19 | - [Browser history](#createbrowserhistory) is used in web apps
20 | - [Hash history](#createhashhistory) is used in web apps where you don't want to/can't send the URL to the server for some reason
21 | - [Memory history](#creatememoryhistory) - is used in native apps and testing
22 |
23 | Just pick the right mode for your target environment and you're good to go.
24 |
25 |
26 |
27 | ### Listening
28 |
29 | To read the current location and action, use [`history.location`](#history.location) and [`history.action`](#history.action). Both of these properties are mutable and automatically update as the location changes.
30 |
31 | To be notified when the location changes, setup a listener using [`history.listen`](#history.listen).
32 |
33 |
34 |
35 | ### Navigation
36 |
37 | To change the current location, you'll want to use one of the following:
38 |
39 | - [`history.push`](#history.push) - Pushes a new location onto the history stack
40 | - [`history.replace`](#history.replace) - Replaces the current location with another
41 | - [`history.go`](#history.go) - Changes the current index in the history stack by a given delta
42 | - [`history.back`](#history.back) - Navigates one entry back in the history stack
43 | - [`history.forward`](#history.forward) - Navigates one entry forward in the history stack
44 |
45 |
46 |
47 | ### Confirming Navigation
48 |
49 | To prevent the location from changing, use [`history.block`](#history.block). This API allows you to prevent the location from changing so you can prompt the user before retrying the transition.
50 |
51 |
52 |
53 | ### Creating `href` values
54 |
55 | If you're building a link, you'll want to use [`history.createHref`](#history.createhref) to get a URL you can use as the value of ``.
56 |
57 | ---
58 |
59 |
60 |
61 | ## Reference
62 |
63 | The [source code](https://github.com/remix-run/history/tree/main/packages/history) for the history library is written in TypeScript, but it is compiled to JavaScript before publishing. Some of the function signatures in this reference include their TypeScript type annotations, but you can always refer to the original source as well.
64 |
65 |
66 |
67 | ### Action
68 |
69 | An `Action` represents a type of change that occurred in the history stack. `Action` is an `enum` with three members:
70 |
71 | - `Action.Pop` - A change to an arbitrary index in the stack, such as a back or forward navigation. This does not describe the direction of the navigation, only that the index changed. This is the default action for newly created history objects.
72 | - `Action.Push` - Indicates a new entry being added to the history stack, such as when a link is clicked and a new page loads. When this happens, all subsequent entries in the stack are lost.
73 | - `Action.Replace` - Indicates the entry at the current index in the history stack being replaced by a new one.
74 |
75 | See [the Getting Started guide](getting-started.md) for more information.
76 |
77 |
78 |
79 |
80 | ### `History`
81 |
82 | A `History` object represents the shared interface for `BrowserHistory`, `HashHistory`, and `MemoryHistory`.
83 |
84 |
85 | Type declaration
86 |
87 | ```ts
88 | interface History {
89 | readonly action: Action;
90 | readonly location: Location;
91 | createHref(to: To): string;
92 | push(to: To, state?: any): void;
93 | replace(to: To, state?: any): void;
94 | go(delta: number): void;
95 | back(): void;
96 | forward(): void;
97 | listen(listener: Listener): () => void;
98 | block(blocker: Blocker): () => void;
99 | }
100 | ```
101 |
102 |
103 |
104 | ### `createBrowserHistory`
105 |
106 |
107 | Type declaration
108 |
109 | ```tsx
110 | function createBrowserHistory(options?: { window?: Window }): BrowserHistory;
111 |
112 | interface BrowserHistory extends History {}
113 | ```
114 |
115 |
116 |
117 | A browser history object keeps track of the browsing history of an application using the browser's built-in history stack. It is designed to run in modern web browsers that support the HTML5 history interface including `pushState`, `replaceState`, and the `popstate` event.
118 |
119 | `createBrowserHistory` returns a `BrowserHistory` instance. `window` defaults to [the `defaultView` of the current `document`](https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView).
120 |
121 | ```ts
122 | import { createBrowserHistory } from "history";
123 | let history = createBrowserHistory();
124 | ```
125 |
126 | See [the Getting Started guide](getting-started.md) for more information.
127 |
128 |
129 |
130 |
131 |
132 | ### `createPath` and `parsePath`
133 |
134 |
135 | Type declaration
136 |
137 | ```ts
138 | function createPath(partialPath: Partial): string;
139 | function parsePath(path: string): Partial;
140 |
141 | interface Path {
142 | pathname: string;
143 | search: string;
144 | hash: string;
145 | }
146 | ```
147 |
148 |
149 |
150 | The `createPath` and `parsePath` functions are useful for creating and parsing URL paths.
151 |
152 | ```ts
153 | createPath({ pathname: "/login", search: "?next=home" }); // "/login?next=home"
154 | parsePath("/login?next=home"); // { pathname: '/login', search: '?next=home' }
155 | ```
156 |
157 |
158 |
159 |
160 | ### `createHashHistory`
161 |
162 |
163 | Type declaration
164 |
165 | ```ts
166 | createHashHistory({ window?: Window }): HashHistory;
167 |
168 | interface HashHistory extends History {}
169 | ```
170 |
171 |
172 |
173 | A hash history object keeps track of the browsing history of an application using the browser's built-in history stack. It is designed to be run in modern web browsers that support the HTML5 history interface including `pushState`, `replaceState`, and the `popstate` event.
174 |
175 | `createHashHistory` returns a `HashHistory` instance. `window` defaults to [the `defaultView` of the current `document`](https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView).
176 |
177 | The main difference between this and [browser history](#createbrowserhistory) is that a hash history stores the current location in the [`hash` portion of the URL](https://developer.mozilla.org/en-US/docs/Web/API/Location/hash#:~:text=The%20hash%20property%20of%20the,an%20empty%20string%2C%20%22%22%20.), which means that it is not ever sent to the server. This can be useful if you are hosting your site on a domain where you do not have full control over the server routes, or e.g. in an Electron app where you don't want to configure the "server" to serve the same page at different URLs.
178 |
179 | ```ts
180 | import { createHashHistory } from "history";
181 | let history = createHashHistory();
182 | ```
183 |
184 | See [the Getting Started guide](getting-started.md) for more information.
185 |
186 |
187 |
188 |
189 | ### `createMemoryHistory`
190 |
191 |
192 | Type declaration
193 |
194 | ```ts
195 | function createMemoryHistory({
196 | initialEntries?: InitialEntry[],
197 | initialIndex?: number
198 | }): MemoryHistory;
199 |
200 | type InitialEntry = string | Partial;
201 |
202 | interface MemoryHistory extends History {
203 | readonly index: number;
204 | }
205 | ```
206 |
207 |
208 |
209 | A memory history object keeps track of the browsing history of an application using an internal array. This makes it ideal in situations where you need complete control over the history stack, like React Native and tests.
210 |
211 | `createMemoryHistory` returns a `MemoryHistory` instance. You can provide initial entries to this history instance through the `initialEntries` property, which defaults to `['/']` (a single location at the root `/` URL). The `initialIndex` defaults to the index of the last item in `initialEntries`.
212 |
213 | ```ts
214 | import { createMemoryHistory } from "history";
215 | let history = createMemoryHistory();
216 | // Or, to pre-seed the history instance with some URLs:
217 | let history = createMemoryHistory({
218 | initialEntries: ["/home", "/profile", "/about"],
219 | });
220 | ```
221 |
222 | See [the Getting Started guide](getting-started.md) for more information.
223 |
224 |
225 |
226 | ### `history.action`
227 |
228 | The current (most recent) [`Action`](#action) that modified the history stack. This property is mutable and automatically updates as the current location changes.
229 |
230 | See also [`history.listen`](#history.listen).
231 |
232 |
233 |
234 | ### `history.back()`
235 |
236 | Goes back one entry in the history stack. Alias for `history.go(-1)`.
237 |
238 | See [the Navigation guide](navigation.md) for more information.
239 |
240 |
241 |
242 | ### `history.block(blocker: Blocker)`
243 |
244 |
245 | Type declaration
246 |
247 | ```ts
248 | interface Blocker {
249 | (tx: Transition): void;
250 | }
251 |
252 | interface Transition {
253 | action: Action;
254 | location: Location;
255 | retry(): void;
256 | }
257 | ```
258 |
259 |
260 |
261 | Prevents changes to the history stack from happening. This is useful when you want to prevent the user navigating away from the current page, for example when they have some unsaved data on the current page.
262 |
263 | ```ts
264 | // To start blocking location changes...
265 | let unblock = history.block(({ action, location, retry }) => {
266 | // A transition was blocked!
267 | });
268 |
269 | // Later, when you want to start allowing transitions again...
270 | unblock();
271 | ```
272 |
273 | See [the guide on Blocking Transitions](blocking-transitions.md) for more information.
274 |
275 |
276 |
277 | ### `history.createHref(to: To)`
278 |
279 | Returns a string suitable for use as an `` value that will navigate to
280 | the given destination.
281 |
282 |
283 |
284 | ### `history.forward()`
285 |
286 | Goes forward one entry in the history stack. Alias for `history.go(1)`.
287 |
288 | See [the Navigation guide](navigation.md) for more information.
289 |
290 |
291 |
292 | ### `history.go(delta: number)`
293 |
294 | Navigates back/forward by `delta` entries in the stack.
295 |
296 | See [the Navigation guide](navigation.md) for more information.
297 |
298 |
299 |
300 | ### `history.index`
301 |
302 | The current index in the history stack.
303 |
304 | > [!Note:]
305 | >
306 | > This property is available only on [memory history](#memoryhistory) instances.
307 |
308 |
309 |
310 | ### `history.listen(listener: Listener)`
311 |
312 |
313 | Type declaration
314 |
315 | ```ts
316 | interface Listener {
317 | (update: Update): void;
318 | }
319 |
320 | interface Update {
321 | action: Action;
322 | location: Location;
323 | }
324 | ```
325 |
326 |
327 |
328 | Starts listening for location changes and calls the given callback with an `Update` when it does.
329 |
330 | ```ts
331 | // To start listening for location changes...
332 | let unlisten = history.listen(({ action, location }) => {
333 | // The current location changed.
334 | });
335 |
336 | // Later, when you are done listening for changes...
337 | unlisten();
338 | ```
339 |
340 | See [the Getting Started guide](getting-started.md#listening) for more information.
341 |
342 |
343 |
344 | ### `history.location`
345 |
346 | The current [`Location`](#location). This property is mutable and automatically updates as the current location changes.
347 |
348 | Also see [`history.listen`](#history.listen).
349 |
350 |
351 |
352 | ### `history.push(to: To, state?: any)`
353 |
354 | Pushes a new entry onto the stack.
355 |
356 | See [the Navigation guide](navigation.md) for more information.
357 |
358 |
359 |
360 | ### `history.replace(to: To, state?: any)`
361 |
362 | Replaces the current entry in the stack with a new one.
363 |
364 | See [the Navigation guide](navigation.md) for more information.
365 |
366 |
367 |
368 | ### Location
369 |
370 |
371 | Type declaration
372 |
373 | ```ts
374 | interface Location {
375 | pathname: string;
376 | search: string;
377 | hash: string;
378 | state: unknown;
379 | key: string;
380 | }
381 | ```
382 |
383 |
384 |
385 | A `location` is a particular entry in the history stack, usually analogous to a "page" or "screen" in your app. As the user clicks on links and moves around the app, the current location changes.
386 |
387 |
388 |
389 | ### `location.pathname`
390 |
391 | The `location.pathname` property is a string that contains an initial `/` followed by the remainder of the URL up to the `?`.
392 |
393 | See also [`URL.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname).
394 |
395 |
396 |
397 | ### `location.search`
398 |
399 | The `location.search` property is a string that contains an initial `?` followed by the `key=value` pairs in the query string. If there are no parameters, this value may be the empty string (i.e. `''`).
400 |
401 | See also [`URL.search`](https://developer.mozilla.org/en-US/docs/Web/API/URL/search).
402 |
403 |
404 |
405 | ### `location.hash`
406 |
407 | The `location.hash` property is a string that contains an initial `#` followed by fragment identifier of the URL. If there is no fragment identifier, this value may be the empty string (i.e. `''`).
408 |
409 | See also [`URL.hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).
410 |
411 |
412 |
413 | ### `location.state`
414 |
415 | The `location.state` property is a user-supplied [`State`](#state) object that is associated with this location. This can be a useful place to store any information you do not want to put in the URL, e.g. session-specific data.
416 |
417 | > [!Note:]
418 | >
419 | > In web browsers, this state is managed using the browser's built-in
420 | > `pushState`, `replaceState`, and `popstate` APIs. See also
421 | > [`History.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state).
422 |
423 |
424 |
425 | ### `location.key`
426 |
427 | The `location.key` property is a unique string associated with this location. On the initial location, this will be the string `default`. On all subsequent locations, this string will be a unique identifier.
428 |
429 | This can be useful in situations where you need to keep track of 2 different states for the same URL. For example, you could use this as the key to some network or device storage API.
430 |
431 |
432 |
433 | ### State
434 |
435 | A `State` value is an arbitrary value that holds extra information associated with a [`Location`](#location) but does not appear in the URL. This value is always associated with that location.
436 |
437 | See [the Navigation guide](navigation.md) for more information.
438 |
439 |
440 |
441 | ### To
442 |
443 |
444 | Type declaration
445 |
446 | ```ts
447 | type To = string | Partial;
448 |
449 | interface Path {
450 | pathname: string;
451 | search: string;
452 | hash: string;
453 | }
454 | ```
455 |
456 |
457 |
458 | A [`To`](https://github.com/remix-run/history/blob/main/packages/history/index.ts#L178) value represents a destination location, but doesn't contain all the information that a normal [`location`](#location) object does. It is primarily used as the first argument to [`history.push`](#history.push) and [`history.replace`](#history.replace).
459 |
460 | See [the Navigation guide](navigation.md) for more information.
461 |
--------------------------------------------------------------------------------
/docs/blocking-transitions.md:
--------------------------------------------------------------------------------
1 | # Blocking Transitions
2 |
3 | `history` lets you block navigation away from the current page using the
4 | [`history.block(blocker: Blocker)`](api-reference.md#history.block) API. For
5 | example, you can make sure the user knows that if they leave the current page
6 | they will lose some unsaved changes they've made.
7 |
8 | ```js
9 | // Block navigation and register a callback that
10 | // fires when a navigation attempt is blocked.
11 | let unblock = history.block((tx) => {
12 | // Navigation was blocked! Let's show a confirmation dialog
13 | // so the user can decide if they actually want to navigate
14 | // away and discard changes they've made in the current page.
15 | let url = tx.location.pathname;
16 | if (window.confirm(`Are you sure you want to go to ${url}?`)) {
17 | // Unblock the navigation.
18 | unblock();
19 |
20 | // Retry the transition.
21 | tx.retry();
22 | }
23 | });
24 | ```
25 |
26 | This example uses
27 | [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm),
28 | but you could also use your own custom confirm dialog if you'd prefer.
29 |
30 | ## Caveats
31 |
32 | `history.block` will call your callback for all in-page navigation attempts, but
33 | for navigation that reloads the page (e.g. the refresh button or a link that
34 | doesn't use `history.push`) it registers [a `beforeunload`
35 | handler](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
36 | to prevent the navigation. In modern browsers you are not able to customize this
37 | dialog. Instead, you'll see something like this (Chrome):
38 |
39 | 
40 |
41 | One subtle side effect of registering a `beforeunload` handler is that the page
42 | will not be [salvageable](https://html.spec.whatwg.org/#unloading-documents) in
43 | [the `pagehide`
44 | event](https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event).
45 | This means the page may not be reused by the browser, so things like timers,
46 | scroll position, and event sources will not be reused when the user navigates
47 | back to that page.
48 |
--------------------------------------------------------------------------------
/docs/getting-started.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Intro
5 |
6 | The history library provides history tracking and navigation primitives for JavaScript applications that run in browsers and other stateful environments.
7 |
8 | If you haven't yet, please take a second to read through [the Installation guide](installation.md) to get the library installed and running on your system.
9 |
10 | We provide 3 different methods for working with history, depending on your environment:
11 |
12 | - A "browser history" is for use in modern web browsers that support the [HTML5 history API](http://diveintohtml5.info/history.html) (see [cross-browser compatibility](http://caniuse.com/#feat=history))
13 | - A "hash history" is for use in web browsers where you want to store the location in the [hash](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/hash) portion of the current URL to avoid sending it to the server when the page reloads
14 | - A "memory history" is used as a reference implementation that may be used in non-browser environments, like [React Native](https://facebook.github.io/react-native/) or tests
15 |
16 | The main bundle exports one method for each environment: [`createBrowserHistory`](api-reference.md#createbrowserhistory) for browsers, [`createHashHistory`](api-reference.md#createhashhistory) for using hash history in browsers, and [`createMemoryHistory`](api-reference.md#creatememoryhistory) for creating an in-memory history.
17 |
18 | In addition to the main bundle, the library also includes `history/browser` and `history/hash` bundles that export singletons you can use to quickly get a history instance for [the current `document`](https://developer.mozilla.org/en-US/docs/Web/API/Window/document) (web page).
19 |
20 | ## Basic Usage
21 |
22 | Basic usage looks like this:
23 |
24 | ```js
25 | // Create your own history instance.
26 | import { createBrowserHistory } from "history";
27 | let history = createBrowserHistory();
28 |
29 | // ... or just import the browser history singleton instance.
30 | import history from "history/browser";
31 |
32 | // Alternatively, if you're using hash history import
33 | // the hash history singleton instance.
34 | // import history from 'history/hash';
35 |
36 | // Get the current location.
37 | let location = history.location;
38 |
39 | // Listen for changes to the current location.
40 | let unlisten = history.listen(({ location, action }) => {
41 | console.log(action, location.pathname, location.state);
42 | });
43 |
44 | // Use push to push a new entry onto the history stack.
45 | history.push("/home", { some: "state" });
46 |
47 | // Use replace to replace the current entry in the stack.
48 | history.replace("/logged-in");
49 |
50 | // Use back/forward to navigate one entry back or forward.
51 | history.back();
52 |
53 | // To stop listening, call the function returned from listen().
54 | unlisten();
55 | ```
56 |
57 | If you're using memory history you'll need to create your own `history` object
58 | before you can use it.
59 |
60 | ```js
61 | import { createMemoryHistory } from "history";
62 | let history = createMemoryHistory();
63 | ```
64 |
65 | If you're using browser or hash history with a `window` other than that of the
66 | current `document` (like an iframe), you'll need to create your own browser/hash
67 | history:
68 |
69 | ```js
70 | import { createBrowserHistory } from "history";
71 | let history = createBrowserHistory({
72 | window: iframe.contentWindow,
73 | });
74 | ```
75 |
76 |
77 |
78 | ## Properties
79 |
80 | Each `history` object has the following properties:
81 |
82 | - [`history.location`](api-reference.md#history.location) - The current location (see below)
83 | - [`history.action`](api-reference.md#history.action) - The current navigation action (see below)
84 |
85 | Additionally, memory history provides `history.index` that tells you the current index in the history stack.
86 |
87 |
88 |
89 | ## Listening
90 |
91 | You can listen for changes to the current location using `history.listen`:
92 |
93 | ```js
94 | history.listen(({ action, location }) => {
95 | console.log(
96 | `The current URL is ${location.pathname}${location.search}${location.hash}`
97 | );
98 | console.log(`The last navigation action was ${action}`);
99 | });
100 | ```
101 |
102 | The [`location`](api-reference.md#location) object implements a subset of [the `window.location` interface](https://developer.mozilla.org/en-US/docs/Web/API/Location), including:
103 |
104 | - [`location.pathname`](api-reference.md#location.pathname) - The path of the URL
105 | - [`location.search`](api-reference.md#location.search) - The URL query string
106 | - [`location.hash`](api-reference.md#location.hash) - The URL hash fragment
107 | - [`location.state`](api-reference.md#location.state) - Some extra state for this
108 | location that does not reside in the URL (may be `null`)
109 | - [`location.key`](api-reference.md#location.key) - A unique string representing this location
110 |
111 | The [`action`](api-reference.md#action) is one of `Action.Push`, `Action.Replace`, or `Action.Pop` depending on how the user got to the current location.
112 |
113 | - `Action.Push` means one more entry was added to the history stack
114 | - `Action.Replace` means the current entry in the stack was replaced
115 | - `Action.Pop` means we went to some other location already in the stack
116 |
117 |
118 |
119 | ## Cleaning up
120 |
121 | When you attach a listener using `history.listen`, it returns a function that can be used to remove the listener, which can then be invoked in cleanup logic:
122 |
123 | ```js
124 | let unlisten = history.listen(myListener);
125 |
126 | // Later, when you're done...
127 | unlisten();
128 | ```
129 |
130 |
131 |
132 | ## Utilities
133 |
134 | The main history bundle also contains both `createPath` and `parsePath` methods that may be useful when working with URL paths.
135 |
136 | ```js
137 | let pathPieces = parsePath("/the/path?the=query#the-hash");
138 | // pathPieces = {
139 | // pathname: '/the/path',
140 | // search: '?the=query',
141 | // hash: '#the-hash'
142 | // }
143 |
144 | let path = createPath(pathPieces);
145 | // path = '/the/path?the=query#the-hash'
146 | ```
147 |
--------------------------------------------------------------------------------
/docs/images/block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remix-run/history/3e9dab413f4eda8d6bce565388c5ddb7aeff9f7e/docs/images/block.png
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | The history library is published to the public [npm](https://www.npmjs.com/)
4 | registry. You can install it using:
5 |
6 | $ npm install --save history
7 |
8 | ## Using a Bundler
9 |
10 | The best way to use the `history` library is with a bundler that supports
11 | JavaScript modules (we recommend [Rollup](https://rollupjs.org)). Recent
12 | versions of Webpack and Parcel are also good choices.
13 |
14 | Then you can write your code using JavaScript `import` statements, like this:
15 |
16 | ```js
17 | import { createBrowserHistory } from "history";
18 | // ...
19 | ```
20 |
21 | If you're using a bundler that doesn't understand JavaScript modules and only
22 | understands CommonJS, you can use `require` as you would with anything else:
23 |
24 | ```js
25 | var createBrowserHistory = require("history").createBrowserHistory;
26 | ```
27 |
28 | ## Using `
42 | ```
43 |
44 | The `history.development.js` build is also available for non-production apps.
45 |
46 | In legacy browsers that do not yet support JavaScript modules, you can use one
47 | of our UMD (global) builds:
48 |
49 | ```html
50 |
51 |
52 | ```
53 |
54 | You can find the library on `window.HistoryLibrary`.
55 |
--------------------------------------------------------------------------------
/docs/navigation.md:
--------------------------------------------------------------------------------
1 | # Navigation
2 |
3 | `history` objects may be used to programmatically change the current location
4 | using the following methods:
5 |
6 | - [`history.push(to: To, state?: State)`](api-reference.md#history.push)
7 | - [`history.replace(to: To, state?: State)`](api-reference.md#history.replace)
8 | - [`history.go(delta: number)`](api-reference.md#history.go)
9 | - [`history.back()`](api-reference.md#history.back)
10 | - [`history.forward()`](api-reference.md#history.forward)
11 |
12 | An example:
13 |
14 | ```js
15 | // Push a new entry onto the history stack.
16 | history.push("/home");
17 |
18 | // Push a new entry onto the history stack with a query string
19 | // and some state. Location state does not appear in the URL.
20 | history.push("/home?the=query", { some: "state" });
21 |
22 | // If you prefer, use a location-like object to specify the URL.
23 | // This is equivalent to the example above.
24 | history.push(
25 | {
26 | pathname: "/home",
27 | search: "?the=query",
28 | },
29 | {
30 | some: state,
31 | }
32 | );
33 |
34 | // Go back to the previous history entry. The following
35 | // two lines are synonymous.
36 | history.go(-1);
37 | history.back();
38 | ```
39 |
--------------------------------------------------------------------------------
/fixtures/block-library/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |