Tutorial

How To Use Vue.js with Electron and Vuex

Published on December 9, 2018
How To Use Vue.js with Electron and Vuex

This tutorial is out of date and no longer maintained.

Introduction

Electron makes it easy to create cross-platform desktop applications with JavaScript. As it uses the Node.js runtime, we have the luxury of using any frontend framework we want! In this article, we’ll be looking at getting up and running with Vue and Electron to create our own applications.

Setting Up the Project

To get started, run the following in your terminal:

  1. npm install vue-cli -g

Use the electron-vue starter and create a my-todos project:

  1. vue init simulatedgreg/electron-vue my-todos

You’ll then be asked some questions about your project, fill them in to generate the config file:

  1. Application Name > my-todos
  2. Application Id > com.example.yourapp
  3. Application Version > 0.0.1
  4. Project Desription > Manage your Todos with Vue and Electron
  5. Use Sass / Scss? Y
  6. Select plugins to install > Axios, vue-electron, vue-router, vuex, vuex-electron
  7. Linting with ESLint? > N
  8. Unit testing with Karma + Mocha? N
  9. End to end testing with Spectron + Mocha? N
  10. Which build tool would you like to use? > electron-builder
  11. Author > Paul Halliday

After filling in the above (you can switch some steps out if you want, most of it is personal preference), we have a folder named my-todos with our project inside. Install the dependencies and then open this with your favorite editor:

Note: If you’re a Windows user, you may need to follow the steps outlined here prior to continuing.

Navigate to the directory:

  1. cd my-todos

Install the packages:

  1. npm install

Run the server:

  1. npm run dev

If we’ve done everything correctly at this stage, we should be greeted with a macOS application that contains information about our project:

Starter Electron project

Most of our work will be done inside of the src directory, and I’d like to bring your attention to the main and renderer folders.

Main houses index.js and index.dev.js, files that are related to the main process. This is things such as creating a new BrowserWindow with dimensions, listening for app-wide events, and so on.

Renderer is where we keep our Vue application and can be thought of as the directory for our frontend code.

Note: For more information on the Electron architecture and the differences between the main and renderer process, visit this page.

In order to get a feel for how Electron works, let’s visit src/main/index.js and see how our main page is defined:

src/main/index.js
const winURL = process.env.NODE_ENV === 'development'
  ? `http://localhost:9080`
  : `file://${__dirname}/index.html`

function createWindow () {
  mainWindow = new BrowserWindow({
    height: 563,
    useContentSize: true,
    width: 1000
  })

  mainWindow.loadURL(winURL)

  mainWindow.on('closed', () => {
    mainWindow = null
  })
}

app.on('ready', createWindow)

The BrowserWindow object can be used to display a new browser window (as the name suggests), and we’re using it to open index.html when the application is ready. In turn, this will start our Vue app, giving us the ability to hook into native desktop features.

Now that we know how the Vue application is started, let’s take a look at the defined routes within our application. Head over to src/router/index.js:

src/router/index.js
export default new Router({
  routes: [
    {
      path: '/',
      name: 'landing-page',
      component: require('@/components/LandingPage').default
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
})

As we can see, the LandingPage component is defined as the default route for our application. We can therefore edit or create a new routes object with our own components in the future.

In order to get Vuex to work with our Electron project, we’ll need to provide a path to our store within src/main/index.js:

src/main/index.js
import { app, BrowserWindow } from 'electron';
import '../renderer/store';

We can then make a new Store module named Todo.js within src/renderer/store/modules:

src/renderer/store/modules/Todo.js
import uuid from 'uuid/v4';

const state = {
  todos: []
};

const actions = {
  ADD_TODO ({ commit }, name) {
    console.log(name);
    commit('ADD_TODO', name);
  },
  COMPLETE_TODO ({ commit }, id) {
    commit('COMPLETE_TODO', id);
  },
  CLEAR_TODOS ({ commit }) {
    commit('CLEAR_TODOS');
  }
};

const mutations = {
  ADD_TODO (state, name) {
    state.todos = [ ...state.todos, { id: uuid(), name } ];
  },
  COMPLETE_TODO (state, id) {
    state.todos = state.todos.filter((todo) => todo.id != id);
  },
  CLEAR_TODOS (state) {
    state.todos = [];
  }
};

export default {
  state,
  actions,
  mutations
};

We’re using the third-party uuid module to generate new IDs for each Todo.

Install that via npm:

  1. npm install uuid

Finally, we can edit our LandingPage.vue component to include our small Todo list:

src/components/LandingPage.vue
<template>
  <div class="container">
    <div>
      <input
        class="todo-input"
        type="text"
        v-model="todoItemName"
        placeholder="What are you doing today?"
        @keyup.enter.prevent="addTodo"
      >
    </div>
    <div class="todos">
      <ul>
        <li
          class="todo-item"
          v-for="todo in todos"
          :key="todo.id"
          @click="completeTodo(todo)"
        >{{todo.name}}</li>
      </ul>
    </div>

    <button class="clear-all" @click="clearTodos" v-if="todos.length > 0">CLEAR ALL</button>
  </div>
</template>

<script>
import { mapState } from "vuex";

export default {
  data() {
    return {
      todoItemName: ""
    };
  },
  methods: {
    addTodo() {
      this.$store.dispatch("ADD_TODO", this.todoItemName);

      this.todoItemName = "";
    },
    clearTodos() {
      this.$store.dispatch("CLEAR_TODOS");
    },
    completeTodo(selectedTodo) {
      this.$store.dispatch("COMPLETE_TODO", selectedTodo.id);
    }
  },
  computed: {
    ...mapState({
      todos: state => state.Todo.todos
    })
  }
};
</script>

<style>
.container {
  height: 100vh;
  text-align: center;
  background-color: #30336b;
}

.todos {
  overflow: scroll;
  height: 70vh;
  margin-top: 20px;
}

.todo-input {
  font-size: 36px;
  width: 90vw;
  border: 0px;
  outline: none;
  padding-top: 20px;
  text-align: center;
  background-color: transparent;
  color: white;
}

.todo-item {
  font-size: 24px;
  padding: 10px 0px;
  color: white;
}

.clear-all {
  border: 1px solid white;
  background: transparent;
  color: white;
  margin-top: 20px;
  padding: 20px;
  font-size: 18px;
}

::placeholder {
  color: white;
}
</style>

As this is a demonstration app, I’ve decided to not componentize this further. This finally gives us the following:

Our Electron todo app

Building Our App

As we’re using electron-builder to build our application, we can run the following:

  1. npm run build

If we look in our package.json, we can also see that we have a variety of other commands at our disposal:

  1. npm run build:dir # this builds an app without an installer
  2. npm run build:clear # deletes all builds from the build folder
  3. npm run build:web # builds for web platform

This can be further customized inside of package.json by editing the following object:

package.json
"build": {
  "productName": "my-todos",
  "appId": "com.example.yourapp",
  "directories": {
    "output": "build"
  },
  "files": [
    "dist/electron/**/*"
  ],
  "dmg": {
    "contents": [
      {
        "x": 410,
        "y": 150,
        "type": "link",
        "path": "/Applications"
      },
      {
        "x": 130,
        "y": 150,
        "type": "file"
      }
    ]
  },
  "mac": {
    "icon": "build/icons/icon.icns"
  },
  "win": {
    "icon": "build/icons/icon.ico"
  },
  "linux": {
    "icon": "build/icons"
  }
},

The results of our built application can be found in the build/mac or build/platform folder.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.