Nested Routes and Views

Learn to add nested views and layouts to your routes, allowing you to create entire apps that can be embedded anywhere.

Occasionally, you’ll find that you have some content that is shared between a section of your app, without being shared across the whole thing. You can’t render it at the top level because it’s not needed for every page, but you don’t want to repeat it across every single page that uses it. For example, the navigation bar on the left of this documentation site is shared by all pages within Navi’s documentation, while not being displayed on Frontend Armory’s courses and blog.

In situations like this, you’ll need a way to render layout components — i.e. components that are shared between pages. And Navi lets you solve this by specifying multiple views.

withView()

While you’ll often map a URL to a view with the route() function, Navi gives you another way to declare views: withView().

withView(
  req => <SomeElementOrComponent />,
  childMatcher
)

The withView() function can be wrapped around route(), mount() or any other matcher. It tells Navi — hey, I want this element to be rendered in place of my top-level <View /> component. There’s just one problem: if you wrap your withView() around a route() that declares its own view, then you’ll be matching two views — and the <View /> component only renders the first.

index.js
routes.js
Layout.js
home.md
about.md
styles.css
import { compose, mount, route, withView } from 'navi'
import React from 'react'
import { View } from 'react-navi'
import Layout from './Layout'

export default compose(
  withView(request =>
    <Layout mountpath={request.mountpath || '/'} />
  ),

  mount({
    '/': route({
      title: "Home",
      getView: () => import('./home.md'),
    }),
    '/about': route({
      title: 'About',
      getView: () => import('./about.md'),
    }),
  })
)
Build In Progress

    Nested <View /> elements

    If you take a look inside the above demo’s Route object, you’ll notice that route.views is an array, and it contains two items.

    It turns out that Navi knows that you’ve matched two views. So how do you render the second one? Easy — you just render another <View /> within the first! And because the nested <View /> can be rendered anywhere within the first one, you can use views to make nested layouts, like this one:

    index.js
    routes.js
    Layout.js
    home.md
    about.md
    styles.css
    import { compose, mount, route, withView } from 'navi'
    import React from 'react'
    import { View } from 'react-navi'
    import Layout from './Layout'
    
    export default compose(
      withView(request =>
        // This is the first view -- it renders the second within a
        // `<Layout>` component.
        <Layout mountpath={request.mountpath || '/'}>
          <View />
        </Layout> 
      ),
    
      mount({
        '/': route({
          title: "Home",
          getView: () => import('./home.md'),
        }),
        '/about': route({
          title: 'About',
          getView: () => import('./about.md'),
        }),
      })
    )
    Build In Progress

    Here’s the golden rule:

    The <View /> component will always render the next view that hasn’t been rendered yet.

    One result of this rule is that it becomes possible to create entire applications with multiple routes, that can be reused by mapping them to a URL and rendering a single <View />. In fact, the Navi website itself is built this way — you can load it independently during development, or you can embed it within another website like Frontend Armory. For more details, take a look at the website’s source.