PHPackages                             swayok/page.js - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. swayok/page.js

ActiveLibrary

swayok/page.js
==============

Tiny client-side router

2.01(7y ago)0631MITJavaScript

Since Jul 28Pushed 7y ago1 watchersCompare

[ Source](https://github.com/swayok/page.js)[ Packagist](https://packagist.org/packages/swayok/page.js)[ RSS](/packages/swayok-pagejs/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (3)DependenciesVersions (5)Used By (1)

[![page router logo](https://camo.githubusercontent.com/7bf01551ec0a375ba052f84ab2b0a93f1ff2f5da06663eda51fd62af4808a9bf/687474703a2f2f662e636c2e6c792f6974656d732f3369336e303031643073315130333172327131502f706167652e706e67)](https://camo.githubusercontent.com/7bf01551ec0a375ba052f84ab2b0a93f1ff2f5da06663eda51fd62af4808a9bf/687474703a2f2f662e636c2e6c792f6974656d732f3369336e303031643073315130333172327131502f706167652e706e67)

Tiny Express-inspired client-side router.

[![Build Status](https://camo.githubusercontent.com/5cbb789645ad0e5a1606fea89fe3896cf13db1a63acc617d1fdf037fb3a2c094/68747470733a2f2f7472617669732d63692e6f72672f766973696f6e6d656469612f706167652e6a732e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/visionmedia/page.js)[![Coverage Status](https://camo.githubusercontent.com/70a5f764ea25efb3410c180075b803ee3706760eb0a18bbfc1b7e7d4735f2b35/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f766973696f6e6d656469612f706167652e6a732f62616467652e706e673f6272616e63683d6d6173746572)](https://coveralls.io/r/visionmedia/page.js?branch=master)[![Gitter](https://camo.githubusercontent.com/abe08b740a4156153736f791393ec4da6619c4be73212e75769f52edacc0e2b5/68747470733a2f2f6261646765732e6769747465722e696d2f4a6f696e253230436861742e737667)](https://gitter.im/visionmedia/page.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

```
page('/', index)
page('/user/:user', show)
page('/user/:user/edit', edit)
page('/user/:user/album', album)
page('/user/:user/album/sort', sort)
page('*', notfound)
page()
```

Or failsafe variant for route declaration:

```
page.route('/', index)
page.route('/user/:user', show)
page.route('/user/:user/edit', edit)
page.route('/user/:user/album', album)
page.route('/user/:user/album/sort', sort)
page.route('*', notfound)
```

This one will not allow you to pass anything except string as 1st argument and function as 2nd argument

Installation
------------

[](#installation)

There are multiple ways to install `page.js`. With package managers:

```
$ npm install page # for browserify
$ component install visionmedia/page.js
$ bower install visionmedia/page.js
```

Running examples
----------------

[](#running-examples)

To run examples do the following to install dev dependencies and run the example server:

```
$ git clone git://github.com/visionmedia/page.js
$ cd page.js
$ npm install
$ node examples
$ open http://localhost:4000

```

Currently we have examples for:

- `basic` minimal application showing basic routing
- `notfound` similar to `basic` with single-page 404 support
- `album` showing pagination and external links
- `profile` simple user profiles
- `query-string` shows how you can integrate plugins using the router
- `state` illustrates how the history state may be used to cache data
- `server` illustrates how to use the dispatch option to server initial content
- `chrome` Google Chrome style administration interface
- `transitions` Shows off a simple technique for adding transitions between "pages"
- `partials` using hogan.js to render mustache partials client side

**NOTE**: keep in mind these examples do not use jQuery or similar, so portions of the examples may be relatively verbose, though they're not directly related to page.js in any way.

API
---

[](#api)

### page.route(path, callback\[, callback ...\])

[](#pageroutepath-callback-callback-)

Failsafe variant of page(path, callback\[, callback ...\])

Defines a route mapping `path` to the given `callback(s)`. Each callback is invoked with two arguments, [context](#context) and `next`. Much like Express invoking next will call the next registered callback with the given path.

```
page.route('/', user.list)
page.route('/user/:id', user.load, user.show)
page.route('/user/:id/edit', user.load, user.edit)
page.route('*', notfound)
```

Under certain conditions, links will be disregarded and will not be dispatched, such as:

- Links that are not of the same origin
- Links with the `download` attribute
- Links with the `target` attribute
- Links with the `rel="external"` attribute

### page(callback)

[](#pagecallback)

This is equivalent to `page('*', callback)` for generic "middleware".

### page(path)

[](#pagepath)

Navigate to the given `path`.

```
$('.view').click(function(e){
  page('/user/12')
  e.preventDefault()
})
```

### page(fromPath, toPath)

[](#pagefrompath-topath)

Setup redirect from one path to another.

### page.redirect(fromPath, toPath)

[](#pageredirectfrompath-topath)

Identical to `page(fromPath, toPath)`

### page.redirect(path)

[](#pageredirectpath)

Calling page.redirect with only a string as the first parameter redirects to another route. Waits for the current route to push state and after replaces it with the new one leaving the browser history clean.

```
page('/default', function(){
  // some logic to decide which route to redirect to
  if(admin) {
    page.redirect('/admin');
  } else {
    page.redirect('/guest');
  }
});

page('/default');
```

### page.show(path, state, dispatch, push, customData)

[](#pageshowpath-state-dispatch-push-customdata)

Navigate to `path`. Returns: `Context`Arguments:

- `path` - URL
- `state` - history state object
- `dispatch` - boolean; Default: `true`; When `false` router will not run any handlers for this url but will create instance of `Context`
- `push` - boolean; Default: true; When `false` router will not change URL in browser's address line
- `customData` - object; some specific data to store into `Context`. For more info see `Context#customData`

### page.replace(path, state, init, dispatch, customData)

[](#pagereplacepath-state-init-dispatch-customdata)

Navigate to `path` replacing current URL in browser's history. Returns: `Context`Arguments:

- `path` - URL
- `state` - history state object
- `init` - boolean; I'm not sure but possibly it is just a flag that indicates initial `Context`dispatched in `page.start()` ([@swayok](https://github.com/swayok))
- `dispatch` - boolean; Default: `true`; When `false` router will not run any handlers for this url but will create instance of `Context`
- `customData` - object; some specific data to store into `Context`. For more info see `Context#customData`

### page.back(path, state)

[](#pagebackpath-state)

Navigate to previous page in history or to `path` when history is empty. Arguments:

- `path` - default URL to navigate to when history is empty. Default: base URL
- `state` - history state object

### page.reload()

[](#pagereload)

Reload current url

### page(\[options\])

[](#pageoptions)

Register page's `popstate` / `click` bindings. If you're doing selective binding you'll like want to pass `{ click: false }`to specify this yourself. The following options are available:

- `click` bind to click events \[**true**\]
- `popstate` bind to popstate \[**true**\]
- `dispatch` perform initial dispatch \[**true**\]
- `hashbang` add `#!` before urls \[**false**\]
- `decodeURLComponents` remove URL encoding from path components (query string, pathname, hash) \[**true**\]

If you wish to load serve initial content from the server you likely will want to set `dispatch` to **false**.

### page.start(\[options\])

[](#pagestartoptions)

Identical to `page([options])` above.

### page.stop()

[](#pagestop)

Unbind both the `popstate` and `click` handlers.

### page.base(\[path\])

[](#pagebasepath)

Get or set the base `path`. For example if page.js is operating within `/blog/*` set the base path to "/blog".

### page.exit(path, callback\[, callback ...\])

[](#pageexitpath-callback-callback-)

Defines an exit route mapping `path` to the given `callback(s)`.

Exit routes are called when a page changes, using the context from the previous change. For example:

```
page('/sidebar', function(ctx, next) {
  sidebar.open = true
  next()
})

page.exit('/sidebar', function(ctx, next) {
  sidebar.open = false
  next()
})
```

### page.exit(callback)

[](#pageexitcallback)

Equivalent to `page.exit('*', callback)`.

### Context

[](#context)

Routes are passed `Context` objects, these may be used to share state, for example `ctx.user =`, as well as the history "state" `ctx.state` that the `pushState` API provides.

#### Context#save()

[](#contextsave)

Saves the context using `replaceState()`. For example this is useful for caching HTML or other resources that were loaded for when a user presses "back".

#### Context#handled

[](#contexthandled)

If `true`, marks the context as handled to prevent [default 404 behaviour](https://github.com/visionmedia/page.js#default-404-behaviour). For example this is useful for the routes with interminate quantity of the callbacks.

#### Context#canonicalPath

[](#contextcanonicalpath)

Pathname including the "base" (if any) and query string "/admin/login?foo=bar".

#### Context#path

[](#contextpath)

Pathname and query string "/login?foo=bar".

#### Context#querystring

[](#contextquerystring)

Query string void of leading `?` such as "foo=bar", defaults to "".

#### Context#pathname

[](#contextpathname)

The pathname void of query string "/login".

#### Context#state

[](#contextstate)

The `pushState` state object.

#### Context#title

[](#contexttitle)

The `pushState` title.

#### Context#push

[](#contextpush)

Indicates if router should change current URL. Default: null

Example: you handled request that shows a modal dialog but you do not want to change page address in browser. To do this you just need to set `contxt.push = false;` in your request handler.

Note: `contxt.push = true;` will not force URL change in cases when `push` argument is `false` in `page.show()` or `page.replace()`

#### Context#customData

[](#contextcustomdata)

Some specific data for this context. Allows you to provide better awareness about this context.

customData object can be passed via page.show() and page.replace() as 5th argument

It is used in page.reload() and in default onclick() and onpopstate() event handlers:

- page.reload() provides {is\_reload: true} customData
- onclick() provides {is\_click: true, target: DOM Element} customData
- onpopstate() provides {is\_history: true} customData

Routing
-------

[](#routing)

The router uses the same string-to-regexp conversion that Express does, so things like ":id", ":id?", and "\*" work as you might expect.

Another aspect that is much like Express is the ability to pass multiple callbacks. You can use this to your advantage to flatten nested callbacks, or simply to abstract components.

### Separating concerns

[](#separating-concerns)

For example suppose you have a route to *edit* users, and a route to *view* users. In both cases you need to load the user. One way to achieve this is with several callbacks as shown here:

```
page('/user/:user', load, show)
page('/user/:user/edit', load, edit)
```

Using the `*` character we can alter this to match all routes prefixed with "/user" to achieve the same result:

```
page('/user/*', load)
page('/user/:user', show)
page('/user/:user/edit', edit)
```

Likewise `*` can be used as catch-alls after all routes acting as a 404 handler, before all routes, in-between and so on. For example:

```
page('/user/:user', load, show)
page('*', function(){
  $('body').text('Not found!')
})
```

### Default 404 behaviour

[](#default-404-behaviour)

By default when a route is not matched, page.js invokes `page.stop()` to unbind itself, and proceed with redirecting to the location requested. This means you may use page.js with a multi-page application *without*explicitly binding to certain links.

### Working with parameters and contexts

[](#working-with-parameters-and-contexts)

Much like `request` and `response` objects are passed around in Express, page.js has a single "Context" object. Using the previous examples of `load` and `show` for a user, we can assign arbitrary properties to `ctx` to maintain state between callbacks.

To build a `load` function that will load the user for subsequent routes you'll need to access the ":id" passed. You can do this with `ctx.params.NAME` much like Express:

```
function load(ctx, next){
  var id = ctx.params.id
}
```

Then perform some kind of action against the server, assigning the user to `ctx.user` for other routes to utilize. `next()` is then invoked to pass control to the following matching route in sequence, if any.

```
function load(ctx, next){
  var id = ctx.params.id
  $.getJSON('/user/' + id + '.json', function(user){
    ctx.user = user
    next()
  })
}
```

The "show" function might look something like this, however you may render templates or do anything you want. Note that here `next()` is *not* invoked, because this is considered the "end point", and no routes will be matched until another link is clicked or `page(path)` is called.

```
function show(ctx){
  $('body')
    .empty()
    .append('' + ctx.user.name + '');
}
```

Finally using them like so:

```
page('/user/:id', load, show)
```

### Working with state

[](#working-with-state)

When working with the `pushState` API, and page.js you may optionally provide state objects available when the user navigates the history.

For example if you had a photo application and you performed a relatively expensive search to populate a list of images, normally when a user clicks "back" in the browser the route would be invoked and the query would be made yet-again.

An example implemenation might look as follows:

```
function show(ctx){
  $.getJSON('/photos', function(images){
    displayImages(images)
  })
}
```

You may utilize the history's state object to cache this result, or any other values you wish. This makes it possible to completely omit the query when a user presses back, providing a much nicer experience.

```
function show(ctx){
  if (ctx.state.images) {
    displayImages(ctx.state.images)
  } else {
    $.getJSON('/photos', function(images){
      ctx.state.images = images
      ctx.save()
      displayImages(images)
    })
  }
}
```

**NOTE**: `ctx.save()` must be used if the state changes *after* the first tick (xhr, setTimeout, etc), otherwise it is optional and the state will be saved after dispatching.

### Matching paths

[](#matching-paths)

Here are some examples of what's possible with the string to `RegExp` conversion.

Match an explicit path:

```
page('/about', callback)
```

Match with required parameter accessed via `ctx.params.name`:

```
page('/user/:name', callback)
```

Match with several params, for example `/user/tj/edit` or `/user/tj/view`.

```
page('/user/:name/:operation', callback)
```

Match with one optional and one required, now `/user/tj`will match the same route as `/user/tj/show` etc:

```
page('/user/:name/:operation?', callback)
```

Use the wildcard char `*` to match across segments, available via `ctx.params[N]` where **N** is the index of `*` since you may use several. For example the following will match `/user/12/edit`, `/user/12/albums/2/admin`and so on.

```
page('/user/*', loadUser)
```

Named wildcard accessed, for example `/file/javascripts/jquery.js`would provide "/javascripts/jquery.js" as `ctx.params.file`:

```
page('/file/:file(*)', loadUser)
```

And of course `RegExp` literals, where the capture groups are available via `ctx.params[N]` where **N**is the index of the capture group.

```
page(/^\/commits\/(\d+)\.\.(\d+)/, loadUser)
```

Plugins
-------

[](#plugins)

An example plugin *examples/query-string/query.js*demonstrates how to make plugins. It will provide a parsed `ctx.query` object derived from [node-querystring](https://github.com/visionmedia/node-querystring).

Usage by using "\*" to match any path in order to parse the query-string:

```
page('*', parse)
page('/', show)
page()

function parse(ctx, next) {
  ctx.query = qs.parse(location.search.slice(1));
  next();
}

function show(ctx) {
  if (Object.keys(ctx.query).length) {
    document
      .querySelector('pre')
      .textContent = JSON.stringify(ctx.query, null, 2);
  }
}
```

### Available plugins

[](#available-plugins)

- [querystring](https://github.com/visionmedia/page.js/blob/master/examples/query-string/query.js): provides a parsed `ctx.query` object derived from [node-querystring](https://github.com/visionmedia/node-querystring).
- [body-parser](https://github.com/kethinov/page.js-body-parser.js): provides a `req.body` object for routes derived from [body-parser](https://github.com/expressjs/body-parser).
- [express-mapper](https://github.com/kethinov/page.js-express-mapper.js): provides a direct imitation of the [Express](http://expressjs.com/) API so you can share controller code on the client and the server with your Express application without modification.

Please submit pull requests to add more to this list.

### Running tests

[](#running-tests)

In the console:

```
$ npm install
$ npm test

```

In the browser:

```
$ npm install
$ npm run serve
$ open http://localhost:3000/

```

### Support in IE8+

[](#support-in-ie8)

If you want the router to work in older version of Internet Explorer that don't support pushState, you can use the [HTML5-History-API](https://github.com/devote/HTML5-History-API) polyfill:

```
  npm install html5-history-api
```

##### How to use a Polyfill together with router (OPTIONAL):

[](#how-to-use-a-polyfill-together-with-router-optional)

If your web app is located within a nested basepath, you will need to specify the `basepath` for the HTML5-History-API polyfill. Before calling `page.base()` use: `history.redirect([prefixType], [basepath])` - Translation link if required.

- `prefixType`: `[string|null]` - Substitute the string after the anchor (#) by default "/".
- `basepath`: `[string|null]` - Set the base path. See `page.base()` by default "/". (Note: Slash after `pathname` required)

### Pull Requests

[](#pull-requests)

- Break commits into a single objective.
- An objective should be a chunk of code that is related but requires explaination.
- Commits should be in the form of what-it-is: how-it-does-it and or why-it's-needed or what-it-is for trivial changes
- Pull requests and commits should be a guide to the code.

License
-------

[](#license)

(The MIT License)

Copyright (c) 2012 TJ Holowaychuk &lt;&gt;

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community22

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~161 days

Total

4

Last Release

2730d ago

Major Versions

1.7.2 → 2.0.02018-11-24

### Community

Maintainers

![](https://www.gravatar.com/avatar/2a631e17770f1ef26d05cbc896e14b218d186973a00a515eed2d86fbc9eb936a?d=identicon)[sway](/maintainers/sway)

---

Top Contributors

[![tj](https://avatars.githubusercontent.com/u/25254?v=4)](https://github.com/tj "tj (148 commits)")[![A](https://avatars.githubusercontent.com/u/1410106?v=4)](https://github.com/A "A (141 commits)")[![swayok](https://avatars.githubusercontent.com/u/629675?v=4)](https://github.com/swayok "swayok (28 commits)")[![rstacruz](https://avatars.githubusercontent.com/u/74385?v=4)](https://github.com/rstacruz "rstacruz (19 commits)")[![kethinov](https://avatars.githubusercontent.com/u/419567?v=4)](https://github.com/kethinov "kethinov (16 commits)")[![jonathanong](https://avatars.githubusercontent.com/u/643505?v=4)](https://github.com/jonathanong "jonathanong (15 commits)")[![GianlucaGuarini](https://avatars.githubusercontent.com/u/879612?v=4)](https://github.com/GianlucaGuarini "GianlucaGuarini (15 commits)")[![andyburke](https://avatars.githubusercontent.com/u/273857?v=4)](https://github.com/andyburke "andyburke (14 commits)")[![devote](https://avatars.githubusercontent.com/u/367691?v=4)](https://github.com/devote "devote (13 commits)")[![hugomrdias](https://avatars.githubusercontent.com/u/314190?v=4)](https://github.com/hugomrdias "hugomrdias (9 commits)")[![hheiskanen](https://avatars.githubusercontent.com/u/6654979?v=4)](https://github.com/hheiskanen "hheiskanen (7 commits)")[![woodlandhunter](https://avatars.githubusercontent.com/u/6824706?v=4)](https://github.com/woodlandhunter "woodlandhunter (5 commits)")[![BorePlusPlus](https://avatars.githubusercontent.com/u/2392641?v=4)](https://github.com/BorePlusPlus "BorePlusPlus (3 commits)")[![fisch42](https://avatars.githubusercontent.com/u/847843?v=4)](https://github.com/fisch42 "fisch42 (3 commits)")[![nwmcsween](https://avatars.githubusercontent.com/u/622959?v=4)](https://github.com/nwmcsween "nwmcsween (3 commits)")[![5im-0n](https://avatars.githubusercontent.com/u/2580993?v=4)](https://github.com/5im-0n "5im-0n (2 commits)")[![aaronshaf](https://avatars.githubusercontent.com/u/122654?v=4)](https://github.com/aaronshaf "aaronshaf (2 commits)")[![ForbesLindesay](https://avatars.githubusercontent.com/u/1260646?v=4)](https://github.com/ForbesLindesay "ForbesLindesay (2 commits)")[![hughsk](https://avatars.githubusercontent.com/u/569817?v=4)](https://github.com/hughsk "hughsk (2 commits)")[![samccone](https://avatars.githubusercontent.com/u/883126?v=4)](https://github.com/samccone "samccone (2 commits)")

---

Tags

javascriptrouter

### Embed Badge

![Health badge](/badges/swayok-pagejs/health.svg)

```
[![Health](https://phpackages.com/badges/swayok-pagejs/health.svg)](https://phpackages.com/packages/swayok-pagejs)
```

###  Alternatives

[symfony/routing

Maps an HTTP request to a set of configuration variables

7.6k789.4M1.8k](/packages/symfony-routing)[slim/slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs

12.2k49.9M1.3k](/packages/slim-slim)[nikic/fast-route

Fast request router for PHP

5.3k92.4M668](/packages/nikic-fast-route)[tightenco/ziggy

Use your Laravel named routes in JavaScript.

4.3k41.6M267](/packages/tightenco-ziggy)[matthiasmullie/minify

CSS &amp; JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.

2.0k30.5M336](/packages/matthiasmullie-minify)[friendsofsymfony/jsrouting-bundle

A pretty nice way to expose your Symfony routing to client applications.

1.5k53.6M229](/packages/friendsofsymfony-jsrouting-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
