Important note: Since the status of Vue 2 binding is alpha, this guide will be updated frequently to reflect API changes and add more information about the bindings.

Starting Project

Download via npm

$ npm install onsenui vue-onsenui --save

Direct download

Get onsenui.js, onsenui.css and onsenui-css-components.css from the latest core release. You will also need vue-onsenui.js file from this CDN link.

Loading Onsen UI

Vue bindings for Onsen UI (VueOnsen) provide Vue 2 components and directives that wrap the core Web Components and expose a Vue-like API to interact with them. You need to load the following two JavaScript modules:

You can load with normal <script></script> and <link> tags as follows:

<link rel="stylesheet" href="onsenui.css">
<link rel="stylesheet" href="onsen-css-components.css">

<script src="vue.js"></script>
<script src="onsenui.js"></script>
<script src="vue-onsenui.js"></script>

Or, you can use VueOnsen from npm with a module bundler like Webpack. In this case, use the onsenui and vue-onsenui packages.

// Webpack CSS import
import 'onsenui/css/onsenui.css';
import 'onsenui/css/onsen-css-components.css';

// JS import
import Vue from 'vue';
import 'onsenui';
import VueOnsen from 'vue-onsenui';

Vue.use(VueOnsen);

Notice how VueOnsen is used as a plugin for Vue in the last line (this is already done automatically if window.Vue exists). Alternatively, you can use require instead of ES6 imports.

Onsen UI HelloWorld with Vue

To get started, let’s create a simple Hello World application. The following sample code is a Vue version of Onsen UI HelloWorld.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="onsenui.css">
    <link rel="stylesheet" href="onsen-css-components.css">
    <script src="vue.js"></script>
    <script src="onsenui.js"></script>
    <script src="vue-onsenui.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      template:
      '<v-ons-page>\
        <v-ons-toolbar>\
          <div class="center"> Title </div>\
        </v-ons-toolbar>\
        <p style="text-align: center">\
          <v-ons-button @click="$notification.alert(\'Hello World!\')">Click</v-ons-button>\
        </p>\
      </v-ons-page>'
    });
  </script>
</html>

This example is loading the following JS libraries, vue.js, onsenui.js and vue-onsenui.js. For stylesheets, it is loading onsenui.css and onsen-css-components.css which are bundled in Onsen UI distribution package. To know the details about Onsen UI stylesheets, please refer to our Style Sheets Guide document.

<link rel="stylesheet" href="path/to/onsenui.css">
<link rel="stylesheet" href="path/to/onsen-css-components.css">

In <body></body> tag, there is only a <div></div> tag having app id. This is where Vue will render the content into (el property in the constructor).

Alpha/Beta Components API

There are basically two different APIs in the alpha version. The first one is similar to the core API. This is, calling methods and properties from components. The second API is more similar to a proper Vue API and it’s based on props and directives. While the first one works well, the latter probably needs some improvements and that’s what we want to do during alpha and beta stages. Both of the APIs work for every component but they should not be mixed, stick to one of them and please send us feedback about your preference.

Methods API

Note: This API could be restricted in future versions in favor of the Props API.

It is recommended to check out the guide for JavaScript Core, since most of the attributes and properties are applicable to these Vue Components.

Every component in Vue bindings implement the core API. They expose the same methods, attributes and properties as the WebComponents directly in the Vue instance. Therefore, they can be accessed with $refs. An example would be:

<v-ons-navigator ref="myNavigator">
  <page1 :some-prop="true"></page1>
</v-ons-navigator>
...

$refs.myNavigator.pushPage(pageComponent);

While this works for every component, the routing components like v-ons-navigator, v-ons-tabbar and v-ons-splitter can be accessed from any children with the computed properties navigator, tabbar and splitter, respectively (assuming it has those 3 parents), that exist in every Vue instance (thanks to VueOnsenui plugin):

const page1 = { // Child page
  template: '<v-ons-page>...</v-ons-page>',
  methods: {
    push() {
      this.navigator.pushPage(page2, {data: { something: true }});
      // In page2 we can access to `this.data.something`.
    },
    changeTab() {
      this.tabbar.setActiveTab(3);
    },
    openMenu() {
      this.splitter.content.load(page4);
    },
    props: ['someProp']
  }
};

In the core normally these methods require a string page parameter (a template ID). In this case, we can pass directly Vue instances with v-ons-page component as the root. For the navigator, passing options.data parameter will modify the next page and include data as a property of the instance (this.data).

Also, similar to the core library, these components can also get a page prop with their initial component instead of nesting it:

<v-ons-splitter-side :page="menuPage"></v-ons-splitter-side>

<v-ons-navigator :page="page1" :some-prop="true"></v-ons-navigator>

<v-ons-tabbar :some-prop="true">
  <v-ons-tab :page="page1"></v-ons-tab>
  <v-ons-tab :page="page2"></v-ons-tab>
</v-ons-tabbar>

Since the Methods API makes a bit hard to pass down props to the pages, there is a feature where the required props of each page are inherited from the parent automatically (if the parent has it). In the previous code, page1 gets this.someProp === true from its parent. Note that these inherited props are not reactive for now, they must be used only as initial values.

Props API - Components Overview

It is recommended to check out the guide for JavaScript Core, since most of the attributes and properties are applicable to these Vue Components.

This API allows a better integration with Vue and is open to different combinations. For almost every custom element property from the core, there is a prop for its correspondent Vue component. Let’s check the components one by one.

Vue can detect changes on components’ children and only update those that were modified. Thanks to this, we can push or pop pages directly to the Navigator’s children array:

<template>
  <v-ons-navigator :options="{}">
    <div :is="page" v-for="page in pageStack" :page-stack="pageStack"></div>
  </v-ons-navigator>
</template>
<script>
  import { page1, page2 } from '...';
  export default {
    data() {
      return {
        pageStack: [page1, page2]
      }
    }
  };
</script>

Notice that in this example we pass pageStack prop down to the pages. We can also pass down methods to modify it from the parent context or, even better, use Vuex to store the stack.

<template>
  <v-ons-page>
    <v-ons-toolbar>
      <div class="left">
        <v-ons-back-button :on-click="pop">Back</v-ons-back-button>
      </div>
      <div class="center">Title</div>
    </v-ons-toolbar>

    Content
    <v-ons-button @click="push">Push</v-ons-button>
    <v-ons-button @click="replace">Replace</v-ons-button>
  </v-ons-page>
</template>
<script>
  export const page2 = {
    methods: {
      push() {
        this.pageStack.push(page3);
      },
      pop() {
        this.pageStack.pop();
      },
      replace() {
        this.pageStack.pop();
        this.pageStack.push(page3);
        // Or use this.pageStack.splice(...)
      }
    },
    props: ['pageStack']
  };
</script>

Vue detects modifications in this array through methods like push, pop, splice, etc. It also works by directly assigning a new array, although this is not possible if we pass the whole pageStack as a prop since they are not modificable.

The Navigator will notice the changes and run the appropriate animation whenever possible. Notice that in order to modify the page stack properly, every page should include a unique key prop. This will let Vue know that some of the pages did not change. If the key is not provided, Vue might consider that the whole stack is dirty and recreate all the pages. In this case, the Navigator could perform a wrong animation. Providing the key prop allows to safely modify any index of the page stack with, for example, pageStack.shift() or even pageStack.splice(0, pageStack.length - 2) without performing any animation (since the top page does not change). However, without key prop the top page will be considered as a new component, thus triggering a “pop” animation.

Splitter

Loading new pages in both v-ons-splitter-content and v-ons-splitter-side should be quite straightforward for Vue users. Simply swap the inner component as you want. You can even use keep-alive if you want to cache your components:

<v-ons-splitter>
  <v-ons-splitter-side v-ons-open="sideOpen">
    <menu-page></menu-page>
  </v-ons-splitter-side>

  <v-ons-splitter-content>
    <keep-alive>
      <div :is="pageContent1"></div>
    </keep-alive>
  </v-ons-splitter-content>
</v-ons-splitter>

For opening or closing the menu, however, we must use a custom directive called v-ons-open, which is basically a v-model for v-ons-splitter-side. Now you can modify the boolean sideOpen variable and the splitter will react vue consequently.

Tabbar

For the Tabbar we make use of Vue’s named slots.

<v-ons-tabbar v-ons-index="tabbarIndex">
  <template slot="pages">
    <page1></page1>
    <page2></page2>
  </template>

  <v-ons-tab icon="md-home" label="Home"></v-ons-tab>
  <v-ons-tab :icon="iconT2" :label="labelT2"></v-ons-tab>
</v-ons-tabbar>

Every v-ons-tab sets the page component that corresponds to its index on click. Optionally, v-ons-index custom directive can be used to assign the active index to the Tabbar. This might be helpful if you want to switch pages from JavaScript or any other way apart from Tab click. If this directive is not used, you can set active attribute in the initial tab.

