5 Practical Examples For Learning Vue.js

Download

We've already covered Angular.js and React in previous articles but there is a new frontend library that we think is worth your time. It's called Vue.js and it has gathered a large community of enthusiastic developers.

The philosophy behind Vue.js is to provide the simplest possible API for creating real-time, two-way data binding between the view (HTML) and the model (a JavaScript object). As you will see in the following examples, the library holds true to that idea and working with it is effortless and enjoyable, without compromising on any functionality.

This article has been updated and now works with Vue.js v2!

Getting Started

The easiest way to install Vue.js is to simply include it with a <script> tag at the end of your HTML's body. The entire library is located in a single JavaScript file which you can download from the official website or import directly via CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.5/vue.min.js"></script>

If you want to use the library in a Node.js project, vue is available as an npm module. There is also an official CLI, which allows users to quickly setup their whole project based on premade template builds.

Below are five editors containing example apps we've built for you. The code has lots of comments and is separated in tabs for each file, making it really easy to follow. The editors have Vue.js built-in so don't be afraid to experiment. Also, you can download an archive containing all the examples from the Download button near the top of this article.

1. Navigation Menu

To kick things off we're going to build a simple navigation bar. There are a few basic components almost every Vue.js app need to have. They are:

  • The model, or in other words our app's data. In Vue.js this is simply a JavaScript object containing variables and their initial values.
  • An HTML template, the correct terminology for which is view. Here we chose what to display, add event listeners, and handle different usages for the model.
  • ViewModel - a Vue instance that binds the model and view together, enabling them to communicate with each other.

The idea behind these fancy words is that the model and the view will always stay in sync. Changing the model will instantly update the view, and vice versa. In our first example this is shown with the active variable, representing which menu item is currently selected.

<div id="main">

    <!-- The navigation menu will get the value of the "active" variable as a class. -->

    <!-- To stops the page from jumping when a link is clicked 
        we use the "prevent" modifier (short for preventDefault). -->

    <nav v-bind:class="active" v-on:click.prevent>

        <!-- When a link in the menu is clicked, we call the makeActive method, 
        defined in the JavaScript Vue instance. It will change the value of "active". -->

        <a href="#" class="home" v-on:click="makeActive('home')">Home</a>
        <a href="#" class="projects" v-on:click="makeActive('projects')">Projects</a>
        <a href="#" class="services" v-on:click="makeActive('services')">Services</a>
        <a href="#" class="contact" v-on:click="makeActive('contact')">Contact</a>
    </nav>

    <!-- The mustache expression will be replaced with the value of "active".
         It will automatically update to reflect any changes. -->

    <p>You chose <b>{{active}}</b></p>
</div>
// Creating a new Vue instance and pass in an options object.
var demo = new Vue({

    // A DOM element to mount our view model.
    el: '#main',

    // This is the model.
    // Define properties and give them initial values.
    data: {
        active: 'home'
    },

    // Functions we will be using.
    methods: {
        makeActive: function(item){
            // When a model is changed, the view will be automatically updated.
            this.active = item;
        }
    }
});
*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The menu
--------------------------*/

nav{
    display:inline-block;
    margin:60px auto 45px;
    background-color:#5597b4;
    box-shadow:0 1px 1px #ccc;
    border-radius:2px;
}

nav a{
    display:inline-block;
    padding: 18px 30px;
    color:#fff !important;
    font-weight:bold;
    font-size:16px;
    text-decoration:none !important;
    line-height:1;
    text-transform: uppercase;
    background-color:transparent;

    -webkit-transition:background-color 0.25s;
    -moz-transition:background-color 0.25s;
    transition:background-color 0.25s;
}

nav a:first-child{
    border-radius:2px 0 0 2px;
}

nav a:last-child{
    border-radius:0 2px 2px 0;
}

nav.home .home,
nav.projects .projects,
nav.services .services,
nav.contact .contact{
    background-color:#e35885;
}

p{
    font-size:22px;
    font-weight:bold;
    color:#7d9098;
}

p b{
    color:#ffffff;
    display:inline-block;
    padding:5px 10px;
    background-color:#c4d7e0;
    border-radius:2px;
    text-transform:uppercase;
    font-size:18px;
}

As you can see working with the library is pretty straightforward. Vue.js does a lot of the work for us and provides familiar, easy to remember syntax:

  • simple JavaScript object for all the options
  • {{double brackets}} for templating
  • v-something inline attributes for adding functionality directly in the HTML.

2. Inline Editor

In the previous example our model had only a couple of predefined values. If we want to give the users the ability to set any data, we can do two-way binding and link together an input field with a model property. When text is entered, it is automatically saved in the text_content model, which then causes the view to update.

<!-- v-cloak hides any un-compiled data bindings until the Vue instance is ready. -->
<!-- When the element is clicked the hideTooltp() method is called. -->

<div id="main" v-cloak v-on:click="hideTooltip" >

    <!-- This is the tooltip. 
         v-on:clock.stop is an event handler for clicks, with a modifier that stops event propagation.
         v-if makes sure the tooltip is shown only when the "showtooltip" variable is truthful -->

    <div class="tooltip" v-on:click.stop v-if="show_tooltip">

        <!-- v-model binds the contents of the text field with the "text_content" model.
         Any changes to the text field will automatically update the value, and
         all other bindings on the page that depend on it.  -->

        <input type="text" v-model="text_content" />
    </div>

    <!-- When the paragraph is clicked, call the "toggleTooltip" method and stop event propagation. -->

    <!-- The mustache expression will be replaced with the value of "text_content".
         It will automatically update to reflect any changes to that variable. -->

    <p v-on:click.stop="toggleTooltip">{{text_content}}</p>

</div>
// Creating a new Vue instance and pass in an options object.
var demo = new Vue({

    // A DOM element to mount our view model.
    el: '#main',

    // Define properties and give them initial values.
    data: {
        show_tooltip: false,
        text_content: 'Edit me.'
    },

    // Functions we will be using.
    methods: {
        hideTooltip: function(){
            // When a model is changed, the view will be automatically updated.
            this.show_tooltip = false;
        },
        toggleTooltip: function(){
            this.show_tooltip = !this.show_tooltip;
        }
    }
})
/* Hide un-compiled mustache bindings
until the Vue instance is ready */

[v-cloak] {
  display: none;
}

*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The edit tooltip
--------------------------*/

.tooltip{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius:3px;
    width: 290px;
    padding: 10px;

    position: absolute;
    left:50%;
    margin-left:-150px;
    top: 80px;
}

.tooltip:after{
    /* The tip of the tooltip */
    content:'';
    position:absolute;
    border:6px solid #5190ac;
    border-color:#5190ac transparent transparent;
    width:0;
    height:0;
    bottom:-12px;
    left:50%;
    margin-left:-6px;
}

.tooltip input{
    border: none;
    width: 100%;
    line-height: 34px;
    border-radius: 3px;
    box-shadow: 0 2px 6px #bbb inset;
    text-align: center;
    font-size: 16px;
    font-family: inherit;
    color: #8d9395;
    font-weight: bold;
    outline: none;
}

p{
    font-size:22px;
    font-weight:bold;
    color:#6d8088;
    height: 30px;
    cursor:default;
}

p b{
    color:#ffffff;
    display:inline-block;
    padding:5px 10px;
    background-color:#c4d7e0;
    border-radius:2px;
    text-transform:uppercase;
    font-size:18px;
}

p:before{
    content:'✎';
    display:inline-block;
    margin-right:5px;
    font-weight:normal;
    vertical-align: text-bottom;
}

#main{
    height:300px;
    position:relative;
    padding-top: 150px;
}

Another thing to note in the above code is the v-if attribute . It show or hides a whole element depending on the truthfulness of a variable. You can read more about it here.

