Conf42 JavaScript 2021 - Online

Make Your Electron App Feel at Home Everywhere

Video size:

Abstract

Electron gives you the power to write a single application for Windows, MacOS and Linux. But Electron apps can easily feel out of place among other applications, exactly because you have so much freedom in designing your UI. This feeling of something being “off” often comes down to the details, not your overall UI.

Kilian takes you through the process of making your app feel at home on all three platforms, making you aware of the pitfalls and how to avoid them.

Summary

  • Electron is a framework to create cross platform apps using web technologies. Building and packaging apps is straightforward and even doable. Cross platform and the chosen abstractions make it easy to port over web applications. But the devil is in the details when it comes to building good uis for all platforms.
  • Kilian Valkov talks about eight ways to make your electron app feel great. For each platform, it will need to take over some of the customs that that particular operating system uses. The combination of this custom UI with what you expect from your desktop is what makes or breaks an application.
  • On Windows and Linux the window is the application. On Mac, however, you need to keep your app running even if there is no main window. Two important things are remembering window positions and remembering last opened folders.
  • Building a crossplatform app that feels great everywhere doesn't require conforming to the platform UI. Use the right keyboard, shortcuts command for Mac and control for Windows and Linux. Keep your memory leaks under control. These are all the ingredients to make app field at home on all platforms.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