Moreover, we can also use Vue’s power to delay the creation of some pages and also cache them if we want. In order to do this, we need to overwrite the Tab’s onClick prop:

<v-ons-tabbar>
  <template slot="pages">
    <keep-alive>
      <div :is="currentPage"></div>
    </keep-alive>
  </template>

  <v-ons-tab :on-click="() => currentPage = 'home'" :active="currentPage === 'home'"></v-ons-tab>
  <v-ons-tab :on-click="() => currentPage = 'settings'" :active="currentPage === 'settings'"></v-ons-tab>
</v-ons-tabbar>

The Carousel can also use v-ons-index custom directive to set its position.

<v-ons-carousel v-ons-index="carouselIndex" :swipeable="sw" overscrollable>
  <v-ons-carousel-item v-for="item in items"><v-ons-carousel-item>
<v-ons-carousel>

Forms

VOnsInput, VOnsSwitch, VOnsRange and VOnsSelect work like the corresponding core elements. A custom directive v-ons-model has been created to imitate the behavior of v-model in native inputs. This is, Array binding for checkboxes, groups for radio buttons, etc.

Floating action buttons

VOnsFab and VOnsSpeedDial components implement a visible prop that toggles their visibility. VOnsSpeedDial also implements open prop to toggle items’ visibility. onClick prop can be used to overwrite the default behavior (this should be used if open prop is present).

<v-ons-speed-dial :visible="isVisible" :open="isOpen" :on-click="isOpen = !isOpen">
  <v-ons-fab :visible="isVisible">+</v-ons-fab>
  <v-ons-speed-dial-item>A</v-ons-speed-dial-item>
  <v-ons-speed-dial-item>B</v-ons-speed-dial-item>
</v-ons-speed-dial>

Dialogs

VOnsDialog, VOnsAlertDialog, VOnsPopover and VOnsModal implement a visible prop to change whether they are shown or not. For those with a background mask element (all except VOnsModal, which is always fullscreen), a mask event is fired every time the mask is clicked. Use this event to “cancel” the dialog instead of the core “cancelable” attribute.

<v-ons-dialog :visible="dialogVisible" @mask="dialogVisible = false">
  Content here
</v-ons-dialog>

For quick alerts and prompts, $notification object is exposed in Vue prototype, so either Vue.$notification or this.$notification can be used. This works exactly as the core ons.notification:

$notification.alert('Hello world!');
$notification.confirm('Do you want to close the app?').then(index => { ... })
$notification.prompt('What\'s your name?').then(input => { ... });

In case you need to customize more your alert dialog, you can use the VOnsAlertDialog component:

<v-ons-alert-dialog modifier="rowfooter"
  :visible="alertVisible"
  @mask="alertVisible = false"
>
  <span slot="title">Title</span>
  Content here
  <template slot="footer">
    <div @click="alertVisible = false">Ok</div>
    <div @click="alertVisible = false">Cancel</div>
  </template>
</v-ons-alert-dialog>

Another API version of this component which makes use of props instead of slots is also provided:

  <v-ons-alert-dialog modifier="rowfooter"
    :title="'Title props'"
    :footer="{Ok: () => alertVisible = false, Cancel: () => alertVisible = false}"
    :visible="alertVisible"
    @mask="alertVisible = false"
  >
    Content here
  </v-ons-alert-dialog>

This component gets a target prop that can be any value supported by the core (DOM query, HTMLElement, Event object) or directly a Vue component reference.

<v-ons-popover
  :target="$refs.myToolbarButton"
  :options="{}"
  :visible="popoverVisible"
  @mask="popoverVisible = false"
>
  Content here
</v-ons-popover>
<v-ons-modal :visible="modalVisible">
  Content here
</v-ons-modal>
Pull Hook

VOnsPullHook emits events when its state changes:

<v-ons-pull-hook
  @action="done => { doSomething(); done(); }"
  @changestate="state = $event.state"
>
  <span v-show="state === 'initial'"> Pull to refresh </span>
  <span v-show="state === 'preaction'"> Release </span>
  <span v-show="state === 'action'"> Loading...  </span>
</v-ons-pull-hook>
Conditional component

Instead of <v-ons-if platform="android">...</v-ons-if>, we can use Vue’s v-if or v-show directives as follows:

<div v-if="$platform.isAndroid()"> ... </div>
Other components

The rest of the components are similar to core’s elements except for the v-* prefix.

Need Help?

If you have any questions, use our Community Forum or talk to us via Gitter chat. The Onsen UI team and your peers in the community will work together to help solve your issues.

For bug reports and feature requests use our GitHub Issues page.