If you are me, and like using Vue.js in Electron, you probably want to keep each of your components in their own tidy little modules. Vue.js enables you to do this using .vue files which can be converted to good old HTML, CSS and JavaScript using Webpack.

A lot of people out there love Webpack and there’s no debate that it's a powerful tool. I get that, even though I’m not one of those people. But using Webpack for single-file components in Electron is simply unnecessary and I don’t like unnecessary things.1 Below, I'll explain how to make use of single-file components in your Electron app without using anything other than ES6 features and Node’s require() method.

I’ll explain why I like doing it this way at the end of the post, but for now let’s get to the good stuff.

Note: This post does not explain how to write Vue components or create an Electron app, only how to integrate the former into the latter sans bundler.

What’s in a .vue File?

To understand what functionality we’re going to replicate, let’s take a look at the anatomy of a .vue single-file component. You can detailed information on them in the Vue documentation, but the gist of it is that it uses three tags to define the HTML template, the JavaScript, and the CSS for that component. It looks like this:

<template>  
    <p>{{ greeting }} World!</p>
</template>

<script>  
module.exports = {  
    data: function() {
        return {  
            greeting: "Hello"
        };
    }
}
</script>

<style scoped>  
p {  
    font-size: 2em;
    text-align: center;
}
</style>  

Two things that immediately stand out to me as “feeling funny” are:

  1. Within my <script> tag, I’m still using Node’s module.exports functionality, except now it doesn’t really work with Node’s require() function. Not a big improvement.

  2. I’m still writing plain-old CSS in the <style> tag, and using element types, IDs, and classes to apply the styles.2 Again, not much of an improvement.

Replicating in Pure JS

Replicating a .vue file in pure JavaScript is quite easy. Thanks to point #1 above, the changes are minimal. We’re still going to use module.exports but move our template and style into the JavaScript section.

We do this using:

  • ES6’s template literals which make it easy to write multi-line HTML within JavaScript;3
  • The fact Vue.js can interpret JS objects as CSS. This makes for easy editing later if we want to set this.style.color = "red" for example. We use v-bind to attach the object.

The example above can be re-written as:

module.exports = {  
    name: "Greeting",
    template: `
        <p v-bind:style='style'>
            {{ greeting }} World!
        </p>
    `,
    data: function() {
        return {  
            greeting: "Hello",
            style: {  
                fontSize: "2em",
                textAlign: "center"
            }
        };
    }
}

We’ve trimmed off the extra tags and are now left with a pure .js file we can require() in other components or our root Vue instance.

Nesting Components

Let’s look at a broader example. Suppose I want to use my Greeting component in the WelcomeScreen component of my app.

In rendering.js (used by my rendering.html page, launched by main.js), I’ll set up my Vue instance like this:

// rendering.js

const Vue = require("vue/dist/vue.common.js");

// Components:
const WelcomeScreen = require("../components/WelcomeScreen.js");

window.onload = function() {  
    new Vue({
        el: "#vue-wrapper",
        render: h => h( WelcomeScreen )
    });
};

Then, in my WelcomeScreen component, I’ll require the Greeting component from above:

// WelcomeScreen.js

// Components:
const Greeting = require("../components/Greeting.js");

module.exports = {  
    name: "WelcomeScreen",
    components: {
        Greeting
    },
    template: `
        <div id="welcome-screen">
            <Greeting></Greeting>
        </div>
    `,
    data: function() {
        return {  
            style: {  
                width: "100%",
                height: "100%"
            }
        };
    }
}

And that’s it!

Why?

One of the main reasons you might want to do this is to start integrating Vue into your Electron app without configuring anything. This can be helpful if you’ve just started a little baby app with a main.js and an index.html and some core logic, and are trying to get a basic UI built to complete a proof-of-concept.

You won’t need to:

  • Transpile any of your existing code
  • Install Webpack, Rollup, or other bundlers
  • Write or copy-paste a config file for the above
  • Go through a build step after changes
  • Install loaders for CSS or images
  • Get frustrated when any of the above starts to feel like more trouble than it’s worth

It’s true: many boiler plates exist to take care of this stuff, but they can be frustrating for beginners because they often don’t explain how their internals work. For advanced coders it can sometimes feel like starting a lovely cooking project by first picking out a frozen meal from the freezer and defrosting in the microwave. Yuck.


Thanks for reading! If you have any questions or find any mistakes you can find me on Twitter or email me at ian [dot] paschal [at] gmail [dot] com.


  1. There are still valid reasons to use Webpack in an Electron project, like minification or in conjunction with Babel, but single-file components are not one of them.

  2. Granted, the scoping feature is a nice addition, but if you’re writing an Electron application, you’re probably also fairly experienced at keeping your CSS cascading in the right direction. It’s also a non-issue if you’re actually using LESS and can just include a scoped declaration within a parent declaration.

  3. Pro-Tip: If you’re using Atom, you can install the language-javascript-plus package to highlight your template literals as though they were HTML.