gatsby-node.js createPage data not passed to component in pageContext

Created on 31 Dec 2018  ·  37Comments  ·  Source: gatsbyjs/gatsby

The call to createPage in gatsby-node.js does not pass the fetched data (or even hard-coded test data) into the page component in pageContext. It is always undefined. I posted this on SO, it has details and example code: https://stackoverflow.com/questions/53990208/gatsby-node-js-createpages-is-not-sending-data-to-component-in-pagecontext

Describe the issue that you're seeing.

Easiest test is add some hard-coded test JSON inside the gatsby-node.js exports.createPages. Pass that to the page compoent via createPage function call. In the page component, the data in pageContext is always "undefined"

Clear steps describing how to reproduce the issue. Please please please link to a demo project if possible, this makes your issue _much_ easier to diagnose (seriously).

The page component pageContext input param should contain the JSON data.

What should happen?

The page component pageContext input param is always undefined.

What happened.

I did what the message below says. I already have gatsby-cli installed but I went ahead and re-installed as directed. Same both times the gatsby info --clipboard does not work. Spits a bunch of build output and errors and nothing is on clipboard. Sorry, but I cannot complete this section.

Run gatsby info --clipboard in your project directory and paste the output here. Not working? You may need to update your global gatsby-cli - npm install -g gatsby-cli

question or discussion

Most helpful comment

@waifo Just took another look and realised what it causing this. employee.js is inside the the pages directory and Gatsby creates pages for each of the files in that directory automatically. In the case where it does so automatically, it doesn't get any context and hence build fails.

A simple fix is to rename pages to templates (and also change it in the paths in gatsby-node)

It took me HOURS trying to reach to this conclusion.

I've copied every bit of the brilliant answer from @jonniebigodes , and it still wouldn't work. Then I realised what was the only thing I didn't change.

There is nothing in the docs saying that you can't createPages from inside the (_irony ahead_) pages folder. Even without a citation in the docs, it could have been easier if there was a simple error triggered by the word 'pages' inside a component:

createPage({
  path: '/',
  component: require.resolve('./src/pages/index.js'),
  context: { someCode }
})

⚠ We see 'pages' in your component path. If your component is inside the 'pages' folder, move it to another directory, as Gatsby won't see your component there from inside createPage. ⚠

All 37 comments

Hey @locohost

Can you please link to a minimal reproduction repository if possible?

@locohost based on the information at hand i'm going to try and see if i can help you with your issue. With that i'm going to break my answer into smaller parts and with that see if you can solve it.

  • In order to emulate to the most your issue i started by creating a simple server with express with the following dummy code:
const express = require('express')
const app = express()
app.set('port', process.env.PORT || 3000)
app.get('/allMessages', (req, res) => {
  const dummyResult = [
    {
      id: 1,
      message: 'dummy1'
    },
    {
      id: 2,
      message: 'desc2'
    },
    {
      id: 3,
      message: 'desc3'
    },
    {
      id: 4,
      message: 'desc4'
    },
    {
      id: 5,
      message: 'desc5'
    },
    {
      id: 6,
      message: 'desc6'
    },
    {
      id: 7,
      message: 'desc7'
    },
    {
      id: 8,
      message: 'desc8'
    },
    {
      id: 9,
      message: 'desc9'
    },
    {
      id: 10,
      message: 'desc10'
    },
    {
      id: 11,
      message: 'desc11'
    },
    {
      id: 12,
      message: 'desc12'
    },
    {
      id: 13,

      message: 'desc13'
    },
    {
      id: 14,
      message: 'desc14'
    },
    {
      id: 15,
      message: 'desc15'
    },
    {
      id: 16,
      message: 'desc16'
    },
    {
      id: 17,
      message: 'desc17'
    },
    {
      id: 18,
      message: 'desc18'
    },
    {
      id: 19,

      message: 'desc19'
    },
    {
      id: 20,
      message: 'desc20'
    },
    {
      id: 21,
      message: 'desc21'
    },
    {
      id: 22,
      message: 'desc22'
    }
  ]
  return res.status(200).json(dummyResult)
})

