Onsen UI is a complete Web Components driven UI framework for hybrid (aka. Cordova / PhoneGap) apps. It also is equipped with popular JS frameworks support, including Angular 1, Angular 2 and React. Although they are using the same core, we are optimizing the APIs for each framework.

This guide provides the basic concepts of Onsen UI. For more information about specific components, please see the reference section. A live example is included in each reference page with extended examples and information.

Starting Project

Note: For more details to use Onsen UI in your project, please refer to Getting Started.

Using Onsen UI toolkit - Monaca CLI

$ npm install -g monaca # Install Monaca CLI - Onsen UI toolkit
$ monaca create helloworld # Choose JavaScript template
$ cd helloworld; monaca preview # Run preview, or "monaca debug" to run on your device

Download via npm or bower

$ npm install onsenui
$ bower install onsenui

Loading Onsen UI

To load Onsen UI in your project simply include onsenui.js and CSS files for styling.

<!doctype html>
<html lang="en">
    <meta charset="utf-8">
    <link rel="stylesheet" href="lib/onsen/css/onsenui.css"/>
    <link rel="stylesheet" href="lib/onsen/css/onsen-css-components.css"/>
    <script src="lib/onsen/js/onsenui.js"></script>


Creating a page

The root of a page is created using the <ons-page> element. It covers the whole screen and is used as a container for the other elements.

    Some content

Adding a toolbar

A toolbar is defined as a <ons-toolbar> or <ons-bottom-toolbar> component. Here is the typical example of a toolbar.

    <div class="left">
    <div class="center">Title</div>
    <div class="right">
        <ons-icon icon="md-menu"></ons-icon>

  Some other content...

The toolbar is divided into 3 sections that can be specified as class names (left, center, and right). You can use <ons-icon> to display an icon, <ons-toolbar-button> or <ons-back-button> to place an button, or insert any HTML content.

Page lifecycle

<ons-page> provides a set of DOM events that will be fired in different moments of its life cycle. Use these events to alter the behavior on each page.

Page lifecycle events will be propagated to the page’s descendants so they are correspondingly shown, hidden or destroyed. For example, destroying <ons-navigator> will throw hide event only for the displayed page (navigator’s top page) and destroy event for every page in navigator’s page stack.

<ons-page id="page1">This is a blank page</ons-page>

