Happy cows produce good buttermilk, and good ingredients make for great apps!

Introducing Buttermilk ⚪️

Beautifully simple routing for React projects.

I think that most would agree the JS ecosystem is a wonderful, but bloated beast. There are ample choices of libraries for any occasion, but the most popular ones try to cater to many use cases instead of taking a more focused approach.

Buttermilk seeks to fulfill that need for routing in isomorphic React applications. It’s lightweight, features a very simple (but powerful) API, and makes an effort to keep usage as obvious as possible.

If you want to jump right in, head on over to the Buttermilk website!

The API

At its core, Buttermilk consists of a <Router> component and a set of route definitions. There’s much more you can do of course, but that’s the minimum to get started.

A typical route definition provides a path and a render method.

{
path: String | RegExp | Function,
redirect: String?,
render: Function?,
}

A path can be a relative URL string, a RegExp, or even a function that returns a boolean. It’s designed to be maximally-flexible so you can do routing as you see fit.

The render method is called when a route is activated. You can provide a React Component class, some JSX, or a promise that resolves to one of the two. The last option is commonly used in conjunction with the new dynamic import() syntax provided by webpack, etc.:

{
path: '/foo',
render: () => import('./SomePage').then(mdl => mdl.default),
}

Note that import() returns a transpiled module, so you’ll need to return the appropriate subproperty.

Using the right route

Following the simple and focused philosophy, Buttermilk has the most obvious API possible on the server side (example using an express request):

<Router
routes={routes}
url={req.originalUrl}
/>

Just pass in the URL and you’re done! The library takes care of the rest.

On the client side, Buttermilk automatically inspects window.location.href on initialization and listens to browser navigation events (popstate, hashchange) afterward. Easy peasy.

You can hook into what the router’s doing if desired with the optional lifecycle events:

  • routerDidInitialize
  • routeWillChange
  • routeDidChange

More info on the website.

<Link>ing up your views

Now that your routes are settled, there’s the matter of how to switch between them. Buttermilk exposes a <Link> polymorphic component to simplify this.

import { Link } from 'buttermilk';
<Link href="/one/of/your/routes">
Some text
</Link>

But wait, what’s a polymorphic component you might ask‽ Simply put, it’s a React component that is designed to change shape in a certain way. In this case, props.as allows you to change the underlying rendered JSX in any way you see fit!

For example, you could render a <button> component instead of an <a> for accessibility reasons:

import { Link } from 'buttermilk';
<Link as="button" href="/one/of/your/routes">
Some text
</Link>
Note: If anything other than "a" is passed to props.as, appropriate aria attributes are added to ensure that assistive technologies know the element causes navigation.

Or supply your own anchor component:

import { Link } from 'buttermilk';
import styled from 'styled-components';
const Anchor = styled.a`
color: tomato;
`;
<Link as={Anchor} href="/one/of/your/routes">
Some text
</Link>

Switching routes programmatically

Similar in spirit to the push and replace methods from react-router-redux, Buttermilk exposes a simple route utility method for changing the active route outside the React tree (client-side only.)

import { route } from 'buttermilk';
// acts like push / pushState
route('/foo');

If you want to avoid making a new browser history entry, set the second argument to false:

import { route } from 'buttermilk';
// acts like replace / replaceState
route('/foo', false);

Consuming the current route state

Sometimes you just gotta know what’s up, amirite? Buttermilk polyfills the brand spankin’ new React 16.3 context API to enable a simple component solution for accessing routing state:

// routingProps.location  (the parsed current URL in window.location.* form)
// routingProps.params (any extracted dynamic params from the URL)
// routingProps.route (the current route)
<RoutingState>
{routingProps => {
return /* some JSX */;
}}
</RoutingState>

Putting it all together

Here’s a holistic example, using many parts of the library (viewable live):

I had a lot of fun writing this library and figuring out how to keep it small (under 4KiB gzipped at the time of writing.) I hope you enjoy it too! 🚀

Website: https://buttermilk.js.org/
GitHub: https://github.com/probablyup/buttermilk

Also, check out my other project: markdown-to-jsx, an ultra lightweight markdown React component 🙂

Like what you read? Give Evan Scott a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.