3. Order Form

This example illustrates multiple services and their total cost. Since our services are stored in an array, we can take advantage of the v-for directive to loop through all of the entries and display them. If a new element is added to the array or any of the old ones is changed, Vue.js will automatically update and show the new data.

<!-- v-cloak hides any un-compiled data bindings until the Vue instance is ready. -->

<form id="main" v-cloak>

    <h1>Services</h1>

    <ul>
        <!-- Loop through the services array, assign a click handler, and set or
             remove the "active" css class if needed -->
        <li v-for="service in services" v-on:click="toggleActive(service)" v-bind:class="{ 'active': service.active}">
            <!-- Display the name and price for every entry in the array .
                Vue.js has a built in currency filter for formatting the price -->
            {{service.name}} <span>{{service.price | currency}}</span>
        </li>
    </ul>

    <div class="total">
        <!-- Calculate the total price of all chosen services. Format it as currency. -->
        Total: <span>{{total() | currency}}</span>
    </div>

</form>
// Define a custom filter called "currency". 
Vue.filter('currency', function (value) {
    return '$' + value.toFixed(2);
});

var demo = new Vue({
    el: '#main',
    data: {
        // Define the model properties. The view will loop
        // through the services array and genreate a li
        // element for every one of its items.
        services: [
            {
                name: 'Web Development',
                price: 300,
                active:true
            },{
                name: 'Design',
                price: 400,
                active:false
            },{
                name: 'Integration',
                price: 250,
                active:false
            },{
                name: 'Training',
                price: 220,
                active:false
            }
        ]
    },
    methods: {
        toggleActive: function(s){
            s.active = !s.active;
        },
        total: function(){

            var total = 0;

            this.services.forEach(function(s){
                if (s.active){
                    total+= s.price;
                }
            });

           return total;
        }
    }
});
@import url(https://fonts.googleapis.com/css?family=Cookie);

/* Hide un-compiled mustache bindings
until the Vue instance is ready */

[v-cloak] {
  display: none;
}

*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The order form
--------------------------*/

form{
    background-color: #61a1bc;
    border-radius: 2px;
    box-shadow: 0 1px 1px #ccc;
    width: 400px;
    padding: 35px 60px;
    margin: 50px auto;
}

form h1{
    color:#fff;
    font-size:64px;
    font-family:'Cookie', cursive;
    font-weight: normal;
    line-height:1;
    text-shadow:0 3px 0 rgba(0,0,0,0.1);
}

form ul{
    list-style:none;
    color:#fff;
    font-size:20px;
    font-weight:bold;
    text-align: left;
    margin:20px 0 15px;
}

form ul li{
    padding:20px 30px;
    background-color:#e35885;
    margin-bottom:8px;
    box-shadow:0 1px 1px rgba(0,0,0,0.1);
    cursor:pointer;
}

form ul li span{
    float:right;
}

form ul li.active{
    background-color:#8ec16d;
}

div.total{
    border-top:1px solid rgba(255,255,255,0.5);
    padding:15px 30px;
    font-size:20px;
    font-weight:bold;
    text-align: left;
    color:#fff;
}

div.total span{
    float:right;
}

To display the prices in a correct format we will have to define a simple currency filter. Filters allow us to lazily modify or filter the model data. To define a custom filter we have to use the following syntax:

// Define a custom filter called "currency". 
Vue.filter('currency', function (value) {
    return '$' + value.toFixed(2);
});

As you can see our filter is pretty straightforward - it adds a dollar sign and proper number decimals. Just like in Angular filters are applied using the | syntax - {{ some_data | filter }}.

4. Instant Search

Here we will create an app, that exhibits some of the articles on our website. The app will also have a text search field allowing us to filter which articles are displayed. All of the articles will be held in an articles array, and those articles that match the search query will be in a computed property called filteredArticles.

<form id="main" v-cloak>

    <div class="bar">
        <!-- Create a binding between the searchString model and the text field -->

        <input type="text" v-model="searchString" placeholder="Enter your search terms" />
    </div>

    <ul>
        <!-- Render a li element for every entry in the computed filteredArticles array. -->

        <li v-for="article in filteredArticles">
            <a v-bind:href="article.url"><img v-bind:src="article.image" /></a>
            <p>{{article.title}}</p>
        </li>
    </ul>

</form>
var demo = new Vue({
    el: '#main',
    data: {
        searchString: "",

        // The data model. These items would normally be requested via AJAX,
        // but are hardcoded here for simplicity.

        articles: [
            {
                "title": "What You Need To Know About CSS Variables",
                "url": "https://blog.daitra.xyz/2016/03/what-you-need-to-know-about-css-variables/",
                "image": "https://blog.daitra.xyz/media/2016/03/css-variables.jpg"
            },
            {
                "title": "Freebie: 4 Great Looking Pricing Tables",
                "url": "https://blog.daitra.xyz/2016/02/freebie-4-great-looking-pricing-tables/",
                "image": "https://blog.daitra.xyz/media/2016/02/great-looking-pricing-tables.jpg"
            },
            {
                "title": "20 Interesting JavaScript and CSS Libraries for February 2016",
                "url": "https://blog.daitra.xyz/2016/02/20-interesting-javascript-and-css-libraries-for-february-2016/",
                "image": "https://blog.daitra.xyz/media/2016/02/interesting-resources-february.jpg"
            },
            {
                "title": "Quick Tip: The Easiest Way To Make Responsive Headers",
                "url": "https://blog.daitra.xyz/2016/02/quick-tip-easiest-way-to-make-responsive-headers/",
                "image": "https://blog.daitra.xyz/media/2016/02/quick-tip-responsive-headers.png"
            },
            {
                "title": "Learn SQL In 20 Minutes",
                "url": "https://blog.daitra.xyz/2016/01/learn-sql-in-20-minutes/",
                "image": "https://blog.daitra.xyz/media/2016/01/learn-sql-20-minutes.png"
            },
            {
                "title": "Creating Your First Desktop App With HTML, JS and Electron",
                "url": "https://blog.daitra.xyz/2015/12/creating-your-first-desktop-app-with-html-js-and-electron/",
                "image": "https://blog.daitra.xyz/media/2015/12/creating-your-first-desktop-app-with-electron.png"
            }
        ]
    },
    computed: {
        // A computed property that holds only those articles that match the searchString.
        filteredArticles: function () {
            var articles_array = this.articles,
                searchString = this.searchString;

            if(!searchString){
                return articles_array;
            }

            searchString = searchString.trim().toLowerCase();

            articles_array = articles_array.filter(function(item){
                if(item.title.toLowerCase().indexOf(searchString) !== -1){
                    return item;
                }
            })

            // Return an array with the filtered data.
            return articles_array;;
        }
    }
});
/* Hide un-compiled mustache bindings
until the Vue instance is ready */

[v-cloak] {
  display: none;
}

*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The search input
--------------------------*/

.bar{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius: 2px;
    width: 400px;
    padding: 14px;
    margin: 45px auto 20px;
    position:relative;
}

.bar input{
    background:#fff no-repeat 13px 13px;
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkU5NEY0RTlFMTA4NzExRTM5RTEzQkFBQzMyRjkyQzVBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkU5NEY0RTlGMTA4NzExRTM5RTEzQkFBQzMyRjkyQzVBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RTk0RjRFOUMxMDg3MTFFMzlFMTNCQUFDMzJGOTJDNUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RTk0RjRFOUQxMDg3MTFFMzlFMTNCQUFDMzJGOTJDNUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4DjA/RAAABK0lEQVR42pTSQUdEURjG8dOY0TqmPkGmRcqYD9CmzZAWJRHVRIa0iFYtM6uofYaiEW2SRJtEi9YxIklp07ZkWswu0v/wnByve7vm5ee8M+85zz1jbt9Os+WiGkYdYxjCOx5wgFeXUHmtBSzpcCGa+5BJTCjEP+0nKWAT8xqe4ArPGEEVC1hHEbs2oBwdXkM7mj/JLZrad437sCGHOfUtcziutuYu2v8XUFF/4f6vMK/YgAH1HxkBYV60AR31gxkBYd6xAeF3VzMCwvzOBpypX8V4yuFRzX2d2gD/l5yjH4fYQEnzkj4fae5rJulF2sMXVrAsaTWttRFu4Osb+1jEDT71/ZveyhouTch2fINQL9hKefKjuYFfuznXWzXMTabyrvfyIV3M4vhXgAEAUMs7K0J9UJAAAAAASUVORK5CYII=);

    border: none;
    width: 100%;
    line-height: 19px;
    padding: 11px 0;

    border-radius: 2px;
    box-shadow: 0 2px 8px #c4c4c4 inset;
    text-align: left;
    font-size: 14px;
    font-family: inherit;
    color: #738289;
    font-weight: bold;
    outline: none;
    text-indent: 40px;
}