document.addEventListener('init', function(event) {
  if (event.target.matches('#page1')) {
    ons.notification.alert('Page 1 is initiated.');
    // Set up content...
}, false);

The ons object

Onsen UI not only provides custom elements, it also exposes an object called ons-ons with a lot of useful functions attached to it. The ons object is part of the core library and can be imported in the bindings.

The following example uses ons.ready(fn) which waits until the app is completely loaded before executing a callback function. Inside the callback, it is calling ons.notification.alert() to display a alert dialog.

ons.ready(function() {
  // Onsen UI is now initialized
  ons.notification.alert('Welcome to Onsen UI!');

See also ons-ons.platform, ons-ons.notification and ons-ons.orientation for more utilities.

Understanding elements

All Onsen UI are custom elements which tag names are starting with <ons->. They have attributes, methods, properties and events just like the normal HTML elements (DOM Elements). This means they can be accessed by ID, tag name, class names or attributes and used directly as they are.

The source code below illustrates a basic usage of <ons-navigator> component. Note how component methods are called and events are attached.

<!-- You can place and mix-up with other elements -->
<ons-navigator animation="slide" id="my-navigator">
  <ons-page><span>This is the first page.</span></ons-page>

  // Get component instance
  var myNavigator = document.querySelector("#my-navigator"); // or use getElementById("my-navigator")

  // Add event listener
  myNavigator.addEventListener('postpush', function(event) {
    console.log('This function is called after a new page is pushed.');

  // Call component method

  // Access to component property

  // Change component attribute
  myNavigator.setAttribute('animation', 'fade');

Adding page content

For a full list of components please check the reference page.

Form elements

Onsen UI provides a rich set of form components. Apart from <ons-button>, <ons-switch>,<ons-select> and <ons-range>, perhaps the <ons-input> component is the most common one since it supports different shapes: checkbox, radio, password, etc.

    <div class="center">Form</div>
  <div style="text-align: center; margin-top: 30px;">
    <p><ons-input type="text" placeholder="Username" float></ons-input></p>
    <p><ons-input type="password" placeholder="Password" float></ons-input></p>
    <p><ons-button>Sign in</ons-button></p>


Lists are a very common pattern in mobile apps and thus Onsen UI provides abstraction for it. By using <ons-list>, <ons-list-item> and <ons-list-header> you can make simple or complex lists of items. Every list item is by default divided into three sections, just like <ons-toolbar>, and some CSS classes are provided for default styles (list-item__icon, list-item__thumbnail, list-item__title and list-item__subtitle).

    <div class="left">
      <ons-icon icon="md-face" class="list-item__icon"></ons-icon>
    <div class="center">
      <span class="list-item__title">Title</span>
      <span class="list-item__subtitle">Subtitle</span>
    <label class="right">
Infinite scroll

Adding new items to the list whenever the user reaches the bottom of the list is a very common practice. This use case is covered in <ons-page> component.

This component provides a onInfiniteScroll property that is called every time the scroll is near the bottom.

Lazy repeat

For cases when the list can contain thousands of items, <ons-lazy-repeat> component will enhance the performance by loading and unloading items depending on the current scroll.


Onsen UI provides a grid system to place your elements in the screen. The grid system divides the screen into rows and columns, just like a spreadsheet. The width and height of each grid is adjustable, and you can also condense two or more grids in a row or column, into one grid.

The layout can be performed by combining <ons-col> and <ons-row> components. The width and height can be adjusted in a flexible way.

Grid is not necessary in general for list items. Special layout is provided for list items based on flat iOS and Material Design specification. See list section for more information.

Control and visual components

Other components from different categories are available to complete the developer needs.

<ons-carousel> and <ons-carousel-item> components provide a simple carousel that can optionally be overscrollable and fullscreen.


A very common way to check to get updates in apps is given by the <ons-pull-hook> component, which enables a simple “pull to refresh” functionality.


<ons-speed-dial> and <ons-speed-dial-item> are just a set of floating action buttons (<ons-fab>) that can be shown or hidden by the user. This components are mainly used in Material Design.


<ons-progress-bar> and <ons-progress-circular> display the loading progress like it’s done in Material Design. Two modes are supported: display the exact progress that is provided to the component or display an indeterminated progress.


Onsen UI bundles three icon packs to be used with <ons-icon> component:

In general, Ionicons are good for iOS apps while the Material Icons work best for apps using Material Design.

Gesture detector

It is a common use case to detect a finger gesture and do a specific task. Onsen UI utilizes a modified version of Hammer.js for gesture detection. The Gesture Detector class (Hammer.js) is exposed in ons-ons.GestureDetector object.

var divGD = ons.GestureDetector(document.querySelector('#my-div'));
divGD.on('dragup dragdown', function(event) {
  console.log('drag Y axis');

If you want to use another library for this purpose and have any conflict, Onsen UI gesture detectors can be disabled easily:

ons.GestureDetector(document.querySelector('#my-menu')).dispose(); // Remove event listeners from the menu

Also, <ons-gesture-detector> component can be used to wrap the target DOM element that should detect the fingers in a handy way.


There are multiple types of dialog components available in Onsen UI: <ons-dialog> for displaying a page inside a centered dialog; <ons-alert-dialog> for displaying a simple message inside an alert style dialog; <ons-popover> for showing some content next to a specified element or a context menu; <ons-toast> for displaying a dismissable short message at the bottom or top of the page; <ons-action-sheet> (bottom sheet in Material Design) for letting users choose among a list of options that comes from the bottom side; and <ons-modal> for displaying a fullscreen dialog that forbids user interaction.

Apart from that, ons-ons object offers a more handy solution for simple dialogs:

ons.notification.alert('Hello world!'); // Basic alert
ons.notification.confirm('Are you ready?'); // OK - Cancel buttons
ons.notification.prompt('What is your name?'); // Text input
ons.notification.toast('New password saved.'); // Toast - Snackbar

ons.openActionSheet({ title: 'Actions', buttons: ['Copy', 'Cut', 'Delete'] }); // Options list

Multiple page navigation

In Onsen UI there are three navigation patterns based on three different components: <ons-navigator>, <ons-tabbar> and <ons-splitter>. These components supply “frames” able to change their inner content. The content of these frames will normally be <ons-page> components but it is also possible to nest navigation components in order to combine them.

More information is provided in the “Docs” tab of the Live Example section of each component.

The main pattern uses <ons-navigator> component to provide a stack where you can push and pop pages with transition animations. This is the basic and most used navigation pattern and can be combined with the other two.

Use this pattern when you have a sequential flow where a page depends on the previous one. Data can be optionally passed from one page to another.


In this case, by using <ons-tabbar> component a visible tabbar is displayed at the bottom or the top of the page with tabs associated to different pages. The component will load content depending on the selected tab (<ons-tab>). This pattern is commonly used to sepparate different sections in the app.

A menu can be added using the <ons-splitter> component. For small devices it can be used to create a swipeable menu, but for larger screens it can automatically display a column layout.

Splitter provides two frames that can load different content: <ons-splitter-side> and <ons-splitter-content>. A common usecase is to show a list in the side menu where each item loads a different page in the content frame. Notice that loading new content in any of these frames will completely remove the previous loaded content. For more complex navigation consider nesting <ons-navigator> inside <ons-splitter-content>.


Pages can be created in separate files and can be addressed by their path string. For example, myNavigator.pushPage('path/to/my/page.html'); will load page.html in path/to/my/ folder. The content of that file must contain a single <ons-page> component with some inner elements.

However, having multiple pages declared in the same file is sometimes more convenient. ons-template component is the solution for this. You can create as many pages as you need in your index.html (there and only there) by using this component. In this case, the template’s id attribute replaces the path string of the page:

  <ons-navigator page="page1.html"></ons-navigator>

  <ons-template id="page1.html">
    <ons-page>First page</ons-page>

  <ons-template id="page2.html">
    <ons-page>Second page</ons-page>

Notice that including <script></script> tags inside <ons-template> or separate files does not work. If you want to initialize a page, please check the init event in “Creating a page” section.

Using Modifier

Modifier is a cross-component way to provide customizability for Onsen UI components. When a component is defined with a modifier, it will have a separate class namespace so that you can apply custom styles to the component. Also, some components have several preset modifiers to change the appearance.

For example, each of the following buttons have different look. To change modifiers dynamically, please manipulate modifier attribute from JavaScript.

<ons-button modifier="quiet">Quiet</ons-button>
<ons-button modifier="light">Light</ons-button>
<ons-button modifier="large">Large</ons-button>
<ons-button modifier="cta">Call To Action</ons-button>
<ons-button modifier="material">Material Design</ons-button>

Cross platform styling

Onsen UI components are automatically styled depending on the platform where the app runs. You can easily test this feature with your browser Dev Tools by switching between iOS and Android views.

Automatic styling simply applies modifier="material" to the components when ons.platform.isAndroid() is true. You can disable this feature by running ons.disableAutoStyling() right after including onsenui.js (i.e. before the app is initialized). If you disable it you may need to manually specify modifier="material" in every component you want to display with Material Design. You can also specify disable-auto-styling attribute in specific components that you don’t want to auto style.

Some tools are provided to give a more accurate customization.

Platform utilities

ons-ons.platform object is available with methods such as ons.platform.isIOS(), ons.platform.isWebView(), etc.

You can set a platform with ons.platform.select('android'), for example, in order to display Material Design on every platform. This must be called before the app is initialized (right after including onsenui.js).

Conditional element

A conditional element called <ons-if> is available to filter content depending on the platform or orientation.

  <ons-if platform="android">
    This is Android
  <ons-if platform="ios other">
    This is NOT Android

With this, for example, you can display <ons-fab> for Material Design and other type of button for iOS flat design.

Icons shortcut

<ons-icon> component provides a shortcut to make auto styling easier:

<ons-icon icon="ion-navicon, material:md-menu" size="24px, material:20px"></ons-icon>

The second icon will be displayed when material modifier is present (other modifiers can be used).

CSS Definitions

Onsen UI styles are defined in onsenui.css and onsen-css-components.css. They are written in pure CSS using some extra features provided by cssnext.

onsenui.css is a core CSS module that defines styles for the custom elements. The source code exists under core/css directory. onsen-css-components.css contains CSS definitions for CSS components. The source code exists in css-components/src.

A local tool is included in Onsen UI core (onsenui) for previewing changes in Onsen CSS Component. This tool, located under onsenui/css-components-src/ directory in a local instalation, is also able to generate a new onsenui-css-components.css file that must be imported in the project.

Overriding CSS style

If you want to apply a different style to a specific component, you can use modifier attribute to override its style definition.

For example, if you want to apply a thick border only to a specific button, you can define like the one below.

<ons-button modifier="thick">Thick Button</ons-button>

Then, write the appropriate style code under the style tag or in the css file.

.button--thick {
  border: 10px;

Device Back Button

For Android devices, Cordova fires a backbutton event on hardware back button. It is important to understand that this event is fired by Cordova so it requires cordova.js file or similars (loader.js in Monaca) to be included in the app. Since it cannot be tested in browsers without Cordova, you can use Monaca Debugger for this purpose.

Onsen UI sets handlers with default behavior for Android back button in certain elements:

If the conditions are not met, these elements will call the parent element’s handler. The final global handler closes the app.

While this is probably the desired behavior for many apps, Onsen UI allows to modify these handlers for better customization. To modify the app global handler (closing the app), the ons-ons object provides few methods:

// Disable or enable

// Set a new handler
ons.setDefaultDeviceBackButtonListener(function(event) {
  ons.notification.confirm('Do you want to close the app?') // Ask for confirmation
    .then(function(index) {
      if (index === 1) { // OK button
        navigator.app.exitApp(); // Close the app

Apart from this, the mentioned elements together with ons-page element expose a way to modify their own handler:

var myNavigator = document.getElementById('my-navigator');
myNavigator.onDeviceBackButton.disable(); // Disables back button handler
myNavigator.onDeviceBackButton.enable(); // Enables back button handler
myNavigator.onDeviceBackButton.isEnabled(); // Returns true if enabled
myNavigator.onDeviceBackButton.destroy(); // Destroys the current handler

var page = myNavigator.topPage;
page.onDeviceBackButton = function(event) {
  console.log('Hardware back button!');
  event.callParentHandler(); // Calls the parent handler (navigator handler in this case)

The event object provided to this handler also contains a event.callParentHandler() method.

Extending animations

Onsen UI already provides multiple built-in animations for its routing components and dialogs. However, it is also possible to create custom animations for specific components or even extend existing animations and change part of them. This is a relatively advanced topic since it requires digging a bit in Onsen UI core code.


Onsen UI relies on Animit, a minimal animation library for managing CSS transtions on mobile browsers.

Animit can be accessed with ons.animit or import { animit } from ons;, depending on the type of the app. It exposes methods to queue CSS animations, apply delays and run callbacks as follows:

let animation1 = animit(myElement) // This defines the animation for the provided element
  .saveStyle() // Saves the original style of the element
  .queue({ // Original position/style in the animation
    css: {
      transform: 'translate3D(0, 100%, 0)'
    duration: 0
  .wait(0.2) // Delay applied before the transition starts
  .queue({ // Next step in the animation
    css: {
      transform: 'translate3D(0, 0, 0)',
    duration: 0.6,
    timing: 'linear'
  .restoreStyle() // Restores the original style of the element
  .queue(done => { // Optional "On transition end" callback

animation1.play(); // Run the animation

Since Animit modifies the element’s style property, it provides saveStyle() and restoreStyle() methods to ensure the previous styles are not lost. queue({css: {...}, duration: 0, timing: 'linear'}) or queue({...}, {duration: 0, timing: 'linear'}) method is provided to add transitions to the queue. The first one will be the first style applied in the animation that will transition into the following styles. In the provided example, we are moving a new page inside the view from right to left. Therefore, it needs to start at position translate3d(0, 100%, 0) and move to translate3d(0, 0, 0). Method wait(...) can be used to apply a delay between transitions. Finally, we can optionally call queue(function(done) { ...; done(); }) again to run a callback if necessary.

It is also possible to pass an array of HTML elements to animit if performing the same animation on multiple elements is required: animit([el1, el2]).saveStyle()....

It is very common to have more than one animation running at the same time. animit.runAll(animation1, animation2, animation3); method can be used for this behavior instead of animation1.play(); animation2.play(); animation3.play();.

Creating animators

Animators can be created from scratch by extending the necessary animator classes that Onsen UI provides. Every component exposes a minimum animator interface that must be extended and implemented: NavigatorTransitionAnimator, AlertDialogAnimator, DialogAnimator, PopoverAnimator, ModalAnimator, TabbarAnimator and SplitterAnimator. This is the desired way to implement animators if you want to build a custom version of Onsen UI or want to make a pull request to the repository. For more information, please have a look at the existing animators for every component.

ES2015 (ES6) is preffered but not strictly required for this to work. An example in ES5 can be found here.

Extending animators

Another way to make new animators is extending an existing animator and modifiying part of its behavior (or all). This is in general easier if you just want to tweak the appearance or timing, or even if you want to create a whole new thing starting from another animator. For this it is also required to have a look at the existing animators, choose one and check its properties and methods. Every animator provides a extend({...}) class method that returns a new animator. Animators are exposed in every component class: ons.NavigatorElement.animators or ons.AlertDialogElement.animators are some examples. These objects contain all the registered animators and can be extended as follows.

var fadeIOS = ons.NavigatorElement.animators['fade-ios'];
var customAnimator = fadeIOS.extend({
  timing: 'cubic-bezier(.1, .7, .1, 1)',
  delay: 0.1,
  push: function(enterPage, leavePage, callback) {

// This step is mandatory
ons.NavigatorElement.registerAnimator('customAnimationName', customAnimator);

This overwrites the push animation but uses the original pop animation. timing and delay properties will still affect both animations. Some animators have extra properties, such as backgroundMask. Please check the animator you want to extend to see all the properties.

After the new animator is created and registered, we can simply specify the animation with its name: myNavigator.pushPage('page.html', {animation: 'customAnimationName'}). Or make it default: <ons-navigator animation="customAnimationName">. The same applies to the other components.

Creating bindings

Since Onsen UI is implemented on top of Web Components (standard technology), it can be used with just pure JavaScript or potencially along with any JS framework. This is straightforward for libraries and frameworks that simply add a layer of usability to the development such as jQuery. However, with more complex frameworks that also manage the DOM itself or need to compile it, some extra logic is needed to tell Onsen UI how to create and load new elements according to these frameworks. This logic is what we call “bindings”.

In most of the cases they are simple wrappers that fix timing or DOM manipulation issues. Sometimes the bindings can implement some optional extra logic to support cool features of the framework. For example, in AngularJS it is common to write attributes for event handlers that can run code from controller scopes. This can be done in the bindings by simply defining these attributes (directives) and adding an event listener behind the scenes that runs a scoped handler. Thanks to this it is possible to tailor the code style in order to make it feel like a continuation of the used framework.

Onsen UI already have bindings support for the most common and possibly complex frameworks such as React, AngularJS, Angular 2 and Vue. Other libraries or frameworks like Knockout.js or jQuery are simple enough and do not require wrappers.

For those frameworks that do not have bindings yet, this guide explains how to extend Onsen UI to make it work properly.

Page loader for routing components

This is the main point that bindings usually need to modify. When creating new pages in the routing components (navigator, tabbar, splitter), Onsen UI core reads the content of ons-templates or makes a request to an external file and gets the content in string format. Afterwards, this string is converted to HTML and appended to the DOM. All of this is done through ons.pageLoader and can be modified to fit the needs of the framework in question.

Every routing component has a pageLoader property that can be overwritten with a new one. The pageLoader constructor needs two paramenter, a loader and an unloader functions:

const loader = ({page, parent, params}, done) => {
    .then((pageElement) => {

const unloader = (pageElement) => {

myNavigator.pageLoader = new ons.pageLoader(loader, unloader);

The loader function gets two parameters. The first one is an object containing three properties: page, parent and params. The second one is a done function that must be called with the final result. parent is the HTMLElement where the final result should be attached to (ons-navigator, ons-splitter-content, etc.). page is the parameter provided in the API of the bindings. For example, in the core library ons-navigator expects a string as the page parameter: myNavigator.pushPage('page.html', options). This parameter will be passed to the pageLoader as is. Therefore, the pageLoader decides what to do with that parameter. For the core it uses the string as a template ID or as a URL to get the real HTML content of the page. For Angular 2, this parameter is already an Angular 2 component so it just needs to be properly linked and loaded. Lastly, params is an object containing the extra options passed to the router component.

The unloader function is much simpler. It only receives pageElement as a parameter and must clean everything related to the element. In case your bindings store information outside the element itself and need a way to access it, JavaScript’s WeakMap might be a useful structure for this. It is possible to set a link to the elment’s information in loader function using the element itself as a key and retrieve it in unloader function.

Wrappers for content components

Most of the components are simple HTML + CSS with some enhanced behavior that should work without issue. However, due to peculiarities and features of some frameworks, there could be timing issues or other minor problems. Usully this is fixed by creating a wrapper around the original component and using it instead. This wrapper can implement event handlers or modify the component if necessary. For example, ons-input in AngularJS needs to parse its content with Angular’s $parse in order to make it work with ng-model attribute.


For real world examples, please have a look at the bindings already implemented in Onsen UI. We are happy to accept Pull Requests with new bindings or improvements for the existing ones in this directory.

Another interesting example is Knockout.js + Onsen UI, where a simple event listener is enough to add Knockout functionality to Onsen UI pages.

Under the hood of Custom Elements

If you need to customize the underlying HTML code, you can access to our core Custom Element definitions. The source code for each components are located under core/src/elements directory.

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.