app.listen(app.get('port'), error => {
  if (error) {
    console.log('====================================')
    console.log(`Error: ${error}`)
    console.log('====================================')
  } else {
    console.log('====================================')
    console.log(`server_loco is running on port ${app.get('port')}`)
    console.log('====================================')
  }
})

this server is running in http://localhost:3000

  • I then proceeded to create a new gatsby site with using the default starter.
  • And based on your code that you posted in stackoverflow and the gatsby's documentation to generate pages "on the fly" located here. I changed my gatsby-node.js file a little bit, resulting in the following:
/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */
const axios = require('axios')
// You can delete this file if you're not using it
exports.createPages = async ({ actions: { createPage } }) => {
    try {
        const response = await axios.get('http://localhost:3000/allMessages');
        const allMessagesComponent=require.resolve('./src/templates/Dashboard.js')
        const MessageComponent=require.resolve('./src/templates/Message.js')
        createPage({
            path:'/messages',
            component:allMessagesComponent,
            context:{
                allMessages:response.data
            }
        })
        response.data.forEach(element => {
            createPage({
                path:`/message/${element.id}`,
                component:MessageComponent,
                context:{
                    messageContent:element.message
                }
            })
        });

        return Promise.resolve()
    } catch (error) {
        return Promise.reject(new Error(error))
    }
};
  • The contents of Dashboard.js is somewhat similiar to your own code as you can see bellow:
import React from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import {Link} from 'gatsby'

const DashboardPage = props => {
  const {pageContext} = props
  const {allMessages} = pageContext
  return (
    <Layout>
      <SEO title="Dashboard" keywords={[`gatsby`, `application`, `react`]} />
      <h1>Dashboard</h1>
      <p>Welcome to your new Gatsby site.</p>
      <ul>
        {allMessages.map(item => {
          return (
            <li
              key={item.id}
              style={{
                textAlign: 'center',
                listStyle: 'none',
                display: 'inline-block'
              }}>
              <Link to={`/message/${item.id}`}>
                <p>{item.message}</p>
              </Link>
            </li>
          )
        })}
      </ul>
    </Layout>
  )
}
export default DashboardPage
  • The contents of the Message.js file is the following:
import React from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import {Link} from 'gatsby'

const Message = props => {
  const {pageContext} = props
  const {messageContent} = pageContext
  return (
    <Layout>
      <SEO title={`Message=`} keywords={[`gatsby`, `application`, `react`]} />
      <h1>Message Contents</h1>
      <h4>This is your message contents:{messageContent}</h4>
      <Link to="/messages">Go back</Link>
    </Layout>
  )
}
export default Message

  • When i issue gatsby develop and i have the server running i'm presented with the image bellow when i go go to http://localhost:8000/messages
    list_of_messages

  • Navigating to a random message produces the following:
    one_message

As you can see the property was injected in pageContext on both ends and all of the pages are rendered with the data that the server sends back.
I hope i could be of assistance with your issue and helped correct it or at least point you in the right direction. Feel free to provide some feedback on the issue and also if you want i can make the example code available for you.

Sorry for taking so long to reply but... Wow! Thank you @jonniebigodes! I'll give this all another try. I reeeeeallly want to use Gatsby on Netlify :-)

I'm trying to remember: I think that I did have code like above working _locally_, but it was erroring when deployed to Netlify. I'll work on this again today. Thanks again @jonniebigodes :-)

@jonniebigodes Yes I have this same example working locally. Thank you for that. I will investigate the code and see how I can refactor to deploy to Netlify and test.

Ok so the confusing parts are...

  • Why in the gatsby-node.js is the "pageContext" passed into createPage as "context", but then in the component, in props, it comes out as "pageContext"? This was absolutely tripping me up.
  • Why in gatsby-node.js do I have to use require.resolve(... inside the exports.createPages to load my components? Why can't I import them (I tried, it doesn't work) at the top where axios is loaded? This is not normal so I don't follow. This may simply be my dumbness here, not sure :-/

Sorry about the confusion, @locohost

Why in the gatsby-node.js is the "pageContext" passed into createPage as "context", but then in the component, in props, it comes out as "pageContext"? This was absolutely tripping me up.

It's called pageContext because there could be multiple contexts in case of an app where the user wraps the root element with a Provider and we wanted to be explicit

