Search code examples
routesnext.jsdynamic-routing

NextJS - Nested routing + subpages with context and layout


I'm currently in the process of migrating one of my react apps to NextJS and am struggling to fit my previous react-router architecture into the nextjs routing.

The page I'm building would look something like this:

enter image description here

When a user goes to a collection/:collectionId, the banner image along with some basic details are fetched. This data should persist across the 3 subpages and should not be refetched, so I want a CollectionContext that wraps the three tabs.

Then for each tab, there are 3 separate data fetches depending on which tab the user is in:

  • collection/:collectionid/items
  • collection/:collectionid/analytics
  • collection/:collectionid/auctions

I would like to have 3 separate contexts for these as well.

My current approach has been a page structure like this:

enter image description here

The issue with this is that items.jsx does not seem to be a child of [address].jsx, and [address].jsx is not a child of collection, so context has not worked for me.

Additionally I have tried to handle the sidebar + banner image using a nested layout, but the layout doesn't seem to be a child of collection either so it does not have access to the context state.


Solution

  • You have probably already figured out the solution but posting an answer so, it can be helpful for others.

    Here is an quick overview of how we can convert the routes in react-router to Next.js routes/files.

    Route in react-router Equivalent file in Next.js
    collection/:collectionid/analytics pages/collection/[collectionid]/analytics.jsx
    collection/:collectionid/auctions pages/collection/[collectionid]/auctions.jsx
    collection/:collectionid/items pages/collection/[collectionid]/items.jsx

    Inside each file, you would be able to access the collectionid as a param to fetch files with data fetching functions like getServerSideProps or getStaticProps like below:

    export async function getServerSideProps(context) {
      const { collectionid } = context.params;
    
      // fetch data based on `collectionid`
      return {
        props: {}, // will be passed to the page component as props
      }
    }
    

    More on dynamic routing here: https://nextjs.org/docs/routing/dynamic-routes

    Regarding the routes for index.js not being child of [address].jsx, and [address].jsx not being child of collection.

    Here is a table to compare what it would look like in react-router.

    Files in screenshots Equivalent route in react-router
    /pages/collection/[address]/analytics.jsx collection/:address/analytics
    /pages/collection/[address]/items.jsx collection/:address/items
    /pages/[address].jsx /:address
    /pages/CollectionContext.jsx /CollectionContext *(probably should not be here if it's a context file?)
    /pages/index.js / (home route)

    *= Any file inside of /pages/ dir is converted into a route/page in Next.js. So, files (hooks, contexts, etc.) that are not intended to be rendered as a page should be removed from this dir.

    To solve this issue:

    items.jsx does not seem to be a child of [address].jsx, and [address].jsx is not a child of collection

    You can remove [address].jsx and add /pages/collection/[address]/index.jsx so, it's equivalent to /pages/collection/address in react-router.

    And to solve this:

    Additionally I have tried to handle the sidebar + banner image using a nested layout, but the layout doesn't seem to be a child of collection either so it does not have access to the context state.

    Based on this:

    When a user goes to a collection/:collectionId, the banner image along with some basic details are fetched. This data should persist across the 3 subpages and should not be refetched, so I want a CollectionContext that wraps the three tabs.

    Then for each tab, there are 3 separate data fetches depending on which tab the user is in:

    • collection/:collectionid/items
    • collection/:collectionid/analytics
    • collection/:collectionid/auctions

    I would like to have 3 separate contexts for these as well.

    With this approach, fetching data (banner image and basic details) on colleciton/:collectionid would make this a bit tricky in case the user visits collection/:collectionid/auctions before visiting collection/:collectionid. So, I would recommend fetching shared data on _app.jsx file and storing it in the context there or in the layout file if you have a shared layout, or with a custom hook that will only fetch the data if it doesn't exist already in the context. And you would have to import all of them in the pages file and they won't be child of the collection.

    layout doesn't seem to be a child of collection either so it does not have access to the context state.

    Hope that helps.