Earlier this week I was working with a desktop app (which I can't talk about... yet) that had an Ionic-look to it. On a whim, I opened up the package contents and discovered it was an Electron app. If you've never heard of it, Electron is an open source project that lets you build desktop apps (for Mac, Windows, and Linux) using web technologies. The last time I did anything in this space was with Adobe AIR, which was years ago. I've played with Electron a tiny bit, but I had not tried to use Ionic with it so I thought I'd give it a shot. Before digging in, I want to bring up two very important points.
First, I've spent maybe three hours looking into this subject, and obviously have not built a production application yet. This post focuses on some of the things I ran into while testing things out, but it is safe to assume more issues probably exist. As time goes on I'll probably blog other things to consider and I hope my readers will share their own discoveries.
Second, while Electron makes it easy to build a desktop app, I cannot stress enough that you need to remember that app development is not the same as web development. When I talk about Cordova and how it makes it easy to use the web to build mobile apps, I try very hard to remind people that building a mobile app is nothing like building a web page. Repeat that after me: "Building a mobile app is nothing like building a web page."
Ok, so with that out of the way, let's talk shop.
The Basics
The first thing I did was to see what would happen if I took a generic Ionic app and just plain ran it under Electron. To do that, I created a new Ionic app and used the -no-cordova flag. If you aren't aware, you can tell the Ionic CLI to create a new project and skip including all the Cordova bits. You still get a bunch of extra stuff like the bower file and gulp script, so I simply copied out the www folder.

Then, following the Electron quick start, I added a package.json file and main.js. For my main.js, I copied their code exactly, but removed the bits to open dev tools. (More on that in a bit.)
var app = require('app'); // Module to control application life.
var BrowserWindow = require('browser-window'); // Module to create native browser window.
// Report crashes to our server.
require('crash-reporter').start();
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600});
// and load the index.html of the app.
mainWindow.loadUrl('file://' + __dirname + '/index.html');
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});
Here's how my directory structure looked. And again, this is the result of taking the www contents from Ionic's default template (tabs) and adding in the files Electron requires.

At this point I went and just tried running it with the Electron CLI. Surprisingly, it just plain worked. In fact, even the $ionicPlatform.ready fired. As far as I can tell, it noticed that cordova.js didn't load and assumed I was running the app on a desktop browser.
So as I said, it just worked, which was cool, but then I began digging in and figuring out bits and pieces of code I needed to rip out and modify.
The first thing I did was remove cordova.js since - obviously - this was no longer a Cordova application. Here's the index.html for my app with that modification. I also added a title value. This is going to come into play in a bit.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>My App</title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
</head>
<body ng-app="starter">
<ion-nav-bar class="bar-stable">
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</body>
</html>
Next, inside app.js I completely emptied out the run block. We don't need to test for the keyboard plugins, and ionicPlatform.ready does not make sense in this content. Electron lets you do 'Desktop stuff', but doesn't make you wait for an event in your code.
I then noticed something odd. Here's the app running:

And here it is with another tab selected:

Did you notice the title? The title of the app changes as I change my view. Now, that could be nice, but to me, it doesn't make sense. The app should have a core title, like "My App", the one I used in html earlier, and the header could continue to be more context-driven. Unfortunately, the code that updates the title is built into Ionic itself and can't be disabled. I raised the issue in an Ionic chat room, and Leandro Zubrezki spent some time helping me out. The following solution is 100% his idea.
In order to prevent the title from updating, you can listen for the $ionicView.afterEnter event and stop it. So for example:
$scope.$on('$ionicView.afterEnter', function(ev, data) {
ev.stopPropagation();
});
This can be used in your controllers, but quickly gets repetitive. While talking with Leandro, he suggested using the root level state to define a controller and run it from there. Here is that top level state from the Tabs demo:
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html',
controller:'TestCtrl'
})
The change here is the addition of TestCtrl (not the best name). I then added this to the controllers.js file:
.controller('TestCtrl', function($scope) {
console.log('test ctrl');
$scope.$on('$ionicView.afterEnter', function(ev, data) {
ev.stopPropagation();
});
})
And that was it! It worked fine at that point.
One more tip. Don't forget you can enable dev tools for your app from the menu.