Why in gatsby-node.js do I have to use require.resolve(... inside the exports.createPages to load my components? Why can't I import them (I tried, it doesn't work) at the top where axios is loaded? This is not normal so I don't follow. This may simply be my dumbness here, not sure :-/

This is because we need to pass the path of the template component to createPage and not the component itself

I do understand the frustration though and we should definitely make this clearer in the documentation!

@locohost like @sidharthachatterjee said and expanding a bit.
Regarding your first point.

During the build process, the inner workings of gatsby will generate a "metric ton of nodes" (pardon the bad pun), if you open the devtools you'll see them, one example i could give you is the following:
nodes_galore

As you see here there are a good ammount of "contexts" being generated and used. These are generated by the inner workings of gatsby, for specific types of things that need to be passed down or exist alltogether. From my meager knowledge of gatsby and some reasoning, what i came to realize is that the property object "context" was declared with this name, due to a implementation feature, meaning, when the actual function/method was being created the person declared the argument with that name. And when the code is executed and returned the actual result comes back in the form of a object with the name "PageContext" that is then provived to any and every page that will use it under a React Prop.
I've got agree with you, for a person that will have to use that api function for any particular case, he/she will be hit with a "scratch your head and what" moment (once again pardon the bad pun), i know i did and even during this weekend i was banging my head on the wall trying to figure out why a similar issue was not working. The documentation does not cover this situation fully and my personal opinion is that the it would be a good thing having jsdocs cover this and provide a explanation of what i going in and what is going out of that particular function/method. Again having gatsby as a open source it means that it's constantly evolving and mutating and with that sooner or later all of the apis will be fully documented.

Regarding your second point.

Require.Resolve is one of the approaches possible, i use it because it was the one that i found out while experimenting with the api, Alternatively you can use path.resolve. What either of them will do, is when you issue the command gatsby develop or gatsby build instruct gatsby to look for a particular component in the path that was provided when constructing the particular page that will be hosted on the specified path. Otherwise it would not know how to build, or based on what component to build the page. Don't forget that is is a page that you are creating programmatically, not a standard one, where you put it on pages folder and gatsby will pick up on it automattically. In this case you have to "teach" gatsby on what you want and how you want it in full.

Sorry for the super long comment, i hope i could shed some light on the issue.

Why data passed in gatsby-node.js file as context is not received in components in pages folder during gatsby build..?
can anybody please explain
Gatsby develop is running fine but Gatsby build is failing

@waifo could you provide a reproduction? Page context (from createPage) is a pretty crucial functionality in Gatsby so we would love to take a look to help you resolve this issue!

Thanks!

@waifo did you go read my comment above?

@DSchau
I have asked a question on stackoverflow
Please check if it is helpful
https://stackoverflow.com/questions/54402112/gatsby-develop-command-runs-but-gatsby-build-gives-error

@jonniebigodes
Ya iread that but i am unable to understan the real issue

if you want i can put all of the code in a repository and you can see it for yourself, or you create a github repo with the code and share so it can be looked at. Sounds good

Hey @waifo

Just took a look at your question on Stack Overflow. Unfortunately we'll need more info to investigate what is happening here! Could you please link to a minimal reproduction?

@sidharthachatterjee @jonniebigodes
I am creating pages dynamically in gatsby-node.js file

 //Create a page for each Product.
  allProducts.map((edge)=>{
    let product = edge.node
    createPage({
      path:`/product/${product.id}`,
      component: require.resolve('./src/pages/product.js'),
      context:{product}
    })
  })

i have product component in pages where i am showing all properties of product
example

 <img src={product.imgSrc }alt=""/>

Accessing these properties for product in product component is fine when i am running gatsby develop
but building up static html file fails when i do gatsby build

Error is

> 50 |             <img src={product.imgSrc }alt=""/>
     |                               ^



  WebpackError: TypeError: Cannot read property 'imgSrc' of undefined

My question is why it is failing at build but not at develop
am i doing anything wrong..?

@waifo that could be an issue with a specific page not _all_ pages right? Is it possible you have a null product?

And sorry--once more we can really help you much more effectively if you are able to link to a Github repository demonstrating the issue.

My question is why it is failing at build but not at develop
am i doing anything wrong..?

Looking at the excerpt, I'm not sure @waifo

