wrapRootElement vs wrapPageElement
Table of contents
According to Gatsby documentation, you can use wrapRootElement to wrap your application with provider components and wrapPageElement to wrap your pages with components that won’t get unmounted on page change. But that explanation begs the question: why we don’t do both with wrapPageElement
, and instead, we need two separate APIs to accomplish those tasks? Let’s see their differences to understand why.
Differences
First of all, wrapPageElement
is a child of wrapRootElement
as you can see in the following image:
Additionally, wrapPageElement is a child of the router—wrapRootElement
is not—so it has access to the router props. You can access those props if you destructure the props
field from the first argument:
exports.wrapPageElement = ({ element, props }) => {
return <Layout {...props}>{element}</Layout>;
};
In the following snippet you can see an example of that props
field:
{
"path": "/",
"location": {
"href": "http://localhost:8001/",
"ancestorOrigins": {},
"origin": "http://localhost:8001",
"protocol": "http:",
"host": "localhost:8001",
"hostname": "localhost",
"port": "8001",
"pathname": "/",
"search": "",
"hash": "",
"state": null,
"key": "initial"
},
"pageResources": {
"json": {
"pageContext": {
"isCreatedByStatefulCreatePages": true
}
},
"page": {
"componentChunkName": "component---src-pages-index-js",
"path": "/",
"webpackCompilationHash": "4987305ccd829dc361b7"
}
},
"uri": "/",
"pageContext": {
"isCreatedByStatefulCreatePages": true
},
"pathContext": {
"isCreatedByStatefulCreatePages": true
}
}
As a result, wrapPageElement
renders every time the page changes—wrapRootElement
does not—making it ideal for complex page transitions, or for stuff that need the page path, like an internationalization context provider for example. On the other hand, because wrapRootElement
doesn’t render when the page changes, it’s a good fit for context providers that don’t need the page, like theme or global application state providers. And that’s their biggest difference.
One similarity they share is that they both mount only once, as opposed to a regular Layout
component—that you use inside your pages—that will unmount every time the page changes.
Finally, if you don’t provide an implementation for wrapPageElement
in gatsby-ssr.js
, it will use the implementation from gatsby-browser.js
. This is not true for wrapRootElement
.
Conclusion
Coming back to the initial question, yes, you can use wrapPageElement
element for everything, but it’s better to use it only for providers that need the router props, or for page transition layouts, and use wrapRootElement
for any other provider, like theme and global state providers.
Extra
Here I list some links and the code I used to find the differences.
Links
Code
The gatsby-browser.js
file:
import React from "react";
import Layout from "./src/components/Layout";
export const wrapRootElement = ({ element, ...restProps }, ...args) => {
return (
<Layout
name="wrapRootElement"
props={restProps}
args={args}
mode="browser"
>
{element}
</Layout>
);
};
export const wrapPageElement = ({ element, ...restProps }, ...args) => {
return (
// <Layout name="wrapPageElement" props={{}} args={args} mode="browser">
<Layout
name="wrapPageElement"
props={restProps}
args={args}
mode="browser"
>
{element}
</Layout>
);
};
And the Layout
component:
import React, { useEffect } from "react";
const Layout = ({ name, props, args, mode, children }) => {
useEffect(() => {
console.log(`${name} Layout mounted.`);
console.log(
`${name} Layout restProps:`,
JSON.stringify(props, null, 2)
);
console.log(
`${name} Layout arguments:`,
JSON.stringify(args, null, 2)
);
}, []);
console.log(`${name} Layout rendered.`);
return (
<div data-name={name} data-mode={mode}>
{children}
</div>
);
};
export default Layout;
Other things to read
Popular
- Reveal animations on scroll with react-spring
- Gatsby background image example
- Extremely fast loading with Gatsby and self-hosted fonts