ul{
    list-style: none;
    width: 428px;
    margin: 0 auto;
    text-align: left;
}

ul li{
    border-bottom: 1px solid #ddd;
    padding: 10px;
    overflow: hidden;
}

ul li img{
    width:60px;
    height:60px;
    float:left;
    border:none;
}

ul li p{
    margin-left: 75px;
    font-weight: bold;
    padding-top: 12px;
    color:#6e7a7f;
}

The input field is bind to the searchString model. When text is entered the model is instantly updated and the computed filteredArticles array is generated again. This way we can create a real-time search without having to worry about rendering or setting up event listeners - Vue.js handles all that!

5. Switchable Grid

In our last example we will demonstrate a common scenario where a page has different layout modes. Just like in the previous app we will be showing a list of articles from blog.daitra.xyz stored in an array.

By pressing one of the buttons in the top bar you can switch between a grid layout containing large images, and a list layout with smaller images and text.

<form id="main" v-cloak>

    <div class="bar">

        <!-- These two buttons switch the layout variable,
             which causes the correct UL to be shown. -->

        <a class="list-icon" v-bind:class="{ 'active': layout == 'list'}" v-on:click="layout = 'list'"></a>
        <a class="grid-icon" v-bind:class="{ 'active': layout == 'grid'}" v-on:click="layout = 'grid'"></a>
    </div>

    <!-- We have two layouts. We choose which one to show depending on the "layout" binding -->

    <ul v-if="layout == 'grid'" class="grid">
        <!-- A view with big photos and no text -->
        <li v-for="a in articles">
            <a v-bind:href="a.url" target="_blank"><img v-bind:src="a.image.large" /></a>
        </li>
    </ul>

    <ul v-if="layout == 'list'" class="list">
        <!-- A compact view smaller photos and titles -->
        <li v-for="a in articles">
            <a v-bind:href="a.url" target="_blank"><img v-bind:src="a.image.small" /></a>
            <p>{{a.title}}</p>
        </li>
    </ul>