You everyone uses apps on their machine that somehow just feel off. Maybe the mouse works slightly different, or menus look weird, or maybe you can't even articulate it. Something's just wrong. There are a million tiny things that can make an app feel off, regardless of which framework it's built in. But if you know what to look for, you can make your app feel great on on all platforms, this talk is about Electron, but I don't want to spend too much time on what Electron is, so here's a quick run through Electron is a framework to create cross platform apps using web technologies. It came out of GitHub, where it was developed for their code editor, Atom. Electron combines a recent version of Chromium node and a set of operating system specific APIs. In essence, they combine the power and freedom of developing for the web with the right apps to interact with your operating system, with the file system, applications, et cetera. What electron does is not new per se. There have been many attempts at this going back to Adobe Air in 2008. What I think makes electron different, and why it has seen such widespread adoption, is that Electron gets most of the things right. Building and packaging apps is straightforward and even doable. Cross platform and the chosen abstractions make it easy to port over web applications. The skill set you already have if you develop for the web or with node, can be used to create electron apps. Here's an example of the code you need to show the Conf 42 schedule as an app. Notice it's not that many lines of code and it's all just regular javascript. Basically, we start an app, then we create a browser window for that app. And lastly, when all the windows are closed again, we also quit the application. Now back to this idea of cross platform applications. We all know all these operating systems have pretty different interfaces. The good and the bad news is that the devil is in the details when it comes to building good uis for all platforms. This is good news because it means if you want to get it right, you don't need a giant redesign of your app to make it conform 100% to each platform. You don't need to reimplement everything using native widgets, and you can keep your app's unique style. We have web apps and mobile devices to thank for that. Because of web apps and mobile devices, users have grown more tolerant and welcoming to different types of interfaces. If something like Gmail would have launched as a Mac app, it would have been disregarded in an instant. But superhuman, launched 13 years later, comes just fine with a very custom interface. So more custom app UI are much more acceptable nowadays, but you do need to pay attention to the details. This is especially interesting for electron app apps because they tend to have a much more custom UI than apps built in other frameworks using native widgets. And strangely enough, the fully custom UI that an app like Slack has is actually a benefit. By creating a consistent interface for slack across different platforms, it actually becomes easier to use for your users. For each platform, however, it will need to take over some of the customs that that particular operating system uses, and the combination of this custom UI that works well with what you expect from your desktop is what makes or breaks an application. In this talk, I'll walk you through eight design and implementation details I think matter the most. Tell you how to think about them and how to solve them through code and design first off, a little about me my name is Kilian Valkov. For the past 20 years, I've developed websites, web applications and desktop software. I'm also part of the electron governance team. For the past ten or so years, I've used a number of different technologies to publish desktop applications. I've used QT and I've also used GTK. But before I moved to Electron, these were just theoretically cross platform. There was nothing much in the code itself to prevent them from being run on different platforms, but the process of building and packaging was just too opaque for me. Then after discovering Electron, suddenly it was easy for me to distribute apps on all platforms, and I've created dozens of them, open source and for clients alike. Electron made that possible for me, and it also helped me focus on the real cross platform details. So here's eight ways to make your electron app feel great. On all three platforms, we all know what it looks like to load a web page. You stare at a white page for a while, then things start to pop in, and after some time, everything's loaded. We also know how loading an app works. You stare at the icon bouncing in your dock for a while, and then the app pops into view, fully formed. Because electron essentially loads a web page, it'll try to do the former. We want to do the letter, and there's two things we need to do for that. So if we go back to the quickstart example, what's happening here is that it shows the window and then starts loading the page. What we want to do is flip that around. We want to wait, showing the app until the page we're showing has fully loaded. Electron gives us a handy event for this, called ready to show when we create a new window using browser window. We initially hide it with show colon false. Then we wait for the ready to show event and at that point we show the window. This guarantees your page has loaded before the windows is shown. The other thing we want to do here is focus the window once it's shown. This is what happens for native Windows two and it lets users interact with it straight away. The second thing we want to do here is set a background color on your browser window instead of the default white for window backgrounds while pages are loading. The background of your window now follows the background of the rest of your application, making it feel much more cohesive and less like a web page. If you have an app that takes a while to load and want to make it feel faster, you can also opt to show the page with a custom background color before the ready to show event so something is already visible on the screen and animating your UI or show a skeleton screen as soon as possible. The time to using your app might be the same or even slightly longer, but because stuff is happening on the screen it'll feel better for your users. We're skipping all the way to the end now, but the way your app closes is just as important as the way it opens. And it's here where something slightly different happens on Windows versus Mac conceptually, on Windows and Linux the window is the application. On Mac, however, the window is just an instance of the application. What this means is that on Windows if you close the app window, you close the app. On Mac the app actually stays open and active in your dock and clicking it will relaunch your window. So on Windows and Linux if someone comes the app window you can safely quit the app as well. On Mac, however, you need to keep your app running even if there is no main window. Darwin here is the internal name for macOS that we can use to match. Now what you'll need to do is make some changes to the little comes example we just gave and export everything to a create window function because we need to recreate the window when needed. With the create window function we can safely call that as soon as the app is ready and what we do here is add a listener so that when the main window is closed we also delete it again by setting it to null. Then when clicking the doc icon we get the activate event and with this activate event we can recreate a window again if there is not currently a window available. So either the window will be created or we'll just open the existing window. Now on the web we save user preferences, but all of them tend to be app specific user preferences. On the desktop, however, we also have meta user preferences, and they're not things you might think about because they don't exist on the web, but they're really important for not frustrating your user. Two important things in that regard are remembering window positions and remembering last opened folders. Once you start user testing your desktop app, you'll find out that nearly each user has their own preference for where your app is on the screen and which dimensions it has. If a user has to reset that every time they open your app, they are going to move on to an app that does conform to their user preferences. So keeping track of your window position is a really nice thing to do. There are a number of variables you want to keep track of, the window dimensions, the position which screen it's on, and whether or not the app is maximized. And the last one is actually pretty tricky. You see, maximized is a state your app can be in, but if you exit the maximized state, native apps restore to the previously userset geometry. So while you should save the fact that your app is maximized, you shouldn't actually save the geometry for that state. Building this yourself is not that hard. Electron has events for the resize and move events, and if you get the window geometry and save that on each of these events, as long as it's not in a maximized state and store that, you can retrieve it again on applaunch and use that. You can save these settings in a flat file or use something like electron settings which you can get on NPM. On applaunch you get the Windows state and test if it has bounds. It won't have those the first time someone opens the app. So you do need to provide adequate fallbacks. Then a gotcha. You can't start a window in its maximized state. You can only maximize a window after it's shown. So we check if we should maximize in the ready to go ready to show event after the window is shown. But if you want to keep things simple, there is the electron window state package on NPM that does this work for you. It will also take care of some uncommon edge cases like resetting the position if your app was on a screen that's no longer connected to your computer. The other thing to keep track of is if your app supports saving or loading files. You want to keep track of the last use folder so that every time a user performs an action, they don't have to drill down from their comes directory. Again, the nice thing to do here is similar to the window positioning let users continue where they left off. If I navigated to a folder to select something, there's a high chance I want to use that folder again next time I do the same action like saving or opening a file, so navigating to that folder saves users a lot of time. What you want to do as a developer is on each successful interaction with the file system. Store the path that the user ended up choosing and next time for the same interaction, start with that path. That path has the highest chance of being the correct path, or at the very least it's better than just opening someone's home directory. You'll notice most native applications also do this. Now I mentioned storing this path on successful interactions. You don't want to store the path if a user ended up canceling the interaction. Obviously what they wanted to find wasn't on that particular path. A couple of versions ago, Electron did not ship with the default application menu, and particularly on macOS. This gave some issues. If an application doesn't have an application menu with cut, copy and paste in it, then you can't actually perform those actions in your app. Guess who found that out after shipping a note taking app? Luckily, nowadays Electron will give you a default menu if you don't set one yourself solving that issue. But the default menu is pretty Mac centric, and to supply menus that also make sense on Windows and Linux, where there's a file menu item instead of the app name and the help menu generally doesn't have search functionality, you'll have to replicate the entire menu structure yourself. To solve this, I made an NPM package called Electron Create Menu. It replaces the menu API that electron gives you and creates a platform appropriate menu for you automatically. It also gives you a new property for each menu item or menu section that it uses to determine which platform to show things on. This way, you can have a single menu structure for all three platforms and still show the appropriate menu items and titles. This is what it looks like. The first object is shown on Mac only and the bottom item is hidden on Mac only, so it's shown on Windows and Linux. And this way you can very easily differentiate your menu between platforms. Text highlighting when you press command a on a website it looks like this, but if you do the same in say, pages, it's a little different. The text selection is only contained to the actual writable area that's currently focused, and none of the UI or buttons are suddenly highlighted to get the right effect in electron, where it doesn't mess up your entire app UI. We need a little CSS to help out with user select none on the body. None of your app's UI text will be selectable. You might think you need to unset this for input fields, but chromium already takes care of that for us. Not every application needs a context menu, the menu that shows when you right click somewhere. But it is something that people expect particularly in text areas with cut, copy and paste at least because context menus are context dependent. Electron doesn't give you one by default, but it does give you an event you can respond to that lets you create a context menu yourself and then show it. It will tell you what the context menu was triggered on so you can show relevant menu options. If something is editable you can add but copy and paste and other relevant options. But if you right click on a link you might also want to show a copy link location option. You get quite a bit of information on a right click alongside knowing if where you right clicked is editable or a link. You can also get the link text, deselected text or detect if you're right clicking an image or a video. If you want to keep things simple there is the electron context menu NPM package that provides some basics for text, Linux and images right out of the box. Different operating systems use different keys for shortcuts and even though things are converging, it's still something you need to think about. There is one major difference where Windows and Linux uses control. Mac actually uses command which we refer to as the super or Windows key. On Linux and Windows, shortcuts in electron are created as global shortcuts which means they work regardless of your app being focused. Keyboard shortcuts are written out as a string. So you can write Alt plus R or backspace and those will work. But what if you want to add a safe shortcut? Do you add two control s and a command s? Electron helps us out here because you can actually type command or control and electron will pick the right one depending on the platform. That's going to save you a lot of if statements to make an app integrate with the system using the same font as the operating system is a really powerful way to make it feel cohesive. Unfortunately the browser default font is not always the system default. Especially when that's user customizable. So you could add a huge font stack like this. This one is from GitHub. Unfortunately, even though it's very long it doesn't account for the usual Linux system fonts being Ubuntu Sans oxygen and deja vu Sans. Luckily, there's a simpler way to do this. System UI is a special keyword value in CSS that macOS to the font the operating system uses. It's well supported in Chromium, and since we know that's what we're running on, we can use this without a fallback. So this will automatically pick Ubuntu Sans on Ubuntu, Segway UI on Windows, and can francisco on Mac. You can also, of course, use your own fonts like slack comes with the font leto. In this case, it's best to ship the font along with your application, either as a wolf two or a TTF. So that's my eight tips and I'll review them in a second. But I can't give a talk about electron without mentioning memory now. Personally, I think this is not the huge problem everyone pretends it is. Sure, your average vim user is going to bark at using 100 megabytes base memory just to run the app, but really, it's not significantly higher than most other GUI heavy apps. The problem you can run into though, is memory leaks. If you come from the web, you really only need to care about the worst of memory leaks. The runtime of a single page tends to be relatively short. This changes a little if you're working on spas, but even those tend to throw in a full page refresh every now and then. Apps are much longer lived, and because of that, small memory leaks can also become issues. So let's check out some strategies in dealing with these. Before I start, I want to mention that Electron has excellent performance documentation at Electron app Docs tutorial performance that focuses on making sure your app starts fast and that actions feel snappy. Things like bundling your code only, shipping the polyfills you need, and loading code strategically, it's an excellent resource, and it's definitely worth checking out. Now, for memory leaks, we get the benefit of Chromium and its developer tools. In recent years, the developer tools have added really good performance tooling, and you can use that right in electron to suss out any memory issues in your application. You can start a recording in this screen making sure that memory is checked, and use your app or the functions in your app that you want to test. When you're done, you end up with this view. It's a little intimidating and too much to go in through for this presentation, but what you want to focus on is this graph. It shows the memory usage and the number of listeners. If both of these go up and up and up. There's probably a listener you're not clearing somewhere. Additionally, in the flame graph above the chart, you can see some function calls that take too long, recognizable by the red ranked angle. These would be calls to look into to see what is causing the slowdown. Now for the note part of Electron, you can also use the chrome devtools in the same way. If you start electron with the inspect flag, then open chromium and go to chrome inspect and pick your application from the list. So to conclude, building a crossplatform app that feels great everywhere doesn't require conforming to the platform UI 100%. Thanks to web apps and mobile devices, people are more familiar with different interfaces, but the devil is in the details to make your app feel at home, take care of at least these things. Don't launch your app like a web page, but hide it until the page is loaded and give it the right background color. Follow the platform's way of handling apps and windows. Keep the app running on macOS. Remember user preferences like Window Geometry and the last opened folder give users the menu, titles and actions they expect from their platform. Prevent users from selecting UI text and make sure you have a context menu where users expect one. Use the right keyboard, shortcuts command for Mac and control for Windows and Linux. Use the system font as a way to make your app feel part of the platform and lastly, keep your memory leaks under control. These are all the ingredients to make app field at home on all platforms. Check out electronjs.org for more information on electron. My name is Kilian Valkhof and you can find me at these links.
...

Kilian Valkhof

Author @ Polypane

Kilian Valkhof's LinkedIn account Kilian Valkhof's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways