Vue.js in WordPress: Using vuera to run vue in react

Sometimes I wish that WordPress had adopted Vue.js instead of React in their editor. I wasn’t a part of the discussion when they were choosing a framework, so I don’t know all the details. So I started to wonder, can I shim the editor and put Vue in WordPress?

The answer is yes. Yes, you can put vue in WordPress if you’re willing to put a little effort into it. Thanks to a package called vuera, this is actually very easy to achieve. It may not be something you’d actually want to do on a production site… but who knows?

It can be done!

Setup… if you really want to try it

We’re going to create a very stripped down build for a plugin here using webpack. We’re going to build a js file and register the block in php.

npm init -y // init, and don't as me questions!
npm install webpack @babel/core @babel/preset-env @babel/preset-react babel-loader webpack-cli --save-dev

This will get us up and running if we create a webpack.config.js file and a .babelrc file.

/* webpack.config.json */
const path = require("path");

module.exports = [
    {
        mode: 'development',    
        entry: "./src/index.js",
        output: {
            path: path.resolve(__dirname, "./dist"),
            filename: "build.js"
        },
        module: {
            rules: [{
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    loader: "babel-loader"
                },
            ]
        },
        stats: {
            colors: true
        },
        externals: {
            react: "React",
            'react-dom': 'ReactDOM",
        }
    },
];
/* .babelrc */
{
    "presets": ["@babel/preset-env", "@babel/preset-react"],
}

Now, make sure you create an index.js file in your /src folder, and then we can do this magic to build our javascript:

webpack

Boom! we have js building. so let’s register this script in php and make sure it works…

<?php
/**
 * Plugin Name: guty-vue-demo
 * Description: A setup to allow running vue in gutenberg
 * Version: 0.0.1
 * Author: Jim Schofield
 * Text Domain: guty-vue-demo
 *
 * @package guty-vue-demo
 */
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
 
/**
 * Enqueue block JavaScript and CSS for the editor
 */
