Getting Started

Curi aims to be easy to setup. The one thing that all Curi projects have in common is a router. You create a router by passing a Hickory history object and an array of route objects to the curi function (the default export from @curi/core.

In order to re-render your application after navigation, you can subscribe to your router using its respond method. respond takes a callback function that will be passed response and action arguments, which you can use to render.

The History Object#

Curi's navigation is powered by the Hickory package. You just need to pick which type of Hickory history object is right for your application.

// Use Browser when your website has a dynamic server
import Browser from '@hickory/browser';
const browserHistory = Browser();

// Use Hash when your website uses a static file server
import Hash from '@hickory/hash';
const hashHistory = Hash();

// Use InMemory when your application doesn't run in a browser
import InMemory from '@hickory/in-memory';
const memoryHistory = InMemory();

Each history object has essentially the same API (InMemory has a few extra properties). The most important properties to know are the location object as well as the navigate, push, and replace methods.

// the location property is the current location object
browserHistory.location === {
  pathname: '/guides/getting-started',
  ...
};

// the push method will navigate to a new location
browserHistory.push({ pathname: '/guides/installation' });

// the replace method will replace the current location
// with the provided one
browserHistory.push({ pathname: '/guides/confirming-navigation' });

// the navigate method will choose whether to push or replace for you
// this behavior mimics how anchors (<a>) navigate
browserHistory.navigate({ pathname: '/guides/getting-started' });

The Routes Array#

Routes are objects with two required properties: name and path.

Paths can be any valid path-to-regexp string. It is just important that you do not begin the string with a forward slash (/). Forward slashes are fine anywhere else in the path. (this/is/fine, but /this/is/not).

The names are used to generate URIs for you. With Curi, you never have to write a URI's pathname string yourself. It is required that all of your routes have unique names. This is because Curi generates location pathnames using route names (and params for non-static paths).

const routes = [
  {
    name: 'Home',
    path: '', // matches the pathname /
    ...
  },
  ...
]

How route matching works and the other route properties are explained more in-depth in the All About Routes guide.

The router#

Once you have your Hickory history object and your routes array, you just need to pass them to the default export from the Curi package (which we will name curi here).

import curi from 'curi';
import Browser from '@hickory/browser';
import routes from './routes';

const history = Browser();
const router = curi(history, routes);

Other router options#

The curi function can also take an optional third argument, which is an options object. You can use this to pass add-ons, side effects, a cache, and a pathnameOptions object to your router.

const router = curi(history, routes, {
  addons: [...],
  sideEffects: [...],
  cache: cacheObject,
  pathnameOptions: { encode: x => x }
});

Responses#

Whenever navigation happens, a new location object is created by Hickory. Curi uses that location object's pathname property to match against all of your routes. When it finds one that matches, it uses that route object to create a response object. You can subscribe to a Curi router with a response handler function. When a new response is created, your response handler function will be called with the response and the action type of the navigation.

const router = curi(history, routes);
router.response((response, action) => {
  // whenever the location changes, this function is called
  // you can use this function to re-render your application
  // using the new response object
});

Responses are generated asynchronously. A Curi router has a response function that you can use to register a function to be called whenever a new response is generated.

const router = curi(history, routes);
// wait to render until a response is generated
router.respond((response, action) => {
  // now we can render using the response
});

Your location-based rendering will be centered around these response objects, so you should be familiar with the different properties that will be available to you. We will get into more details about responses in the Rendering with Responses guide, but for now we will just go over how a route maps to a response.

// if you have the following routes
const routes = [
  ...,
  {
    name: 'Album',
    path: 'photos/:albumID',
    ...,
    children: [
      {
        name: 'Photo',
        path: ':photoID',
        match: {
          response: ({ set }) => {
            set.body(Photo);
          }
        }
      }
    ]
  }
];
// when the user visits the URI /photos/6789/12345
// the following response object would be created:

{
  // The location key
  key: '1.0',

  // The location object used to generate the response.
  location: { pathname: '/photos/6789/12345', ... },

  // The value returned by the route's body function
  body: Photo,

  // The name of the best matching route
  name: 'Photo',

  // The name of ancestor routes that matched
  // part of the location's pathname
  partials: ['Album'],

  // An object containing the values parsed
  // from the pathname by path-to-regexp.
  params: { photoID: 12345, albumID: 6789 },

  // There are a few more properties as well. Please read
  // the Rendering with Responses guide to see those
}

Next

Now that you know the core of how Curi works, let's take a closer look at routes with the All About Routes guide.