Router `<Set>`s in Redwood 0.28.0

One (of the many!) new things included in Redwood v0.28 is a new component you can use in your Routes.js file: <Set>

<Set> allows you to group a set (get it? :laughing:) of routes and wrap them, using the wrap prop, in a layout, or a context, or whatever you need.

Taking an example from the Redwood tutorial, if you previously had a HomePage and a AboutPage that both did something like this

import BlogLayout from 'src/layouts/BlogLayout'

// ...

const HomePage = () => {
  return (
    <BlogLayout>
      <p>This is the page content</p>
    </BlogLayout>
  )
}

you would move the import statement and the BlogLayout to the routes file instead

import { Router, Route, Set, Private } from '@redwoodjs/router'
import BlogLayout from 'src/layouts/BlogLayout'

const Routes = () => {
  return (
    <Router>
      <Set wrap={BlogLayout}>
        <Route path="/about" page={AboutPage} name="about" />
        <Route path="/" page={HomePage} name="home" />
      </Set>
      <Route notfound page={NotFoundPage} />
    </Router>
  )
}

export default Routes

This fits conceptually very well with how we think about layouts as something that’s wrapping a page and contains any content that’s outside of the page itself.

And as an added bonus your layouts won’t be re-rendered as soon as a page is re-rendered, or even when going from one page to another that’s in the same <Set>.

Wrapping with multiple components

In the example above the wrap prop took a single layout as its value. If you want to wrap more components around your routes, it will also accept an array: wrap={[BlogLayout, PageContext]}

4 Likes

That’s cool.

So if I follow the logic, for a Layout that receives props, the only option is to use a context?

Are the components generated in the array’s initial natural order?

There are a few options

  • Context is one of them.
  • If you want something from the url, you can use useParams() inside your layout
  • If it’s something static you can inline a function <Set wrap={({children}) => <MyLayout propOne="une">{children}</MyLayout>}>

Yes, <Set wrap={[CustomWrapper, GlobalLayout]}><Route ... /></Set> will be

<CustomWrapper>
  <GlobalLayout>
    <Route ... />
  </GlobalLayout>
</CustomWrapper>

Thanks for your question, let me know if there’s anything else you want to know :slight_smile:

1 Like

Well, thanks for your answer! :slight_smile:

I can migrate some of my pages to this new feature, but not all straight away: my layout has a breadcrumbs prop and an actions placeholder. It’s very cool to have those options though, and for my specific case I think I have no choice but to look for Context - which is cool.

Thanks a lot \o/

This sounds like a perfect use-case for a context with some custom hooks. Something like

const { pushBreadcrumb } = useBreadcrumbs()

on pages/in cells where you want to add stuff to your breadcrumb. And then in your layout you can have

const { breadcrumbs } = useBreadcrumbs()

return (
  <ul>
    {breadcrumbs.map((breadcrumb) => <li>{breadcrumb} »</li>)}
  </ul>
)

(with some special handling for the last one, to not show »)

What’s your action placeholder? How do you use that? What is it?

My thoughts as well :stuck_out_tongue:

I’ll try hook before context, great idea, I’m reluctant to play with contexts x).

My actions placeholder is a spot at the same level as the breadcrumbs.
We’re building an application for school management, on the pages where we edit a Teacher, a Student, a Customer, a Session, we systematically have a button at the top to let us delete the entity we’re currently editing.
On some of those pages we have extra buttons, so we expect these to be dynamic :).

( there’s a preprod coming as soon as I manage to fix my netlify deploy, you’d see in context how we use the actions placeholder :slight_smile: )

No, you misunderstood. You’ll need a context to back that hook :slight_smile:

Erf x) allright :), I’ll check it out once v0.28.0 is shipped

There’s a PR on the way that adds another option - any prop you add to <Set> is passed to the layout (and all other components specified in wrap)

2 Likes

ah great! interesting!

@Tobbe
Eventually did it and migrated all appropriate routes to <Set />, also did what we discussed with a hook and context to handle my breadcrumbs and actions :), it :rocket: high!

4 Likes

Great that it worked out for you, and thanks for reporting back :slight_smile:

1 Like