By default this will appear in the app. Don't forget you can 'pop it' out using the icon highlighted below.

That icon actually has three states - right, bottom, and popped out. So if it looks like this:

Clicking will just send it to the bottom. Click and hold to open a menu and select the icon I showed earlier. Using this, I was able to have my dev tools nicely separate from the app which made debugging easier. I also made use of the Reload option too so I didn't have to restart the app from the command line.

Speaking of the command line, it is worth noting that console.log messages will show up there too.

Whew - that was a lot. So if you want to see the code behind the modified Tabs demo, you can find it here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/ionic_electron_tabs
A Demo
With a basic understanding of how to make this work in place, I decided to try a desktop version of my RSS Reader. Here it is up and running:


Not the most thrilling app, but let me discuss how I changed it for Electron.
I began by making the modifications I mentioned above - removing cordova.js and ionicPlatform.ready. I then needed to do the "title fix" as I mentioned before. This was a bit weird for me as my app did not already have an abstract state on top. I ran into issues with this and got some great help from Mike Hartington. Here is my new app.js with the change to state.
(function() {
/* global angular,window,cordova,console */
angular.module('starter', ['ionic','rssappControllers','rssappServices'])
.constant("settings", {
title:"Raymond Camden's Blog",
rss:"http://feeds.feedburner.com/raymondcamdensblog"
})
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('root', {
url: '/root',
abstract: true,
controller:'RootCtrl',
template:'<ion-nav-view />'
})
.state('root.Home', {
url: '/home',
controller: 'HomeCtrl',
templateUrl: 'partials/home.html'
})
.state('root.Entries', {
url: '/entries',
controller: 'EntriesCtrl',
templateUrl: 'partials/entries.html',
})
.state('root.Entry', {
url: '/entry/:index',
controller: 'EntryCtrl',
templateUrl: 'partials/entry.html',
})
.state('root.Offline', {
url: '/offline',
templateUrl: 'partials/offline.html'
});
$urlRouterProvider.otherwise("/root/home");
}])
.run(['$ionicPlatform','$rootScope','$state', function($ionicPlatform, $rootScope, $state) {
$rootScope.goHome = function() {
$state.go("root.Entries");
};
}]);
}());
Essentially, my root state has a blank template because that's required in Angular. Well, not blank, but a template with just a nav-view. All I really wanted was my root controller. I won't show the code as it is the same event hack I described above.
Next, I had to make another modification. My RSS Reader app used the Network Information Cordova plugin (and ngCordova) to check to see if you were online. This is somewhat simpler in Electron - you just use navigator.onLine:
if(navigator.onLine) {
rssService.getEntries(settings.rss).then(function(entries) {
$ionicLoading.hide();
$rootScope.entries = entries;
$state.go("root.Entries");
});
} else {
console.log("offline, push to error");
$ionicLoading.hide();
$state.go("root.Offline");
}
So far so good. My final change was to the "Read Entry on Site" button. In the Cordova demo, this uses the InAppBrowser. But in Electron, we can actually use a shell command to open a url. The cool thing is that this will then use the user's desired browser to open a new tab:
$scope.readEntry = function(e) {
var shell = require('shell');
shell.openExternal(e.link);
};
Pretty cool, right? If you want the full source code, you may find it here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/rssreader_electron
Conclusion
All in all, I think this is an interesting idea. Ionic provides a great UI, and it looks just as good on desktop as it does mobile, and obviously the power of Angular to help architect your application is just as useful here. Certainly there are issues to keep in mind when building a desktop app that won't apply to mobile, but they are things you can address. I'd love to hear what people think, and if you build something (or have built something), please share it in the comments below!
Archived Comments
Great job Raymond! I've never played with Electron, although is nice to know that it fits well with Ionic.
Does Electron fill the the same niche as node-webkit?
well done!
Yes. And see this doc for differences: https://github.com/atom/ele...
Can we create database (MySQL) connectivity in an Electron application?
In theory - stress - in theory - you could try using the Node.js packages available for MySQL from your main.js file. Your web app can 'speak' to it, and vice versa, so it should be possible.
Ok Thank you :)
Hi, Thanks for the great post! Inspired from it I started a simple project - https://github.com/NaveenKa...
I noticed that the size of packaged app is too big (100mb). Do you know how this could be reduced? I assume it's because of node modules included.
No, I don't. To be honest, it doesn't seem like a big deal to me since we're talking desktops here. Most likely you won't have folks on dialup. I do get your concern though. Best I can suggest is asking them (and sharing with us what you find :).
Sure. Thank you :)
Thanks Raymond. Just the answer I was looking for. I'm looking at Ionic for a current project and thought, geez. it'd be nice to just pick this up and port to a desktop app! Thanks for sharing!
Just wow! Super amazed.
Glad you liked it.
Any chance we'll see a revisit to this article with Ionic 2 someday? :-)
Honestly had not thought of it - but maybe so. Certainly not till 2 is final. I've added it to my list of things to write about.
Which Ionic version do you suggest for new project. Ionic 1 or 2?
I feel weird answering this because I do *very* little real work. As a dev advocate, most of my work is blog posts, demos, presentations, and *not* client work. Therefore, my needs/requirements/etc do not necessarily match what most folks do.
In general, I'd feel iffy doing Ionic 2 for a production client since v2 isn't 100% done yet. It works darn well and I *love* coding with it. I'm trying to do all my blog posts in v2, but I'm not sure how safe I'd feel doing production work in something that isn't released yet. I have supreme confidence in the Ionic folks and their work, but if they themselves haven't marked it as final, then it isn't final. :)
The flip side is - if you are looking at a multi-month project timeline, I'd probably do v2 as my gut says v2 is *very* close to being done.
Thank you Raymond Camden for detail answer. This is the biggest dilemma currently almost ionic developer are having like me. I hope Ionic 2 will be stable soon.
Just to add a bit to this, I don't see any reason why you should not use Ionic 2. We have one big batch of changes coming in the next release that we hope to be our last, then we hope to be "done". Kind of echoing what Ray has already said, but if you have a project that needs to be done 3-4months from now, use Ionic 2. It's everything you love about ionic 1, but reworked to be faster, smoother, and more performant.
That being said, if you need to ship something by the end of next week, take the time and do and ionic 1 app.
Thank you Mike for answering the topic. You guys are doing great thing at Ionic. My next project is bit long term project so i will go with Ionic 2.
Hi Raymond,
your article is very detailed and explaining your points of view for development with Ionic and Electron. I got a question for your phrase you mentioned in the introduction, "Building a mobile app is nothing like building a web page."
I'm so sorry if I can't comprehend on what you intend to say about it, but I know that there are so many framework here today that allow to code responsive web apps adaptable for any device with the functionality (like Cordova) to build for browser, Android, iOS and so on... So, why don't make a "unique" coding adaptable for differente devices?
I repeat, maybe I don't knw what everyone of you know, so for you that phrase has on obvious meaning... But for me not. :-|
Thanks for your patience on reading this.
Bye,
Yuri
My point was more about how a content-driven site, like a blog, is very different from a web app, like your bank's web site for example. This blog, for ex, doesn't really have a functional UI outside of simple navigation, and that's fine because your just here reading, or writing comments, while your bank has a multiple buttons/UI elements etc to enable you to work with your accounts.
That's rough, but hopefully it gives you an idea of what I'm talking about.
Great Tutorial.It's help lot
Hey, I started an Ionic-Electron app myself and i searched to see if i'm the only one curious here! hahaha Nice Tut BTW!
Thanks!
Hi Raymond Camden thank you for the tutorial which I just read. One question I was having in mind is that how to hide the code from the folder? I mean is there a way to create just an exe file so that I can share it with my users as well rather than giving them the whole folder? Thanks in advance.
That I don't know. I'd check the Electron docs. Your only option may be to obfuscate the code.
Hi thanks for your prompt response. Please let me know if you find any solution to this. Thanks
Awesome blog post! Thank you very much!