title |
---|
Migrating from v2 to v3 |
Looking for the v2 docs?
Have you run into something that's not covered here? Add your changes to GitHub!
This is a reference for upgrading your site from Gatsby v2 to Gatsby v3. Since the last major release was in September 2018, Gatsby v3 includes a couple of breaking changes. If you're curious what's new, head over to the v3.0 release notes.
If you want to start fresh, run
npm init gatsby
oryarn create gatsby
in your terminal.
- Updating Your Dependencies
- Handling Breaking Changes
- Future Breaking Changes
- For Plugin Maintainers
- Known Issues
First, you need to update your dependencies.
You need to update your package.json
to use the latest version of Gatsby.
{
"dependencies": {
"gatsby": "^3.0.0"
}
}
Or run
npm install gatsby@latest
Please note: If you use npm 7 you'll want to use the --legacy-peer-deps
option when following the instructions in this guide. For example, the above command would be:
npm install gatsby@latest --legacy-peer-deps
Update your package.json
to use the latest version of Gatsby related packages. You should upgrade any package name that starts with gatsby-*
. Note, this only applies to plugins managed in the gatsbyjs/gatsby repository. If you're using community plugins, they might not be upgraded yet. Many plugins won't need updating so they may keep working (if not, please check their repository for the current status). You can run an npm script to see all outdated dependencies.
npm outdated
Compare the "Wanted" and "Latest" versions and update their versions accordingly. For example, if you have this outdated version:
> npm outdated
Package Current Wanted Latest Location
gatsby-plugin-sharp 2.14.1 2.14.1 3.0.0 test
Install the new package with:
npm install gatsby-plugin-sharp@latest
yarn upgrade-interactive --latest
You'll be given an overview of packages where you can select to upgrade them to latest
.
Using community plugins, you might see warnings like these in your terminal:
warning Plugin gatsby-plugin-acme is not compatible with your gatsby version 3.0.0 - It requires gatsby@^2.32.0
If you are using npm 7, the warning may instead be an error:
npm ERR! ERESOLVE unable to resolve dependency tree
This is because the plugin needs to set its peerDependencies
to the new version of Gatsby (see section for plugin maintainers). While this might indicate that the plugin has incompatibilities, in most cases they should continue to work. When using npm 7, you can pass the --legacy-peer-deps
to ignore the warning and install anyway. Please look for already opened issues or PRs on the plugin's repository to see the status. If you don't see any, help the maintainers by opening an issue or PR yourself! :)
If you run into the scenarios listed below, you will need to use yarn resolutions until the plugin authors upgrade the plugins they maintain.
Gatsby has an amazing ecosystem of plugins that make it easier to get up and running, and to incorporate various data sources and functionality into your Gatsby project. Part of that huge ecosystem includes dependency trees!
Depending on how the plugin authors have declared dependencies (e.g. marking a package as a dependency instead of a peerDependency) within those plugins, there could be a myriad of failures that arise. If you encounter any of these issues when migrating your project to Gatsby Version 3, we recommend that you use Yarn resolutions within your package.json
.
Please note: If your rely on a plugin that is not found within the list of plugins within the Gatsby framework, you very well may need to use the following resolutions in the near term.
The specific resolutions we recommend at this time are found below:
{
"resolutions": {
"graphql": "^15.4.0",
"graphql-compose": "^7.25.0",
"webpack": "^5.24.2"
}
}
When upgrading an already existing project (that has an existing node_modules
folder and package-lock.json
file) you might run into version mismatches for your packages as npm/yarn don't resolve to the latest/correct version. An example would be a version mismatch of webpack@4
and webpack@5
that can throw an error like this:
Error: NormalModuleFactory.afterResolve (ReactRefreshPlugin) is no longer a waterfall hook, but a bailing hook instead.
An effective way to get around this issue is deleting node_modules
and package-lock.json
and then running npm install
again. Alternatively, you can use npm dedupe
.
This section explains breaking changes that were made for Gatsby v3. Most, if not all, of those changes had a deprecation message in v2. In order to successfully update, you'll need to resolve these changes.
We are dropping support for Node 10 as it is approaching maintenance EOL date (2021-04-30).
The new required version of Node is 12.13.0
. See the main changes in Node 12 release notes.
Check Node’s releases document for version statuses.
We tried our best to mitigate as much of the breaking change as we could. Some are sadly inevitable. We suggest looking at the official webpack 5 blog post to get a comprehensive list of what changed.
If you hit any problems along the way, make sure the Gatsby plugin or webpack plugin supports version 5.
If you're using Gatsby's default ESLint rules (no custom eslintrc
file), you shouldn't notice any issues. If you do have a custom ESLint config, make sure to read the ESLint 6 to 7 migration guide
The APIs push
, replace
& navigateTo
in gatsby-link
(an internal package) were deprecated in v2 and now with v3 completely removed. Please use navigate
instead.
import React from "react"
- import { navigateTo, push, replace } from "gatsby"
+ import { navigate } from "gatsby"
const Form = () => (
<form
onSubmit={event => {
event.preventDefault()
- navigateTo("/form-submitted/") // or push() or replace()
+ navigate("/form-submitted/")
}}
>
)
The deprecated __experimentalThemes
key inside gatsby-config.js
was removed. You'll need to define your Gatsby themes inside the plugins
array instead.
module.exports = {
- __experimentalThemes: ["gatsby-theme-blog"]
+ plugins: ["gatsby-theme-blog"]
}
The deprecated API pathContext
was removed. You need to rename instances of it to pageContext
. For example, if you passed information inside your gatsby-node.js
and accessed it in your page:
import React from "react"
- const ContextPage = ({ pathContext }) => (
+ const ContextPage = ({ pageContext }) => (
<main>
<h1>Hello from a page that uses the old pathContext</h1>
<p>It was deprecated in favor of pageContext</p>
- <p>{pathContext.foo}</p>
+ <p>{pageContext.foo}</p>
</main>
)
export default ContextPage
The deprecated API boundActionCreators
was removed. Please rename its instances to actions
to keep the same behavior. For example, in your gatsby-node.js
file:
exports.createPages = (gatsbyArgs, pluginArgs) => {
- const { boundActionCreators } = gatsbyArgs
+ const { actions } = gatsbyArgs
}
The deprecated API deleteNodes
was removed. Please iterate over the nodes
instead and call deleteNode
:
const nodes = ["an-array-of-nodes"]
- deleteNodes(nodes)
+ nodes.forEach(node => deleteNode(node))
The arguments fieldName
and fieldValue
were removed from the createNodeField
API. Please use name
and value
instead.
exports.onCreateNode = ({ node, actions }) => {
const { createNodeField } = actions
createNodeField({
node,
- fieldName: "slug",
- fieldValue: "my-custom-slug",
+ name: "slug",
+ value: "my-custom-slug",
})
}
This API is no longer necessary, as there is an internal check for whether or not a node has changed.
The sizes
and resolutions
queries were deprecated in v2 in favor of fluid
and fixed
.
While fluid
, fixed
, and gatsby-image
will continue to work in v3, we highly recommend migrating to the new gatsby-plugin-image
. Read the Migrating from gatsby-image
to gatsby-plugin-image
guide to learn more about its benefits and how to use it.
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
const Example = ({ data }) => {
<div>
- <Img sizes={data.foo.childImageSharp.sizes} />
- <Img resolutions={data.bar.childImageSharp.resolutions} />
+ <Img fluid={data.foo.childImageSharp.fluid} />
+ <Img fixed={data.bar.childImageSharp.fixed} />
</div>
}
export default Example
export const pageQuery = graphql`
query IndexQuery {
foo: file(relativePath: { regex: "/foo.jpg/" }) {
childImageSharp {
- sizes(maxWidth: 700) {
- ...GatsbyImageSharpSizes_tracedSVG
+ fluid(maxWidth: 700) {
+ ...GatsbyImageSharpFluid_tracedSVG
}
}
}
bar: file(relativePath: { regex: "/bar.jpg/" }) {
childImageSharp {
- resolutions(width: 500) {
- ...GatsbyImageSharpResolutions_withWebp
+ fixed(width: 500) {
+ ...GatsbyImageSharpFixed_withWebp
}
}
}
}
`
Calling touchNode
with a string (the node id) was deprecated in Gatsby v2. Pass the full node
to touchNode
now.
exports.sourceNodes = ({ actions, getNodesByType }) => {
const { touchNode } = actions
- getNodesByType("YourSourceType").forEach(node => touchNode(node.id))
+ getNodesByType("YourSourceType").forEach(node => touchNode(node))
}
Calling deleteNode
with a string (the node id) was deprecated in Gatsby v2. Pass the full node
to deleteNode
now.
exports.onCreateNode = ({ actions, node }) => {
const { deleteNode } = actions
- deleteNode(node.id)
+ deleteNode(node)
}
A couple of gatsby-browser
APIs were removed. In the list below you can find the old APIs and their replacements:
getResourcesForPathnameSync
=>loadPageSync
getResourcesForPathname
=>loadPage
replaceComponentRenderer
=>wrapPageElement
Until now you were able to use the graphql
tag for queries without explicitly importing it from Gatsby. You now have to import it: import { graphql } from 'gatsby'
import React from "react"
+ import { graphql } from "gatsby"
const Page = ({ data }) => (
<div>Show my data: {JSON.stringify(data, null, 2)}</div>
)
export default Page
export const query = graphql`
{
site {
siteMetadata {
description
}
}
}
`
The web moves forward and so do we. ES Modules allow us to better treeshake and generate smaller files. From now on you'll need to import cssModules as: import { box } from './mystyles.module.css'
import React from "react"
- import styles from './mystyles.module.css'
+ import { box } from './mystyles.module.css'
const Box = ({ children }) => (
- <div className={styles.box}>{children}</div>
+ <div className={box}>{children}</div>
)
export default Box
You can also still import all styles using the import * as styles
sytax e.g. import * as styles from './mystyles.module.css'
. However, this won't allow webpack to treeshake your styles so we discourage you from using this syntax.
Assets are handled as ES Modules. Make sure to switch your require functions into imports.
import React from "react"
import { Helmet } from "react-helmet";
+ import myFont from '../assets/fonts/myfont.woff2'
const Layout = ({ children }) => (
<div>
<Helmet>
- <link rel="preload" href={require('../assets/fonts/myfont.woff2')} as="fonts/woff2" crossOrigin="anonymous" type="font/woff2" />
+ <link rel="preload" href={myFont} as="fonts/woff2" crossOrigin="anonymous" type="font/woff2" />
</Helmet>
{children}
</div>
)
export default Layout
If you're using require with expression
or require.context
(which is not recommended), you'll have to append .default
to your require statement to make it work.
import React from "react"
import { Helmet } from "react-helmet";
const Layout = ({ children, font }) => (
<div>
<Helmet>
- <link rel="preload" href={require('../assets/fonts/' + font + '.woff2')} as="fonts/woff2" crossOrigin="anonymous" type="font/woff2" />
+ <link rel="preload" href={require('../assets/fonts/' + font + '.woff2').default} as="fonts/woff2" crossOrigin="anonymous" type="font/woff2" /
</Helmet>
{children}
</div>
)
export default Layout
Some components need you to patch/disable node APIs in the browser, like path
or fs
. webpack removed these automatic polyfills. You now have to manually set them in your configurations:
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
- node: {
- fs: "empty",
- path: "mock",
- },
+ resolve: {
+ alias: {
+ path: require.resolve("path-browserify")
+ },
+ fallback: {
+ fs: false,
+ }
+ }
})
}
If it's still not resolved, the error message should guide you on what else you need to add to your webpack config.
A common error is process is not defined
. webpack 4 polyfilled process automatically in the browser, but with v5 it's not the case anymore.
If you're using process.browser
in your components, you should switch to a window is not undefined check.
import React from "react"
const Base64 = ({ text }) => {
let base64;
- if (process.browser) {
+ if (typeof window !== "undefined") {
base64 = btoa(text)
} else {
base64 = Buffer.from(text).toString('base64')
}
return (
<div>
{base64}
</div>
)
}
export default Base64
If you're using any other process properties, you want to polyfill process.
- Install
process
library -npm install process
- Configure webpack to use the process polyfill.
exports.onCreateWebpackConfig = ({ actions, stage, plugins }) => {
if (stage === 'build-javascript' || stage === 'develop') {
actions.setWebpackConfig({
plugins: [
plugins.provide({ process: 'process/browser' })
]
})
}
}
In v2, backslashes in regex
filters of GraphQL queries had to be escaped
twice, so /\w+/
needed to be written as "/\\\\w+/"
.
In v3, you only need to escape once:
const query = {
allFile(
filter: {
- relativePath: { regex: "/\\\\.png/" }
+ relativePath: { regex: "/\\.png/" }
}
) {
nodes {
relativePath
}
}
}
In v2, the __typename
field used to be added implicitly when querying for a field of abstract type (interface or union).
In v3, __typename
has to be added explicitly in your query:
import React from "react"
import { graphql } from "gatsby"
const Page = ({ data }) => {
if (data.foo.someUnion.__typename === `A`) {
return data.foo.someUnion.a
}
if (data.foo.someUnion.__typename === `B`) {
return data.foo.someUnion.b
}
return null
}
export default Page
export const query = graphql`
{
foo {
someUnion {
+ __typename
... on A { a }
... on B { b }
}
}
}
`
Imagine you have node type Foo
that has several child nodes of type Bar
(so you expect field Foo.childBar
to exist).
In Gatsby v2 this field was added automatically even if inference was disabled for type Foo
.
In Gatsby v3 you must declare a parent-child relationship explicitly for this case:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type Foo implemenst Node @dontInfer {
id: ID!
}
- type Bar implements Node {
+ type Bar implements Node @childOf(types: ["Foo"]) {
id: ID!
}
`)
}
To make upgrading easier, check the CLI output of your site on the latest v2 and follow the suggestions when you see a warning like this:
warning Deprecation warning: In Gatsby v3 fields `Foo.childBar` and `Foo.childrenBar`
will not be added automatically because type `Bar` does not explicitly list type `Foo`
in `childOf` extension.
Add the following type definition to fix this:
type Bar implements Node @childOf(types: ["Foo"]) {
id: ID!
}
https://www.gatsbyjs.com/docs/actions/#createTypes
If you don't see any warnings, you are safe to upgrade to v3.
If this warning is displayed for a type defined by some plugin, open an issue in the plugin repo with a suggestion to upgrade (and a link to this guide).
You can still fix those warnings temporarily in your site's gatsby-node.js
file until it is fixed in the plugin.
Related docs:
Starting with v3, whenever you define a field of complex type, you must also assign the corresponding extension (or a custom resolver):
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type MyType {
- date: Date
+ date: Date @dateformat
- image: File
+ image: File @fileByRelativePath
- authorByEmail: Author
+ authorByEmail: Author @link
}
`)
}
In Gatsby v2, we add those extensions for you automatically but display a deprecation warning.
To make upgrading easier, when you see a warning like the one below, check the CLI output of your site on the latest v2 and follow the suggestions provided.
warning Deprecation warning: adding inferred extension `link` for field Foo.bar
In Gatsby v3, only fields with an explicit directive/extension will be resolved correctly.
Add the following type definition to fix this:
type Foo implements Node {
bar: [Bar] @link(by: "id", from: "bar___NODE")
}
https://www.gatsbyjs.com/docs/actions/#createTypes
If this warning is displayed for a type defined by some plugin, open an issue in the plugin repo with a suggestion to upgrade (and a link to this guide).
You can still fix those warnings temporarily in your site's gatsby-node.js
until it is fixed in the plugin.
If you don't see any warnings, you are safe to upgrade to v3. Read more about custom extensions in this blog post.
Search for noDefaultResolvers
entries and remove them:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
- type Foo implements Node @infer(noDefaultResolvers: true)
+ type Foo implements Node @infer
{
id: ID!
}
- type Bar implements Node @dontInfer(noDefaultResolvers: true)
+ type Foo implements Node @dontInfer
{
id: ID!
}
`)
}
Deprecation announcement for noDefaultResolvers
.
The many
argument is no longer needed for the childOf
directive in Gatsby v3:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
- type Foo implements Node @childOf(types: ["Bar"], many: true)
+ type Foo implements Node @childOf(types: ["Bar"])
{
id: ID!
}
`)
}
For Gatsby v2, nodeModel.runQuery
with firstOnly: false
returns null
when nothing is found.
In v3 it returns an empty array instead.
To upgrade, find all occurrences of runQuery
(with firstOnly: false
or not set) and make sure checks for emptiness
are correct:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
Foo: {
bars: {
resolve(source, args, context, info) {
const result = context.nodeModel.runQuery({
query: {/* */},
type: "Bar",
firstOnly: false,
})
- if (result === null) {
+ if (result.length === 0) {
throw new Error("Not found!")
}
return result
},
},
},
}
createResolvers(resolvers)
}
Note: When using argument firstOnly: true
the returned value is object
or null
.
So do not confuse those two cases.
This section explains deprecations that were made for Gatsby v3. These old behaviors will be removed in v4, at which point they will no longer work. For now, you can still use the old behaviors in v3, but we recommend updating to the new signatures to make future updates easier.
For Gatsby v2 the touchNode
API accepted nodeId
as a named argument. This now has been deprecated in favor of passing the full node
to the function.
exports.sourceNodes = ({ actions, getNodesByType }) => {
const { touchNode } = actions
- getNodesByType("YourSourceType").forEach(node => touchNode({ nodeId: node.id }))
+ getNodesByType("YourSourceType").forEach(node => touchNode(node))
}
In case you only have an ID at hand (e.g. getting it from cache or as __NODE
), you can use the getNode()
API:
exports.sourceNodes = async ({ actions, getNodesByType, cache }) => {
const { touchNode, getNode } = actions
const myNodeId = await cache.get("some-key")
touchNode(getNode(myNodeId))
}
For Gatsby v2, the deleteNode
API accepted node
as a named argument. This now has been deprecated in favor of passing the full node
to the function.
exports.onCreateNode = ({ actions, node }) => {
const { deleteNode } = actions
- deleteNode({ node })
+ deleteNode(node)
}
For Gatsby v2, @nodeInterface
was the recommended way to implement queryable interfaces.
Now it is deprecated in favor of interface inheritance:
exports.createSchemaCustomization = function createSchemaCustomization({ actions }) {
const { createTypes } = actions
createTypes(`
- interface Foo @nodeInterface
+ interface Foo implements Node
{
id: ID!
}
`)
}
JSON modules are coming to the web. JSON modules only allow you to import the default export and no sub-properties. If you do import properties, you'll get a warning along these lines:
Should not import the named export 'myProp' from default-exporting module (only default export is available soon)
import React from "react"
- import { items } from "../data/navigation.json"
+ import navigationData from "../data/navigation.json"
const Navigation = () => (
<nav>
<ul>
- {items.map(item => {
- return <li><a href={item.to}>{item.text}</a></li>
- })}
+ {navigationData.items.map(item => {
+ return <li><a href={item.to}>{item.text}</a></li>
+ })}
</ul>
</nav>
)
export default Navigation
When running community Gatsby plugins, you might see [DEP_WEBPACK]
messages popup during the "Building JavaScript" or the "Building SSR bundle" phase. These often mean that the plugin is not compatible with webpack 5 yet. Contact the Gatsby plugin author or the webpack plugin author to flag this issue. Most of the time Gatsby will build fine, however there are cases that it won't and the reasons why could be cryptic.
Gatsby v3 introduces incremental builds for HTML generation. For this feature to work correctly, Gatsby needs to track all inputs used to generate HTML file. Arbitrary code execution in gatsby-ssr.js
files allow usage of fs
module, which is marked as unsafe and results in disabling of this feature. To migrate, you can use import
instead of fs
:
import * as React from "react"
-import * as fs from "fs"
-import * as path from "path"
+import stylesToInline from "!!raw-loader!/some-auto-generated.css"
export function onRenderBody({ setHeadComponents }) {
- const stylesToInline = fs.readFileSync(path.join(process.cwd(), `some-auto-generated.css`))
setHeadComponents(
<style
dangerouslySetInnerHTML={{
__html: stylesToInline,
}}
/>
)
}
In most cases, you won't have to do anything to be v3 compatible. But one thing you can do to be certain your plugin won't throw any warnings or errors is to set the proper peer dependencies.
gatsby
should be included under peerDependencies
of your plugin and it should specify the proper versions of support.
{
"peerDependencies": {
- "gatsby": "^2.32.0",
+ "gatsby": "^3.0.0",
}
}
This section is a work in progress and will be expanded when necessary. It's a list of known issues you might run into while upgrading Gatsby to v3 and how to solve them.
We vendored reach-router to make it work for React 17. We added a webpack alias so that you can continue using it as usual. However, you might run into an error like this after upgrading:
Generating development JavaScript bundle failed
Can't resolve '@gatsbyjs/reach-router/lib/utils' in '/c/Users/xxx/test/node_modules/gatsby-link'
If you're trying to use a package make sure that '@gatsbyjs/reach-router/lib/utils' is installed. If you're trying to use a local file make sure that the path
is correct.
File: node_modules/gatsby-link/index.js:24:13
To resolve the error above, make sure that you have updated all dependencies. It's also possible that you have an outdated .cache
folder around. Run gatsby clean
to remove the outdated cache.
In some situations the webpack alias will be ignored, so you will need to add your own alias. The most common example is in Jest tests. For these, you should add the following to your Jest config:
Configuring using a jest.config.js
file:
module.exports = {
moduleNameMapper: {
"^@reach/router(.*)": "<rootDir>/node_modules/@gatsbyjs/reach-router$1",
},
}
Configuring using package.json
:
{
"jest": {
"moduleNameMapper": {
"^@reach/router(.*)": "<rootDir>/node_modules/@gatsbyjs/reach-router$1"
}
}
}
You might see errors like these when using Windows or WSL:
Watchpack Error (initial scan): Error: EACCES: permission denied, lstat '/c/DumpStack.log.tmp'
Watchpack Error (initial scan): Error: EACCES: permission denied, lstat '/c/hiberfil.sys'
Watchpack Error (initial scan): Error: EACCES: permission denied, lstat '/c/pagefile.sys'
Watchpack Error (initial scan): Error: EACCES: permission denied, lstat '/c/swapfile.sys'
Gatsby will continue to work. Please track the upstream issue to see how and when this will be fixed.
Workspaces and their hoisting of dependencies can cause you troubles if you incrementally want to update a package. For example, if you use gatsby-plugin-emotion
in multiple packages but only update its version in one, you might end up with multiple versions inside your project. Run yarn why package-name
(in this example yarn why gatsby-plugin-emotion
) to check if different versions are installed.
We recommend updating all dependencies at once and re-checking it with yarn why package-name
. You should only see one version found now.