3

EDIT: I forgot to mention that my site contains around 25k pages built from this template. The answer from Derek Nguyen will work for smaller sites with a small amount of pages, but when this is scaled up the matchPath data is stored in the JS, resulting in massive bundle sizes. (Around 3.1mb for me!)

The Challenge

I need to create a page using createPage which contains a sub-routing system in which the default route is built to static HTML, but any other routes are client-only.

The Problem

I have a template component used for static HTML generation in the createPage method, but also a component in the pages/ directory used to assign a matchPath to for the client-only routes.

On the latest version of Gatsby, the template component renders for the default route, but then the "page" component renders for the client-only ones, leading to full page re-renders when I only want the sub-route to render, as there is data in the parent route which needs to persist.

It seems wrong in my head to have two components with two routers in, but I can't think of any other way to achieve what I am trying to achieve.

Here is an example of my routing set up:

Template used for createPage

<Router>
  <ContainerComponent
     path={urlFromGraphQL}
  >
    <DefaultRoute
      default
    />
  </ContainerComponent>
</Router>

Client only component in /pages

<Router>
  <ContainerComponent
     path="/some-route/:slug/:id"
  >
    <DefaultRoute
      default
    />
    <SecondTabRoute
      path="second-tab-route"
    />
    <ThirdTabRoute
      path="third-tab-route"
    />
  </ContainerComponent>
</Router>

I have also replicated the behaviour in this codesandbox. Make sure to go to https://hkhbb.sse.codesandbox.io/test/foo/bar to see the correct output.

I have created an issue in github about this but have yet to have a reply.

Community
  • 1
  • 1
C Lindsay
  • 209
  • 1
  • 4

1 Answers1

1

Problem

It looks like you're mixing statically generated page & dynamic page, i.e you're both generating a page at /test/foo/bar while matching test/:param1/:param2 in test.js.

Statically generated pages will have higher specificity than dynamic pages, so Gatsby thinks test/foo/bar & test/foo/bar/second-route are 2 different pages instead of being 2 dynamic routes from the same static page.

Potential solution

What you'd need to do in this case is to make both /test & /test/foo/bar root page for dynamic routes. This way, a random match such as /test/one/two will be a dynamic route of /test; while /test/foo/bar/second-route will be a dynamic route of /test/foo/bar.

Since you've already creating /test/foo/bar programmatically, making it a root for dynamic pages is trivial:

  // gatsby-node.js
  exports.createPages = async ({ actions }) => {
    const { createPage } = actions
    const pathFromGraphQL = "/test/foo/bar"

    createPage({
      component: path.resolve(`./src/templates/template.js`),
      context: {
        url: pathFromGraphQL,
      },
      path: pathFromGraphQL,
+     matchPath: `${pathFromGraphQL}/*`,
    })
  }

The issue is now that the 'template.js' is not exactly a template anymore, it'd have to contain components & routes that are specific to test/foo/bar. Depending on your requirements this could be a show stopper, or it could be somehow circumvented.

I edit your fork a little bit to demonstrate this; beside the code change above code from pages/test.js is moved to templates/template.js & I add a dummy component for pages/test.js to show that it'll still handle other dynamic route:

Also, if you decide to do this, be aware of this 'wont-fix' bug.

Derek Nguyen
  • 11,294
  • 1
  • 40
  • 64
  • 1
    Thanks! I forgot to mention that in my case, we generate 25k pages based from these templates. I went down that route of supplying a `matchPath` directly in the `createPage` options, and you're right it works fine. The only issue is that since Gatsby v2.9.0, an array containing `matchPath` data is bundled into the JS. This results in a massive JS bundle for sites with a large amount of pages. I initially created an [issue](https://github.com/gatsbyjs/gatsby/issues/14954) about this, which led onto the one I currently have open! – C Lindsay Aug 12 '19 at 07:49
  • wow yeah, this won't help you then :/I'll watch the issue & see if another solution come up – Derek Nguyen Aug 12 '19 at 09:56