function guty_vue_demo_plugin_editor_scripts() {
 
    // Enqueue block editor JS
    wp_register_script(
        'guty-vue-demo/editor-scripts',
        plugins_url( '/dist/build.js', __FILE__ ),
        [ 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n' ],
        filemtime( plugin_dir_path( __FILE__ ) . '/dist/build.js' ) 
    );

    register_block_type('guty-vue-demo/block-library', array(
        'editor_script' => 'guty-vue-demo/editor-scripts',
    ));
 
}

// Hook the enqueue functions into the editor
add_action( 'init', 'guty_vue_demo_plugin_editor_scripts' );

➡ Make sure you install the plugin in the admin, and then you should see the script running in your editor! As usual, I just did a console log to make sure the script was there.

‘Hi’ from the editor

Last thing we should do is create the simplest registerBlockScript so there’s actually a block to add Vue to!

const {registerBlockType} = wp.blocks;

registerBlockType('guty-vue/vue-test-demo', {
    title: 'Vue block!',
    icon: 'smiley',
    category: 'common',

    attributes: { },

    edit(props) {
        return (
            <div>
                test
            </div>
);
    },

    save(props) {

        return (
            <div>
                test
            </div>
        );
    },
});

Including Vue.js

Cool, so we have a test block that shows in our editor. Let’s include some vue.

Firstly, we need to install the plugin that will allow us to call Vue from a React component. We also need to include some loaders so that webpack knows how to include Vue in the project… also, we need Vue.

npm install vue vue-loader vue-template-compiler vuera

Now, let us instruct webpack so it knows how to use vue files. We include the vue-template-compiler plugin, and then we add a rule for *.vue files in webpack.config.js:

/* webpack.config.json */
const path = require("path");

const VueLoaderPlugin = require('vue-loader/lib/plugin') //new!


module.exports = [
    {
        mode: 'development',
        entry: "./src/index.js",
        output: {
            path: path.resolve(__dirname, "./dist"),
            filename: "build.js"
        },
        plugins: [new VueLoaderPlugin()], //new!
        module: {
            rules: [{
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    loader: "babel-loader"
                },
                {
                    test: /\.vue$/, //new!
                    loader: "vue-loader"  //new!
                }
            ]
        },
        stats: {
            colors: true
        },
        externals: {
            react: "React"
        }
    },
];

For Vuera to do its thing we need to include it in .babelrc:

{
    "presets": ["@babel/preset-env", "@babel/preset-react"],
    "plugins": ["vuera/babel"] //new!
}

Cool. I recommend running ‘webpack –watch’ from now on so webpack will just stay running and watch your files.

Now to actually create a vue-in-react component. Back in the /src folder let’s create two new things. First, the actual vue component. I’m naming it ‘guty-vue-test.vue’.

<template>
    <div>
        Hello from Vue!
    </div>
</template>

<script>
export default {
    name:'guty-view-test'
}
</script>

<style>

</style>

Next, we need to create the react component that loads this vue component. This is the one we’ll actually import into index.js and use. this is where the magic happens. Vuera notices the import and somehow connects the react reactivity to the vue reactivity. Yay!

import React from 'react';

import GutyVueTest from './guty-vue-test.vue';

export default (props) => {
    return <GutyVueTest />;
}

Now, let us import Wrapper into index.js and use it in the edit() method:

const {registerBlockType} = wp.blocks;

import Wrapper from './wrapper'; //new!

registerBlockType('guty-vue/vue-test-demo', {
    title: 'Vue block!',
    icon: 'smiley',
    category: 'common',

    attributes: { },

    edit(props) {
        return (
            <div>
                <Wrapper /> //new!
            </div>
);
    },

    save(props) {

        return (
            <div>
                test
            </div>
        );
    },
});

If you reload your editor, you should be rewarded with a fully functional Vue block! Go ahead, use the vue devtool to check it out! 😀

Inspecting the Vue component inside of the gutenberg block by using Vue.js devtools

How to use reactivity

The strategy is very React-ish. You pass the attribute value down to the Vue component as a prop. You also pass a callback function to update attributes. Here’s how I did a simple text box:

/* index.js */
const {
    registerBlockType
} = wp.blocks;

import Wrapper from './wrapper';

registerBlockType('guty-vue/vue-test-demo', {
    title: 'Vue block!',
    icon: 'smiley',
    category: 'common',

    attributes: {
        message: {
            type: String,
        }
    },

    edit(props) {
        const {setAttributes} = props;
        const {message} = props.attributes;

        function changeMessage(newString) {
            setAttributes({
                message: newString,
            })
        }
        return (
            <div>
                <Wrapper
                    message={message}
                    changeMessage={changeMessage}
                    />
            </div>
        );
    },

    save(props) {

        return (
            <div>
                test
            </div>
        );
    },
});
import React from 'react';

import GutyVueTest from './guty-vue-test.vue';

export default (props) => {
    return <GutyVueTest {...props} />;
}
<template>
    <div>
        <div>
            <input type="text" placeholder="Enter text here" value="message" @keyup="changeMessage($event.target.value)">
        </div>
        {{message}}
    </div>
</template>

<script>
export default {
    name:'guty-view-test',
    props: {
        message: String,
        changeMessage: Function
    }
}
</script>

<style>

</style>

Now we can see how to update attributes.

Changing the text in the text box updates the message value in the vue devtools

Caveats…

So, why haven’t we talked about the save method in the guty block?

Well, to be honest, I tried to render out a vue component, but it’s not working. I have a lot to learn about the save function, because I’ve run into this issue where I have a component that I think is stateless and it doesn’t render on save. Does anyone have a good explanation for why this doesn’t work?

I’ve shown that we can use vue in the editor to create an editing experience. I have yet to show that you can use Vue to render strings… hopefully I can figure that out.

Why would I want to put vue in WordPress? Why would I not?

Vue, in my opinion, is just faster and more robust and more understandable and prettier than React. I can code React just fine, and it’s not bad- don’t get me wrong. But if you prefer building interfaces with Vue, we have options.

I am seeing many people talk about use cases where a company wanted to use Vue components and didn’t want to rewrite them in React or vice versa.

Another cool thing that goes along with this is that you can hook up Vuex and other cool global Vue things- router, plugins, etc. Maybe you want to create a complex, multipage vue block? It could happen.

But, it’s just ugly to write something in two frameworks… and I had to load two framework dependencies (and some more plugins) to achieve this. I also had to create a wrapper because of the way Gutenberg works. This is not something I would suggest right now. When I told some of my colleagues this, they recoiled at the thought of a framework in a framework. I feel the same when people mention using jQuery in React or Vue.

Let me know what you think

Comment below or @ me on twitter

Leave a Reply

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