Migrate to Netlify Today

Netlify announces the next evolution of Gatsby Cloud. Learn more

ContactSign Up
Community Plugin
View plugin on GitHub

gatsby-plugin-portal

If you want to use React Portals in your gatsby site, this is a simple way to add the required div element to your /public/index.html file.

For convenience, I’ve explained a couple of gotchas that you will run into when implementing portals using Gatsby and how to fix them.


What are Portals

Introduced in React 16, portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. Here’s the React documentation on portals

Why use Portals

Portals are great for creating things like re-usable modals for alerts, login dialogs, etc.

Portal Documentation and Tutorials


Portal Gotchas in Gatsby

There are a two gotchas in Gatsby that you need to look out for.

  • There is no index.html where you can simply add a div.

That’s why this plugin exists, so… problem solved. Follow the install and setup instructions below.

  • The document object is not defined during build.

When you create a modal component the document object will not be available during build. I’ve posted some example code below showing you how to get around this.


Why is this plugin necessary?

Adding a portal requires that you insert a div with an id into ./public/index.htmlthat can be used by document.getElementById.

<!-- ./Public/index.html -->
<div id="portal"></div>

Gatsby builds the index.html file when the site is generated, so there is no /public/index.html file to edit. What you need to do is to add the div component using the setPostBodyComponents method of the onRenderBody API in your gatsby-ssr.js file.

There are 3 ways you can do this.

This is the easiest way to do it. It’s a really simple plugin made mostly for convinience. You may have reasons to do this another way. If so, here are two additional options.

If you decide to use this plugin, follow the instructions below. Be sure


Install

npm install --save gatsby-plugin-portal

or

yarn add gatsby-plugin-portal

How to use

Add the plugin to your gatsby-config.js file.

/**
* In your gatsby-config.js
* 
* Available Options
* 
* key: "string"
* A key is needed so that react doesn't complain about arrays
* needing a unique key. This is a string value and will default
* to 'portal' if you leave it out.
* 
* id:"string"
* Sets the id of the div element. It will default to 'portal' if you leave it out.   
*/


// You can resolve it with options like this.
module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-portal`,
      options: {
        key: 'portal',
        id: 'portal',        
      },
    },
  ],
}

// Or add is without options like this.
module.exports = {
  plugins: [`gatsby-plugin-portal`]
}

Gatsby Gotcha - document is undefined

If you follow the react documentation or one of the many useful tutorials on youtube like this one from LevelUp Tutorials you will create a re-usable portal component.

The portal will work in development mode. However, when you build the file, Gatsby will complain that the document object doesn’t exist and refer you to the documentation on this error here.

Here’s a code snippet explaining how to fix the problem.

import { Component } from 'react'
import ReactDOM from 'react-dom'

// Use a ternary operator to make sure that the document object is defined
const portalRoot = typeof document !== `undefined` ? document.getElementById('portal') : null

export default class Portal extends Component {

  constructor() {
    super()
    // Use a ternary operator to make sure that the document object is defined
    this.el = typeof document !== `undefined` ? document.createElement('div') : null
  }

  componentDidMount = () => {    
    portalRoot.appendChild(this.el)
  }

  componentWillUnmount = () => {
    portalRoot.removeChild(this.el)
  }

  render() {
    const { children } = this.props

    // Check that this.el is not null before using ReactDOM.createPortal
    if (this.el) {
      return ReactDOM.createPortal(children, this.el)
    } else {
      return null
    }

  }
}
© 2023 Gatsby, Inc.