Would love to help resolve this but will need to be able to reproduce what you're seeing! Can you link to a repo? Will be glad to take a look and help fix this

@waifo <img src={product.imgSrc }alt=""/> you have an error here, it should be <img src={product.imgSrc } alt=""/>.
Check the allProducts, beforehand, meaning console.log(....) it in gatsby-node.js to see if any of them has a null in that specific property.

If you can share the entire page code, this because, while reading the code from your post on stackoverflow it seems to me that probably you forgot to destructure, i.e, to extract the property from the props. I might be wrong. But if you don't mind i would like to check it.

when i am doing console.log in gatsby-node.js, i am getting all the products
Destructuring is fine, it runs when i do gatsby develop all products pages are created and respective data comes on the page

@sidharthachatterjee @jonniebigodes
You can clone this repo https://github.com/waifo/gatsby-test.git
Steps to reproduce
1 run gatsby develop
2 navigate to localhost:abcd/employees
You will see list of employees clicking onto each employee you will get details of each employee

Now run gatsby build
You will get error

Just took a look at the repo @waifo

One of the employee objects has first_name and last_name set to null
screenshot 2019-01-29 21 07 18
You should be able to fix this by adding a null check in the React component or filtering out bad data when fetching and creating nodes. The reason it works during develop and breaks during build is because we don't do SSR during develop.

Hope this helps!

Closing this issue for the moment since this is unrelated to the original question which seems to be resolved. Please feel free to open another issue if you're still seeing a problem.

@sidharthachatterjee
Thanx for looking into it
I have removed null value from db, still i am getting same error

@waifo Your API is still returning an empty object for that employee
screenshot 2019-01-29 21 52 23

I don't see any null value returned from this api https://emp-server.herokuapp.com/getEmp

@waifo i've just cloned your repo, let me set it up and see what i can come up with. Sounds good?

@jonniebigodes sure

@waifo Just took another look and realised what it causing this. employee.js is inside the the pages directory and Gatsby creates pages for each of the files in that directory automatically. In the case where it does so automatically, it doesn't get any context and hence build fails.

A simple fix is to rename pages to templates (and also change it in the paths in gatsby-node)

@sidharthachatterjee
Ya doing so fixes the issue

@sidharthachatterjee @jonniebigodes
Thank you very much for your time and effort, i really appreciate it

@waifo no need to thanks, glad you solved your issue 👍

(Y)

Just wanted to +1 the issue and solution:

@sidharthachatterjee

Just took another look and realised what it causing this. employee.js is inside the pages directory and Gatsby creates pages for each of the files in that directory automatically. In the case where it does so automatically, it doesn't get any context and hence build fails.

A simple fix is to rename pages to templates (and also change it in the paths in gatsby-node)

I was following the tutorial and decided to put my new "page" into the "page" directory, oops.

@waifo Just took another look and realised what it causing this. employee.js is inside the the pages directory and Gatsby creates pages for each of the files in that directory automatically. In the case where it does so automatically, it doesn't get any context and hence build fails.

A simple fix is to rename pages to templates (and also change it in the paths in gatsby-node)

It took me HOURS trying to reach to this conclusion.

I've copied every bit of the brilliant answer from @jonniebigodes , and it still wouldn't work. Then I realised what was the only thing I didn't change.

There is nothing in the docs saying that you can't createPages from inside the (_irony ahead_) pages folder. Even without a citation in the docs, it could have been easier if there was a simple error triggered by the word 'pages' inside a component:

createPage({
  path: '/',
  component: require.resolve('./src/pages/index.js'),
  context: { someCode }
})

⚠ We see 'pages' in your component path. If your component is inside the 'pages' folder, move it to another directory, as Gatsby won't see your component there from inside createPage. ⚠

@estevanmaito That seems like a fair note to add to the docs. Want to PR it in?

@estevanmaito That seems like a fair note to add to the docs. Want to PR it in?

Sure.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jimfilippou picture jimfilippou  ·  3Comments

ghost picture ghost  ·  3Comments

Oppenheimer1 picture Oppenheimer1  ·  3Comments

KyleAMathews picture KyleAMathews  ·  3Comments

dustinhorton picture dustinhorton  ·  3Comments