Navigated to /docs/routing.

Routing

Creating pages for your application

File-based routing

Nokkio supports a file-based routing system that helps naturally organize the structure of your application. Hierarchal files within the pages/ directory each define a page (or, "route") of your application. Each page in the pages/ directory must adhere to the Page Module API.

The simplest incantation of a nokkio app contains a single index.js page and reads like this:

pages
└── index.js

If we were to open pages/index.js, we'd see the following:

pages/index.js
export default function Index() {
  return 'Hello world!';
}

When visiting the root route of your application, /, you would see the output of the Index component in your browser. In our case, we would see the text "Hello world!".

Adding pages

If we wanted to add an "about" page to our app, we'd start off by creating a new file: pages/about.js:

pages
β”œβ”€β”€ index.js
└── about.js

So long as the default export of pages/about.js were a valid React component, it would render when visiting the /about path of the application.

Updating the page's metadata

Pages can express more than just what React component to render–they can also set the document metadata for a given route. To opt into this API, a page exports a custom getPageMetadata function. For example, to set the document's title:

pages/index.js
export function getPageMetadata() {
  return { title: 'Hello world!' };
}

export default function Index() {
  return <p>Hello world!</p>;
}

Click here to learn more about the Page Module API.

Dynamic routes / Parameterized URLs

Nokkio also supports the concept of "dynamic" routes. A dyanmic route (or dynamic page) is one whose input is bound to the URL itself. For example: let's consider a blog whose posts page all share the same routing scheme of /blog/posts/[id]. In Nokkio, these types of pages are created just like any other, with one small difference. In place of using a static routing segment in the filename (e.g. index.js or about.js) a dynamic page provides a parameter name, wrapped with brackets (e.g. [id].js).

Let's say we did want to build out the structure for a blog using Nokkio. Here's how we might set up our pages/ directory to enable this:

pages
β”œβ”€β”€ blog
β”‚   β”œβ”€β”€ index.js
β”‚   └── [slug].js
└── index.js

This structure would map the /blog path to pages/blog/index.js, while forwarding all requests at /blog/* to pages/blog/[slug].js.

pages/blog/[slug].js
export default function BlogPost({ params }) {
  return <h1>{params.slug}</h1>
}

Nokkio pages are automatically provided all parameterized URL segments under the name of the defined page. That is to say, if you call your dynamic page [slug].js, your component will be rendered with a params prop with the slug as one of its properties. If we were to then visit this app at /blog/foo-bar-123 the component would ultimately render <h1>foo-bar-123</h1> to the DOM.

Tip
When building dynamic pages, Nokkio will provide params to the page component, as well as the getPageData and getPageMetadata functions per the Page Module API specification.

In order to hook into the nokkio routing machinery, we expose a <Link /> component:

pages/index.js
import { Link } from '@nokkio/router';

export default function HelloWorld() {
  return (
    <>
      <h1>Hello world</h1>
      <Link to="/about">About</Link>
    </>
  );
}

Under the hood, this component renders a regular anchor element which should handle the vast majority of use cases in an application. As an escape hatch, we also provide a custom useNavigate hook, which can be programmatically called elsewhere.

Accessibility

Creating accessible web applications can be quite difficult, and when you throw the constraints of a single-page application into the mix, things get even trickier. Nokkio doesn't leave you hanging here–the router that powers our application runtime makes sure to announce route changes to assitive technologies in order to create a more inclusive experience.

Authenticated pages

If you happen to be using authentication in your nokkio project, setting up authenticated pages is as easy as decorating the filename of the given page:

pages
β”œβ”€β”€ admin.auth.js
β”œβ”€β”€ blog
β”‚   β”œβ”€β”€ index.js
β”‚   └── [slug].js
β”œβ”€β”€ index.js
└── login.js

In this example, we have an auth-only page, admin.auth.js, which is only accessible to authenticated users. When a visitor to your app has not authenticated and views a page with the .auth.js extension, they will be redirected to the login route, and returned upon login.

Hierarchal layouts

In addition to file-based routing, nokkio also provides an API for hierarchal layouts. To opt into this API, you'll want to first create a _layouts/ directory at the root of your pages/ directory. Layouts are organized in a similar fashion to pages, with one important distinction–they cascade downward, and as such, not every page requires one. In practice, this means that layouts can be shared as routes diverge, and will be automatically used by all descendent pages unless otherwise specified. In other words, adding a layout component for /blog will apply the layout to all paths starting with /blog, unless another layout component is found.

Building on our blog example above, we might choose to create the following structure:

pages
β”œβ”€β”€ layouts
β”‚   β”œβ”€β”€ index.js
β”‚   └── blog.js
β”œβ”€β”€ blog
β”‚   β”œβ”€β”€ index.js
β”‚   └── [slug].js
└── index.js

This setup allows us to have specific layout components for the rootmost page, as well as a shared layout for all blog-specific pages. When building a custom layout, take care to pass through the children prop, as this represents the page to be rendered.

pages/_layouts/index.js
export default function IndexLayout({ children }) {
  return (
    <>
      <h1>Hello, world</h1>
      <main>{children}</main>
    </>
  );
}
pages/_layouts/blog.js
import { Suspense } from 'react';
import { BlogSkeleton } from 'components/Skeletons';

export default function BlogLayout({ children }) {
  return (
    <>
      <h1>Blog</h1>
      <h2>Things I've written</h2>
      <main>
        <Suspense fallback={<BlogSkeleton />}>
          {children}
        </Suspense>
      </main>
    </>
  );
}
Tip
The astute reader will notice the inclusion of the Suspense component in the above layout examples. While it not strictly necessary to include the Suspense component in a layout, it does offer more control when building out loading states. Nokkio makes aggressive use of code-splitting when building applications and relies onSuspense to resolve each page's component (and potentially its data) before rendering.

This leads us to another important concept–where shared code, reused among pages or layouts, might live.

Sharing components

A common question for folks new to nokkio is "if pages/ is for pages, then where do I put my shared application code?" The answer to this is inside of the components/ directory, which is accessible to any components within pages/. Nokkio automatically resolves all directories from the root of your project:

pages/index.js
import Header from 'components/Header';

export default function Home() {
  return (
    <>
      <Header>Hello, world</Header>
      You are home.
    </>
  );
}

In the above example, the shared Header component would be easily shared among other pages.

What's next?

Learn how to provide your pages with rich data using nokkio's data APIs