paulwhitaker / gatsby-plugin-portal

React Portal Plugin for GatsbyJS
MIT License
5 stars 4 forks source link

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.


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.

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
    }

  }
}