</form>
var demo = new Vue({
    el: '#main',
    data: {
        // The layout mode, possible values are "grid" or "list".
        layout: 'grid',

        articles: [{
            "title": "What You Need To Know About CSS Variables",
            "url": "https://blog.daitra.xyz/2016/03/what-you-need-to-know-about-css-variables/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2016/03/css-variables.jpg",
                "small": "https://blog.daitra.xyz/media/2016/03/css-variables.jpg"
            }
        },
        {
            "title": "Freebie: 4 Great Looking Pricing Tables",
            "url": "https://blog.daitra.xyz/2016/02/freebie-4-great-looking-pricing-tables/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2016/02/great-looking-pricing-tables.jpg",
                "small": "https://blog.daitra.xyz/media/2016/02/great-looking-pricing-tables.jpg"
            }
        },
        {
            "title": "20 Interesting JavaScript and CSS Libraries for February 2016",
            "url": "https://blog.daitra.xyz/2016/02/20-interesting-javascript-and-css-libraries-for-february-2016/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2016/02/interesting-resources-february.jpg",
                "small": "https://blog.daitra.xyz/media/2016/02/interesting-resources-february.jpg"
            }
        },
        {
            "title": "Quick Tip: The Easiest Way To Make Responsive Headers",
            "url": "https://blog.daitra.xyz/2016/02/quick-tip-easiest-way-to-make-responsive-headers/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2016/02/quick-tip-responsive-headers.png",
                "small": "https://blog.daitra.xyz/media/2016/02/quick-tip-responsive-headers.png"
            }
        },
        {
            "title": "Learn SQL In 20 Minutes",
            "url": "https://blog.daitra.xyz/2016/01/learn-sql-in-20-minutes/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2016/01/learn-sql-20-minutes.png",
                "small": "https://blog.daitra.xyz/media/2016/01/learn-sql-20-minutes.png"
            }
        },
        {
            "title": "Creating Your First Desktop App With HTML, JS and Electron",
            "url": "https://blog.daitra.xyz/2015/12/creating-your-first-desktop-app-with-html-js-and-electron/",
            "image": {
                "large": "https://blog.daitra.xyz/media/2015/12/creating-your-first-desktop-app-with-electron.png",
                "small": "https://blog.daitra.xyz/media/2015/12/creating-your-first-desktop-app-with-electron.png"
            }
        }]

    }
});
/* Hide un-compiled mustache bindings
until the Vue instance is ready */

