Photo by Rodrigo Pinto

Passing data between pages in GatsbyJS

Due to the way Gatsby is build, it has particular ways to transfer information between pages. It is more limited than an app built with Crete-React-App, for instance. Yet, it is surprising how much it can be achieved.

Let’s take a look at a few ways to move data between pages with Gatsby.

Contents

  1. Passing data to another page with Link

  2. Passing data to child components

    2.1. Sending data back to parent

  3. Passing a callback to another page

    3.1. With a component

    3.2. With Redux

This is the most straight-forward way to transfer data between pages in Gatsby. As indicated in its documentation, you can put any variable in a property called state when using links, and retrieve it from a variable called location in the second page.

All done for you for free by Gatsby’s router.

Taking as example an app built with gatsby new gatsby-site, which has two pages, index.js and page-2.js:

On the first page, index.js, we include a variable “username” in the link:

src/pages/index.js
<Link to="/page-2/" state={{ username: 'Adam' }} >Go to page 2</Link>

And retrieve it on page-2.js:

src/pages/page-2.js
const SecondPage = ({ location }) => {
	return (
		<Layout>
			<SEO title="Page two" />
      <h1>Hi from the second page, {location.state.username}</h1> // highlight-line
      <p>Welcome to page 2</p>
      <Link to="/">Go back to the homepage</Link>
    </Layout>
	)
}

Passing data to child components

The previous example transfers data between two different pages. When page-2 is loaded, the first page, index-js, disappears completely.

But when your first page has child components, both the parent and the child remain loaded at the same time, and transfer of data can be achieved on a different way:

src/pages/index.js
import CustomButton from '../components/custom-button' // highlight-line

const IndexPage = () => {
	return (
		<Layout>
      <SEO title="Home" />
      <h1>Hi people</h1>
      <p>Welcome to your new Gatsby site.</p>
      <p>Now go build something great.</p>
      <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
        <Image />
      </div>

      <CustomButtom title='Click to change color' /> // highlight-line

      <Link to="/page-2/">Go to page 2</Link>
    </Layout>
	)
}

And the child component can use the data by doing:

src/components/custom-button.js
import React from 'react'

const CustomButton = (props) => {
	return (
		<button>{props.title}</button>
	)
}

Sending data back to parent

This allows a lot more liberty than the previous example with Link. Here, it is possible to send a callback to the child component, and use it to move data back to the parent:

src/pages/index.js
import React, { useState } from 'react' // highlight-line

const IndexPage = () => {
	let [clicked, setClicked] = useState(false) // highlight-line

	let callback = e => { // highlight-line
		setTitleColor(e) // highlight-line
	} // highlight-line

	return (
		<Layout>
      <SEO title="Home" />
      <h1>Hi people</h1>
      <p>Welcome to your new Gatsby site.</p>
      <p>Now go build something great.</p>
      <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
        <Image />
      </div>

      <CustomButtom title='Click to change color' cb={callback}/> // highlight-line

      <Link to="/page-2/">Go to page 2</Link>
    </Layout>
	)
}

And the child component uses the callback by doing:

src/components/custom-button.js
import React from 'react'

const CustomButton = (props) => {
	let handleClick = () { // highlight-line
		props.cb() // highlight-line
	} // highlight-line

	return (
		<button onClick={handleClick}>{props.title}</button> // highlight-line
	)
}

Link will not allow you to do this, as the first page is no longer available after page-2 is displayed.

But fret not! Gatsby still has your back. You can achieve this by using a wrapper:

Passing a callback to another page

With a component

With a regular React app, like the one created with create-react-app, this task can be achieved with the router. But Gatsby doesn’t expose its router to be configured that way.

Instead, it offers gatsby-browser.js, where you can use the Gatsby Browser API. The endpoints in this API allow adding powerful features to the application, ranging from routing to hydration.

For our purposes we will use one very handful API called wrapPageElement. It is a wrapper for pages that won’t be unmounted on page changes.

Gatsby documentation gives an example that wraps the Layout component:

gatsby-browser.js
const React = require("react")
const Layout = require("./src/components/layout").default

exports.wrapPageElement = ({ element, props }) => {
  // props provide same data to Layout as Page element will get
  // including location, data, etc - you don't need to pass it
  return <Layout {...props}>{element}</Layout>
}

But you can use any component you want.

With Redux

And that is not all. For even greater control over the data that is passed around through the application, you can add Redux.

And some people say Gatsby is just to render static pages.

First, add Redux to the application

terminal
npm install --save redux react-redux

Create a folder called /src/state where you can add actions and reducers. They will be called from gatsby-browser, but instead of wrapPageElement we use wrapRootElement:

gatsby-browser.js
const React = require("react")
const { Provider } = require("react-redux")

const createStore = require("./src/state/createStore")
const store = createStore()

exports.wrapRootElement = ({ element }) => {
  return (
    <Provider store={store}>
      {element}
    </Provider>
  )
}

Pretty neat!

May 14, 2020.