Building Wordpress Gutenberg Blocks with Vue.js
Tags: vue, wordpress, javascript
Published: 2018-08-01 16:25:27

---
title: Building Wordpress Gutenberg Blocks with Vue.js
published: true
tags: vue, wordpress, javascript
canonical_url: https://zendev.com/2018/07/31/wordpress-gutenberg-blocks-vue.html
---

Wordpress's new Gutenberg editor is an extremely hot new topic in the web development world. With it, Wordpress is fully embracing React and modern JavaScript, bringing millions of developers into the modern front-end world, and creating a massive audience for existing Frontend developers.

While React is the primary framework supported for Gutenberg, there have been indications that implementing Gutenberg blocks using other JavaScript frameworks like Vue.js [should be possible using libraries like vuera](https://github.com/WordPress/gutenberg/issues/4344), so I decided to explore how to get this to happen and how well it works.

**TL/DR: We can implement basic blocks in Vue without much trouble, but we quickly run into limitations if we try to use Wordpress builtins like `BlockControls` or `InnerContent`.**

## Setting Up

First off, we're going to set up the plugin with [create-guten-block](https://github.com/ahmadawais/create-guten-block) scaffold.

Go into the `wp-content/plugins` directory and set up a new plugin:

```
npm install -g create-guten-block
npx create-guten-block vuetenberg
```

This creates a scaffold with a very basic initial block that lives in `src/block/block.js`. Once you `activate` it inyour wordpress admin, you'll be able to see it.

For the purpose of this blog post, I'm not going to change much about the functionality of this block, simply convert it to using Vue and Vue Single File Components (SFCs)

To do this it is helpful to understand the core structure of a Gutenberg block. It consists of a pure JavaScript object that contains a number of fields,including two - edit and save - which are React components.

```
registerBlockType( 'cgb/block-vuetenberg', {
  // Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
  title: __( 'vuetenberg - CGB Block' ), // Block title.
  icon: 'shield', // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
  category: 'common', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
  keywords: [
    __( 'vuetenberg — CGB Block' ),
    __( 'CGB Example' ),
    __( 'create-guten-block' ),
  ],

  edit: function( props ) {
    // Creates a <div class='wp-block-cgb-block-vuetenberg'></div>.
    return (
      <div className={ props.className }>
        <p>— Hello from the backend.</p>
      </div>
    );
  },

  save: function( props ) {
    return (
      <div>
        <p>— Hello from the frontend.</p>
      </div>
    );
  },
} );
```

In order to use Vue.js components for our core blocks, we'll use a library called [vuera](https://github.com/akxcv/vuera) thatallows us to invoke Vue components within React components.

Then we'll simply replace `edit` and `save` with wrappers that pass along props to our Vue components.

## Preparing to Customize Configuration

In order to add Vue to our component, we'll need to do some customization of our build environment. To do this with `create-guten-app` we willneed to `eject` the build scripts - otherwise they are managed internal to the plugin. We do by running the following from within the plugin directory:

```
npm run eject
```

This populates our directory with a set of build scripts in the `scripts` directory, and some webpack configuration files in the `config` directory.

## Setting up Vue and Vuera

Our next step then is to install Vuera using npm, and set up our build configuration to allow us to use it. We will also need to install Vue, and since we want to use Vue SFCs we need `vue-loader`.

Using `vue-loader` also requires using `css-loader` and `vue-template-compiler`, so our final NPM install looks like:

```
npm install --save vuera vue vue-loader css-loader vue-template-compiler
```

To use Vue inside of React, Vuera recommends configuring a babel plugin via `.babelrc`, but I was not able to get that to work within the Gutenberg environment. Instead, we'll use an alternative method of wrapping Vue components with a `VueInReact` higher order component.

First, to compile our `.vue` files, we'll need to configure webpack to add `vue-loader`. There are two webpack configurations in `create-guten-block`, `config/webpack.config.dev.js` and `config/webpack.config.prod.js`.

The changes we need to make are:

1. Add a Vue Loader Plugin
2. Add a vue-loader reference to the rules

This means that to each config file we need to add this to the plugins list:

```
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  // ...
  plugins: [blocksCSSPlugin, editBlocksCSSPlugin, new VueLoaderPlugin()],
}
```

And add this block to the `rules`

```
module.exports = {
  // ...
  rules: [
    // ...
    {
        test: /.vue$/,
        use: [
          {
            loader: 'vue-loader'
          }
        ]
      }
  ]
}
```

## Bare Minimum Vue in Gutenberg

We're now ready for our bare minimum "Proof of concept" for putting Vue in Gutenberg. To do this I created a very simple Vue edit component that does nothing but say hello from Vue:

```
<template>
  <p>{{message}}</p>
</template>
<script>
export default {
  data() {
    return {
      message: 'Hello from Vue',
    };
  },
}
</script>
```

Then, to include this in my block, I need to import it, wrap it with a `VueInReact` higher order component from `vuera`, and just put it into my template.

```
import { VueInReact } from 'vuera'
import EditComponent from './edit.vue';
const Edit = VueInReact(EditComponent);

registerBlockType( 'cgb/block-vuetenberg', {
  // ...
  edit: Edit
}
```

Note: Once we wrap our component in `VueInReact`, it _behaves_ as a React component, letting us use it inside of JSX or return it anywhere that expects a component.

Props are passed exactly as you would expect, so our `Edit` Vue component can reference any Gutenberg specific properties.

## Using Gutenberg Builtin Components

Ok, great, so we have our Vue component rendering inside of Gutenberg just fine. But what if we want to use some of Gutenberg's builtin components, like their nice BlockControls?

This should be similarly straightforward to implement using a `ReactInVue` wrapper similar to how we embedded Vue inside React.

Lets try adding some block controls to customize alignment. First we set up an attribute for alignment in our block:

```
registerBlockType( 'cgb/block-vuetenberg', {
  //...
  attributes: {
    align: {
      type: 'string',
      default: 'full',
    },
  },
  //...
}
```

Next, in our component we'll utilize the `BlockControls` and `BlockAlignmentToolbar` builtin components from `wp.editor`.

In our script portion:

```
import { ReactInVue } from 'vuera';
const {
  BlockControls,
  BlockAlignmentToolbar,
} = wp.editor;

export default {
  props: ['attributes', 'setAttributes'],
  components: {
    'block-controls': ReactInVue(BlockControls),
    'block-alignment-toolbar': ReactInVue(BlockAlignmentToolbar),
  },
  data() {
    return {
      message: 'Hello from Vue',
    };
  },
}
```

And then in our template:

```
<template>
  <div>
    <block-controls>
      <block-alignment-toolbar :value="attributes.align"
          :onChange="align => setAttributes( { align } )"
          :controls="['wide', 'full']"
       />
    </block-controls>
    <p>{{message}}</p>
  </div>
</template>
```

Seems straightforward, but here we run into a bit of a challenge and drawback in the current state of Gutenberg and Vuera.

The `BlockControls` component only is visible when the block is selected - but in our Vue based implementation it never shows up!

After some digging, I traced this to a challenge with the new [React Context API](https://reactjs.org/docs/context.html).

While we can render React components just fine inside of Vue components with Vuera, many of Gutenberg's builtin components take advantage of React's Context API to change behavior based on whether an element is selected, and Context does not appear to cross the React/Vue border.

In the case of `BlockControls`, this means that the element never shows up.

## Workarounds

This is a severe limitation for building Gutenberg blocks with Vue - the builtin editor components are a huge part of making the interface consistent across all blocks.

For things like the controls - `BlockControls` or `InspectorControls`, these are positioned absolutely and don't need to live inside of our core block.

We could work around this limitation by placing them **outside** of our Vue component using pure React, and continue to have just the meat of our component in Vue:

```
import { VueInReact } from 'vuera'
import EditComponent from './edit.vue';
const Edit = VueInReact(EditComponent);
const {
  BlockControls,
  BlockAlignmentToolbar,
} = wp.editor;

registerBlockType( 'cgb/block-vuetenberg', {
  // ...
  edit: function(props) {
    return (
      <div>
        <BlockControls>
          <BlockAlignmentToolbar />
        </BlockControls>
        <Edit {...props} />
      </div>
    );
  }
}
```

However, for things like `InnerBlocks`, this workaround is insufficient, because by its vary nature it is embedded within the block.

At this time, I must conclude that only Gutenberg blocks that don't depend on builtins and don't nest content can be built using Vue.js

## The Path Forward

The React Context API is still relatively new, and it is very possible Vuera will be able to implement a way to pass along contexts. I have opened [a github issue](https://github.com/akxcv/vuera/issues/50) for this exact thing, and spent a fair amount of time trying to understand how to implement it, but so far haven't been able to figure it out.

**If anyone reading this understands the inner workings of the Context API and could help point me in the right direction, I'd greatly appreciate it!**

Another possibility, if it turns out that passing Contexts through Vuera isn't possible, is that Gutenberg could implement an alternative way to pass down the selected state of components to subcomponents.

The main Vue component receives an `isSelected` prop that _is_ updating properly, and it could pass this down to child components. However, those components right now are not set up to receive this prop, only looking at the Context.

However we get there, I am optimistic that one day we will be able to implement complex Gutenberg Blocks using Vue.js almost as easily as we can in React. We just aren't there quite yet.

* * *

P.S. - If you're interested in these types of topics, you should probably [follow me on Twitter](https://twitter.com/kbal11) or join my mailing list. I send out a weekly newsletter called the ‘[Friday Frontend](https://zendev.com/friday-frontend.html)’. Every Friday I send out 15 links to the best articles, tutorials, and announcements in CSS/SCSS, JavaScript, and assorted other awesome Front-end News. Sign up here: [https://zendev.com/friday-frontend.html](https://zendev.com/friday-frontend.html)