UNPKG

gatsby-plugin-portal

Version:

Plugin to add a div element to the root html file for use by react portal.

146 lines (108 loc) 4.85 kB
# 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](https://reactjs.org/docs/portals.html) ## Why use Portals Portals are great for creating things like re-usable modals for alerts, login dialogs, etc. ## Portal Documentation and Tutorials - [React Documentation](https://reactjs.org/docs/portals.html) - [Creating A Re-useable Portal by Levelup Tutorials](https://www.youtube.com/watch?v=SVQ2l2w-zPM) --- ## 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.html```that can be used by ```document.getElementById```. ```html <!-- ./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. - [Install the gatsby-plugin-portal](https://www.npmjs.com/package/gatsby-plugin-portal) 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. - [Make your own local plugin](https://www.gatsbyjs.org/docs/plugin-authoring/#local-plugins) - [Add the onRenderBody API code to the gatsby-ssr.js file in your project root](https://www.gatsbyjs.org/docs/ssr-apis/#onRenderBody) If you decide to use this plugin, follow the instructions below. Be sure --- ## Install ```javascript npm install --save gatsby-plugin-portal ``` or ```javascript yarn add gatsby-plugin-portal ``` --- ## How to use Add the plugin to your gatsby-config.js file. ```javascript /** * 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](https://reactjs.org/docs/portals.html) or one of the many useful tutorials on youtube [like this one from LevelUp Tutorials](https://www.youtube.com/watch?v=SVQ2l2w-zPM) 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](https://www.gatsbyjs.org/docs/debugging-html-builds/). Here's a code snippet explaining how to fix the problem. ```javascript 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 } } } ```