How to use external React components in your Gutenberg blocks

Gutenberg itself already exposes a lot of components ready to be re-used in our custom blocks. Most of these are located in wp.components and wp.blocks, and they include helpful building blocks for every Gutenberg block: Text Controls, Toggles, Tooltips, Icon Buttons, Tabs, and many many others. Gutenberg’s native component library pretty much has us covered on all basic cases, on every kind of basic UI control we might need but still there are cases where we might need to take it a step further on some kind of more specialized custom block.

For example, while we were developing the new Icon Box block for our Gutenbee plugin we needed an Autocomplete Select component for our icon library, which Gutenberg does not provide (understandably):

For our Icon Box block we wanted to use the extremely flexible react-select library which covers all our needs.

Thankfully, as everything in Gutenberg is built on top of React, every single component in the vast React ecosystem is compatible and can be used in our custom blocks as well! All we need to do is npm install them and require them inside our codebase. However there’s one problem, if we were to just install any React component from npm, required it, and tried to compile our code we’d get something like:

ERROR in ./node_modules/react-select/dist/react-select.es.js
Module not found: Error: Can't resolve 'react' in '/node_modules/react-select/dist'

ERROR in ./node_modules/react-select/dist/react-select.es.js
Module not found: Error: Can't resolve 'react-dom' in '/node_modules/react-select/dist'

That’s because react-select (as all React components) depends upon React and ReactDOM. Webpack (our bundle builder) doesn’t really know where to find React since we haven’t installed it as a project dependency.

The first solution would be to simply install react and react-dom from npm.  This will easily solve everything, as React and ReactDOM would be fixed as dependencies in our project, but has the drawback of increasing our bundle size by about ~100kb as that’s the total filesize of React plus ReactDOM (minified). Another more serious drawback of this solution is that we’re going to end up with a disparity between the React version that Gutenberg uses and the version that our plugin uses. What we’d really really want would be to have a guaranteed version parity (i.e. use the same React version as WordPress).

Thankfully, Gutenberg also exposes React as a library in the global scope (the browser’s window object) and we can use that same React object with just a tiny adjustment in our Webpack configuration.

Webpack externals

Webpack provides a powerful externals property in its configuration which allows us to do just that.

The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer’s environment.

The consumer’s environment in our case is the browser’s window object and Gutenberg makes sure to populate it with the React library. All we need to do is declare React and ReactDOM as an external dependency in our Webpack config:

// ...
const webpackConfig = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'myplugin.build.js',
    libraryTarget: 'window',
  },
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM',
  },
};
// ...
webpack.config.js

And that’s it! Our module will compile perfectly now and use Gutenberg’s (i.e. WordPress’s) React, with same version guarantees and all!

In case you have been following our blog and you’ve read and implemented how to import Gutenberg’s native libraries as ES Modules you can achieve the same outcome by modifying the configuration we provided there by adding React and ReactDOM as keys to the initial object we’re passing into our reduce method:

const wplib = [
  'blocks',
  'components',
  'date',
  'editor',
  'element',
  'i18n',
  'utils',
  'data',
];

const webpackConfig = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'myplugin.build.js',
    library: ['wp', '[name]'],
    libraryTarget: 'window',
  },
  externals: wplib.reduce((externals, lib) => {
    externals[`wp.${lib}`] = {
      window: ['wp', lib],
    };

    return externals;
  }, {
    'react': 'React',
    'react-dom': 'ReactDOM',
  }),
}
webpack.config.js

The above code will handle everything, importing Gutenberg’s native libraries as ES modules and exposing React and ReactDOM into our bundle as a global dependency.

Related Articles

11 comments

  1. Christoph says:

    Thank you, that was very helpful!

    I started building with cgb-scripts and it took me a while to find the location where to apply the settings: node_modules/cgb-scripts/config/webpack.config.dev.js (or webpack.config.prod.js) where I pasted

    externals: {
    ‘react’: ‘React’,
    ‘react-dom’: ‘ReactDOM’,
    },

    into module.exports = { … }. Do you know about any location where it won’t be overwritten by the next cgb-scripts update?

    It didn’t pick up the webpack.config.js from the plugin location.

    1. Vassilis Mastorostergios says:

      I’m afraid there’s no good way to have this in create-guten-block and not be overidden by an update or another npm install. If you want to do this properly you should really eject from (npm run eject) where you’ll have access to CGB’s webpack config and everything but from that point onwards you’d have to maintain that configuration yourself (no going back and you won’t be able to simply update CGB from npm). The other option would be to communicate this need to the CGB repo, perhaps the maintainers there will be helpful and somehow add it in their configuration. Hope this helps!

      1. Rhys Clay says:

        Thank you so much for this article I would be totally lost without this!

        There is a PR in CGB at the moment to add externals to the webpack config. For now you can just manually override the cgb-scripts/config files from here:
        https://github.com/ahmadawais/create-guten-block/tree/master/packages/cgb-scripts/config

        PS its slated for the v2.0 release

        1. Vassilis Mastorostergios says:

          Nice! Thanks for the heads up! :)

  2. Error in line 9 webpack.config.js….please help what did i miss??

    1. Vassilis Mastorostergios says:

      Can you paste your entire webpack.config.js ?

      1. Jonathan says:

        In the first webpack.config.js there’s a comma missing in line 10 after ‘react’: ‘React’

        1. Vassilis Mastorostergios says:

          Good catch! Thanks! :)

  3. Nestor Segura says:

    This work fine for the edit part of a gutenberg block, but the save part doesn’t recognize the external library.

    I am using pure-react-carousel library, and on edit mode it works fine, but not by the save function, any tips?

    1. Vassilis Mastorostergios says:

      If you need to add it to the save function you would need to load React and a React build along with pure-react-carousel in the front end and then run the entire React pipeline on the frontend of your website. It’s a bit involved but not impossible, although personally I wouldn’t recommend it.

  4. Anh Tran says:

    This post is very useful. I have struggled with the setup for custom React pages inside WP admin and this is what I need. Thanks a lot for sharing!

Leave a Reply

Your email address will not be published. Required fields are marked *