A router's history enables navigation. It is responsible for creating the location objects, interfacing with its native environment (e.g. a browser) to perform navigation, and telling the router that navigation has occured.

Curi uses Hickory packages for its history implementations. Hickory is designed with asynchronous navigation in mind. This means that when a user clicks a link, your application can load data for the matched route before updating the location.

import { browser } from "@hickory/browser";
const router = createRouter(browser, routes);

You most likely will not need to interact directly with the application's history, but you should be familiar with the different choices.

Types of History

There are three Hickory packages to choose from for an application. Which one you use depends on where your application is running.

Browser History

import { browser } from "@hickory/browser";
const router = createRouter(browser, routes);

The browser history is used for applications running in a browser.

If you use the browser history, your application should be hosted on a server that can handle dynamic requests. This either means:

  1. A server with real time route matching (like an Express server).
  2. Configuring the server to respond with a fallback page when the request doesn't map to a real file on the server (e.g. with NGINX or Apache).
  3. Using a static file host that can be configured to respond with a fallback page.

Hash History

import { hash } from "@hickory/hash";
const router = createRouter(Hash, routes);

The hash history is a fallback history for applications running in a browser. It should only be used if you cannot configure the server to respond to requests that don't match files on the server.


This should only be used as a last resort. The browser history is almost always a better choice.

In Memory History

import { inMemory, createReusable } from "@hickory/in-memory";

const router = createRouter(inMemory, routes);
// or
const reusable = createReusable();
const router = createRouter(reusable, routes);

An in-memory history is used for applications not running in a browser. For example, the in memory history is used on the server, in a React Native app, and during testing.

The inMemory function is a full history object, capable of in-app navigation.

The createReusable function returns lightweight history function for server-side rendering. The returned history cannot navigate, which is fine for server rendering.


If you are not familiar with how single-page applications interact with a server, this article should help: Single-Page Applications and the Server.


The history object will map URL strings into location objects. Only the pathname, query (search), and hash components of a URL are used; locations ignore a URL's domain and protocol.

Matching locations to routes only uses the location's pathname.

// https://www.example.com/page?key=value#trending
location = {
  pathname: "/page",
  query: "key=value"
  hash: "trending"

Query Objects

The query value of a location is a string by default, but the history object can be configured to automatically parse it into an object.

You can choose whichever query parsing/stringifying package you prefer. Some of the most popular are qs, query-string, and querystring.

import { parse, stringify } from "qs";
import { browser } from "@hickory/browser";

const router = createRouter(browser, routes, {
  history: {
    query: { parse, stringify }

// https://www.example.com/page?key=value#trending
location = {
  pathname: "/page",
  query: { key: "value" }
  hash: "trending"

For more details on the history objects and their APIs, please check out the Hickory documentation.