[v-cloak] {
  display: none;
}

*{
    margin:0;
    padding:0;
}

body{
    font:15px/1.3 'Open Sans', sans-serif;
    color: #5e5b64;
    text-align:center;
}

a, a:visited {
    outline:none;
    color:#389dc1;
}

a:hover{
    text-decoration:none;
}

section, footer, header, aside, nav{
    display: block;
}

/*-------------------------
    The search input
--------------------------*/

.bar{
    background-color:#5c9bb7;

    background-image:-webkit-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:-moz-linear-gradient(top, #5c9bb7, #5392ad);
    background-image:linear-gradient(top, #5c9bb7, #5392ad);

    box-shadow: 0 1px 1px #ccc;
    border-radius: 2px;
    width: 580px;
    padding: 10px;
    margin: 45px auto 25px;
    position:relative;
    text-align:right;
    line-height: 1;
}

.bar a{
    background:#4987a1 center center no-repeat;
    width:32px;
    height:32px;
    display:inline-block;
    text-decoration:none !important;
    margin-right:5px;
    border-radius:2px;
    cursor:pointer;
}

.bar a.active{
    background-color:#c14694;
}

.bar a.list-icon{
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkYzNkFCQ0ZBMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkYzNkFCQ0ZCMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RjM2QUJDRjgxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RjM2QUJDRjkxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7h1bLqAAAAWUlEQVR42mL8////BwYGBn4GCACxBRlIAIxAA/4jaXoPEkMyjJ+A/g9MDJQBRhYg8RFqMwg8RJIUINYLFDmBUi+ADQAF1n8ofk9yIAy6WPg4GgtDMRYAAgwAdLYwLAoIwPgAAAAASUVORK5CYII=);
}

.bar a.grid-icon{
    background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjBEQkMyQzE0MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjBEQkMyQzE1MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MERCQzJDMTIxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MERCQzJDMTMxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4MjPshAAAAXklEQVR42mL4////h/8I8B6IGaCYKHFGEMnAwCDIAAHvgZgRyiZKnImBQsACxB+hNoDAQyQ5osQZIT4gH1DsBZABH6AB8x/JaQzEig++WPiII7Rxio/GwmCIBYAAAwAwVIzMp1R0aQAAAABJRU5ErkJggg==);
}

.bar input{
    background:#fff no-repeat 13px 13px;

    border: none;
    width: 100%;
    line-height: 19px;
    padding: 11px 0;

    border-radius: 2px;
    box-shadow: 0 2px 8px #c4c4c4 inset;
    text-align: left;
    font-size: 14px;
    font-family: inherit;
    color: #738289;
    font-weight: bold;
    outline: none;
    text-indent: 40px;
}

/*-------------------------
    List layout
--------------------------*/

ul.list{
    list-style: none;
    width: 500px;
    margin: 0 auto;
    text-align: left;
}

ul.list li{
    border-bottom: 1px solid #ddd;
    padding: 10px;
    overflow: hidden;
}

ul.list li img{
    width:120px;
    height:120px;
    float:left;
    border:none;
}

ul.list li p{
    margin-left: 135px;
    font-weight: bold;
    color:#6e7a7f;
}

/*-------------------------
    Grid layout
--------------------------*/

ul.grid{
    list-style: none;
    width: 570px;
    margin: 0 auto;
    text-align: left;
}

ul.grid li{
    padding: 2px;
    float:left;
}

ul.grid li img{
    width:280px;
    height:280px;
    object-fit: cover;
    display:block;
    border:none;
}

Conclusion

There is a lot more to Vue.js than what we've showcased in these examples. The library also offers animations, custom components and all sorts of other features. We recommend you check out the excellent official documentation which is full of information and helpful snippets.

Having troubles deciding whether Vue.js is the right library for your project? The following links will be of great help to you:

  • An official, detailed comparison with other frameworks - here.
  • TodoMVC - a website where the same app is recreated with many different frameworks.
  • Our articles where we've done similar examples using React and Angular.js.

Thanks for reading!

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Comments 16

  • These are great, but I am pulling my hair out (I am new to vue.js) trying to figure out how to include the menu example (for example) inside my main Vue app (App.vue, I guess as a component?) Any tips you can offer? Thanks!

You rock Dude !

I always wanted more concrete examples of how to use Vue.js and now i have them. Thank you, Danny.

Arvind Bhardwaj

Thanks for nice and simple explanation!

Nice explanation.

May I suggest you write up an article on donejs? It is an excellent highly flexible and performant framework similar to angular and react with a lot more.
https://donejs.com/

Hi,
nice article, but notice these things:

  1. In the third example 'total' should be a computed preoperty since it depends on data observables. For usage please look: https://github.com/guypeer8/js-todos/tree/master/vue
  2. When binding events on your html, you may use v-on or just @. For example- v-on:click is equivalent to @click. Similar about classes, you can use v-bind:class or simply :class.

Great! More please!

I just started evaluating Vue.js for one of our projects. Vue.js is awesome and very well documented. These are some really useful examples for starters. Thanks Danny for sharing these.

Yes really nice examples thank you

I dont think the search works anymore in Vue 2.0. Sucks because I need to use it.

shubhampatwa

Thanks bro!, Gives me perfect start for vue.js

Thanks for the great tutorials first.

Do we must use v-bind on element attribute?
<a v-bind:href="article.url"><img v-bind:src="article.image" /></a>

Or we can do something like this?
<a href="{{article.url}}"><img src="{{article.image}}" /></a>

Danny Markov

Although the href="{{data.url}}" syntax is easier to read and works most of the time, there are known bugs with it causing 404 errors.

Vue.js even shows warnings when you try and use the template syntax:

[Vue warn]: src="...": interpolation in "src" attribute will cause a 404 request. Use v-bind:src instead.

So yeah, v-bind is safer and recommended.

P.S There is a shorthand version if you find it too verbose:

<!-- full syntax -->
<a v-bind:href="url" rel="nofollow"></a>
<!-- shorthand -->
<a :href="url" rel="nofollow"></a>

Got it, thanks for your explanation!

Why you are not using computed: for calculating total?