We'll learn how to make our own plugin for Vue.js, and distribute it on NPM for everyone to use.
Plugins are what makes our lives as developers so much more productive. Most of our projects depend on them as they allow us to ship new features with great speed.
As stated in the Official Vue.js documentation, there is no strictly defined scope for a plugin. It simply adds global-level functionality to your project. But they typically fall into these five categories based on the things we are trying to achieve with them:
Now that you understand how handy plugins can be and what needs they can fulfill, let’s see how to add one to your project. Then, we’ll learn how to make our own and distribute it on NPM for everyone to use (yes, it’s going to be super fun!).
To use your plugin after you’ve installed it with npm install
(or yarn add
), you need to go to your main.js
file. This is the entry point that drives our Vue application. Import it and call the Vue.use()
global method. One word of caution though: All plugins must instantiated before you start your app with new Vue()
.
import Vue from "vue";
import YourPlugin from "yourplugin";
Vue.use(YourPlugin);
new Vue({
// [...]
})
There is also another way to add a new plugin when the package author allows it: dropping the CDN link in your header’s script tag.
<script src="https://cdn.jsdelivr.net/npm/yourplugin@latest/dist/yourplugin.min.js"></script>
Sometimes, you would like to customize how a plugin behaves. You can easily do so by passing some options to it when calling Vue.use()
. Here is how it works:
Vue.use(YourPlugin, {
someOption: false,
anotherOption: false
})
For instance with vue-chartist, you can choose the text to display when no data is available to properly draw the chart as follows:
Vue.use(VueChartist, {
messageNoData: "You have not enough data"
});
Now let’s get back to the main event — building your first Vue.js plugin together. 💪
If you are reading this, you are probably a frontend developer like me. And like any other frontend developer, you probably love having nice handsome buttons for your interfaces! So that’s what we’ll be building: a bunch of nice handsome buttons that we’ll be able to reuse. This will save us a lot of time for future projects! You’ll also have the knowledge to package all your remaining base components and why not release your own design system?
Let’s create an empty folder for our package and initialize NPM. This will generate a new package.json
file. We’ll deal with it later.
$ mkdir nice-handsome-button && cd nice-handsome-button
$ npm init
# The command above will create a new package.json
# Press enter to answer all the following questions
Add a new folder called src at the root, in which you create a new file NiceHandsomeButton.vue
. You can rapidly prototype with just a single *.vue
file with the vue serve
and vue build
commands, but they require an additional global addon to be installed first:
npm install -g @vue/cli
npm install -g @vue/cli-service-global
Now if you run:
$ vue serve src/NiceHandsomeButton.vue
Visit http://localhost:8080/
. A blank page should appear in your browser. Let’s work on our button component from now on! 👩💻👨💻
You can read more about @vue/cli-service-global in the official documentation. This addon is that it is quite useful for working on a single
.vue
file without scaffolding an entire project withvue create my-new-project
.
As this tutorial is not about learning how to write Vue components, I expect you to be familiar with the basics. The full code of our nice handsome button is available below (the template, the JavaScript logic and the style). Copy it, open NiceHandsomeButton.vue
and paste the content inside.
<template>
<button
@click="onClick"
@dblclick="onDoubleClick"
:class="[
'nice-handsome-button',
'nice-handsome-button--' + color,
'nice-handsome-button--' + size,
{
'nice-handsome-button--rounded': rounded
}
]"
>
<slot></slot>
</button>
</template>
We have kept things simple, but here are a few things to note:
color
, size
and rounded
. As their names indicate, they will allow us to control the color, the size and whether or not our button should be rounded.
<nice-handsome-button>My Button Label</nice-handsome-button>
.
Let’s define the props our component can accept as well as the two methods that will emit an event when we click/double-click on it.
<script>
export default {
props: {
color: {
type: String,
default: "blue",
validator(x) {
return ["blue", "green", "red"].indexOf(x) !== -1;
}
},
rounded: {
type: Boolean,
default: true
},
size: {
type: String,
default: "default",
validator(x) {
return ["small", "default", "large"].indexOf(x) !== -1;
}
},
},
methods: {
onClick(event) {
this.$emit("click", event);
},
onDoubleClick(event) {
this.$emit("dblclick", event);
},
}
};
</script>
Last but not least, let’s style our component. 👩🎨
<style>
.nice-handsome-button {
display: inline-block;
outline: 0;
border: 1px solid rgba(0, 0, 0, 0.1);
color: #ffffff;
font-weight: 500;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
user-select: none;
cursor: pointer;
}
/* --> COLORS <-- */
.nice-handsome-button--blue {
background-color: #0194ef;
}
.nice-handsome-button--green {
background-color: #1bb934;
}
.nice-handsome-button--red {
background-color: #e1112c;
}
/* --> SIZES <-- */
.nice-handsome-button--small {
padding: 8px 10px;
border-radius: 4px;
font-size: 12px;
line-height: 12px;
}
.nice-handsome-button--default {
padding: 12px 14px;
border-radius: 6px;
font-size: 14px;
line-height: 16px;
}
.nice-handsome-button--large {
padding: 16px 18px;
border-radius: 8px;
font-size: 16px;
line-height: 20px;
}
/* --> BOOLEANS <-- */
.nice-handsome-button--rounded {
border-radius: 60px;
}
</style>
Our component is now ready to use and can be used like this:
<nice-handsome-button :rounded="true" color="red" size="large">My Button</nice-handsome-button>
Let’s package it now. 💪
Before we start this section, let’s create an index.js
file in your src folder.
Remember that Vue.use()
global we talked about earlier? Well… what this function does is call the install()
method that we will define now.
This function takes two parameters: the Vue
constructor and the options
object that a user can set. You can skip the last argument if you don’t need it as it is optional. But if you want to make your plugin customizable, remember this is where you will catch the different parameters:
Vue.use({
param: "something"
})`;
// Then in your install method options.param will equal to "something"
Back to our pluging. Now in index.js
, let’s import our component and define our install
method.
import NiceHandsomeButton from "./NiceHandsomeButton.vue";
export default {
install(Vue, options) {
// Let's register our component globally
// https://vuejs.org/v2/guide/components-registration.html
Vue.component("nice-handsome-button", NiceHandsomeButton);
}
};
Congratulations, you almost made it! 👏
package.json
Open your package.json
file that you created when running npm init
.
{
"private": false,
"name": "nice-handsome-button",
"version": "0.0.1",
"description": "
A nice handsome button to help you learn basin plugin craftsmanship 🎨",
"author": "Nada Rifki",
"license": "MIT",
"main": "./dist/index.js",
"scripts": {
"dev": "vue serve NiceHandsomeButton.vue",
"build": "bili --name index --plugin vue --vue.css false"
},
"files": [
"dist/*"
]
}
A few notes:
private
is set to false
. This means your package is public (i.e. everyone is able to see and install it).
name
for your package. You have to make sure that it’s not already taken.
0.0.1
. You will have to increment this number every time you publish an update for your package. If you are not familiar with semantic versioning, I highly recommend you read this.
main
is the primary entry point to your program. That is, if your package is named foo
, and a user installs it, and then does require("foo")
, then your main module’s exports object will be returned.
scripts
property is a dictionary containing script commands that you can easily run with npm run
.
files
property specifies which files should be published on NPM. It is usually a bad idea to publish everything. We’ll be using bili
, so all files in dist
folder should be included.
You can read more about all these properties in the official NPM documentation.
In case you don’t know, bundling is the process of grouping all your code from all your files in your project into one single file. The reason behind this is simply to increase performance. This will also minify the code and accomplish some other cool things.
To do so, we’ll use Bili, a fast and zero-config library bundler that uses Rollup.js under the hood.
First make sure you have the latest version of Node.js (Bili requires Node.js 8 or above):
# Check your Node.js version
$ node -v
If the version of Node you’re using is outdated, head to node.js.org to update it.
Now, let's install Bili.
$ npm install --save-dev bili
# We'll need these two packages to transpile .vue files
# https://bili.egoist.moe/#/recipes/vue-component
$ npm install --save-dev rollup-plugin-vue
$ npm install --save-dev vue-template-compiler
Now, create our bili.config.js
file in the root folder and add our bundling settings:
module.exports = {
banner: true,
output: {
extractCSS: false,
},
plugins: {
vue: {
css: true
}
}
};
All you have left to do is run the command below on your terminal and your package is bundled — it’s as easy as 1-2-3!
$ npx bili --bundle-node-modules
You should obtain a new dist
folder with a index.cjs.js
file.
By default
<style>
tag in Vue SFC will be extracted to the same location where the JS is generated but with.css
extension. That’s why we added--vue.css false
in the command above.
To learn more about Bili and how to customize it, I recommend you take a look at the documentation.
Now that your package is ready, the only thing left for you is to publish your package on NPM.
Start by creating an account on NPM (you can also run npm adduser
if you prefer using the command lines). Then go to your terminal and run npm login
. You will have to input your username, password and email.
You can check that you are logged in by typing npm whoami
. This should display your username.
There is now only one terminal command that stands between you and publishing your package:
$ npm publish
And voilà! 🎉
To update your package, just increment the version
number in your package.json
and rerun npm publish
.
You can install it like any other package:
$ npm install --save nice-handsome-button
In your main.js
, or a similar entry point for your app:
import NiceHandsomeButton from "nice-handsome-button";
import Vue from "vue";
Vue.use(NiceHandsomeButton);
Now, the nice handsome button should be able in any of your .vue
files.
<nice-handsome-button :rounded="true" color="red" size="large">My Button</nice-handsome-button>
There is a lot you can do now and that’s awesome! You learned how to package your first component and publish it on NPM for everyone to use. But don’t stop now! Here are a few ideas that may inspire you:
mouseenter
or mouseout
and so on.
Easy peasy! Finally, we’re done. You can find the plugin’s final code on my GitHub. Feel free to give me your feedback or to reach me on Twitter @RifkiNada if you need help. Enjoy and have a good day! 👋
Nada is a JavaScript developer who likes to play with UI components to create interfaces with great UX. She specializes in Vue/Nuxt, and loves sharing anything and everything that could help her fellow frontend web developers. Nada also dabbles in digital marketing, dance and Chinese. You can reach her on Twitter @RifkiNada or visit her website, nadarifki.com.