How to use fonts in your hybrid mobile app

SquarePosted by Roel Nieskens on 3-8-2017

Fonts can give your content a unique look, so naturally you want to use them in your hybrid mobile app. And like any other asset, you want to prevent bloat, bugs and performance issues. File size influences the download size of your app, and the footprint when installed. Using the wrong format or a font that’s missing characters is ugly and annoying.

So what can you do to make sure you’re correctly embedding fonts in your app? For a recent project I got to geek out on this, and here’s what I learned:

Do you really need custom fonts?

First up: are you sure you need to include fonts instead of going with the ones included in the OS? Because there’s nothing better suited to display text on a device than the fonts it came installed with. They’re optimised for readability and performance, and will make your app look native. So unless your app design specifically describes a specific font, why not stick to system fonts?

Use the following @font-face rule in your CSS to use the main system font, as per CSS Tricks advice:

/* Define the "system" font family */
@font-face {
  font-family: system;
  font-style: normal;
  font-weight: 300;
  src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"),
       local(".LucidaGrandeUI"), local("Ubuntu Light"), local("Segoe UI Light"),
       local("Roboto-Light"), local("DroidSans"), local("Tahoma");

/* Now, let's apply it on an element */
body {
  font-family: "system";

(Take note, this is only for one style. Check the full CSS on Jonathan Neal’s Github)

Use a sane number of fonts

Typefaces come in different families (Open Sans, Helvetica, Comic Sans), with different weights (light, regular, bold, black), different widths (condensed, wide) and different styles (roman, italic). Each combination of those four variables demands a new font file. Do you need Open Sans in light, regular and bold? That’s three font files. Want italics for all those? You’re up to six font files. Need the same, but now for the typeface PT Sans? You got twelve font files.

Cutting down the number of fonts is not only practical and good for performance — it’s (usually!) a sign of good design. Don’t make a hodgepodge of styles: use a limited set of fonts to keep your design tight!

Avoid remote requests

If you’re creating a mobile app based on web tech, you might be tempted to drop in a remote Google Fonts stylesheet and have the font load from a server. It works for web pages, right?

A mobile app should also work without a connection to the internet. If your request fails and your fonts won’t be downloaded, your app will suddenly use a different font. Eek!

Use the right font format

As a webdeveloper, you might be familiar with the “bulletproof @font-face”:

@font-face {
  font-family: 'MyCustomFont';
  src: url('mycustomfont.eot');
  src: url('mycustomfont.eot?#iefix') format('embedded-opentype'),
       url('mycustomfont.woff2') format('woff2'),
       url('mycustomfont.woff') format('woff'),
       url('mycustomfont.ttf') format('truetype'),
       url('mycustomfont.svg#svgFontName') format('svg');

It might be tempting to drop this in your CSS, add all these different fonts to your assets and call it a day. How much better can you do than bulletproof, right?

Although this might work for a web project (and even then you might not need it), you’d be stuffing five different files in your app. Per family and width, weight and style combination!

But you’ll never need the .eot version in a mobile app — it just for old Microsoft desktop browsers. The .woff is a compressed version of a regular OpenType or TrueType font (.otf or .ttf), and serves as fallback for when the even better compressed .woff2 fails to load. Support for both versions is pretty good for modern browsers: .woff2 from iOS10 and Android Lollipop (5) upwards, and the older .woff from iOS5 and Android KitKat (4.4) upwards. But if you need to support slightly older devices too, .ttf or .otf is the way to go. They don’t have the smaller file sizes of the .woff and .woff2 version but are exactly the same otherwise. The .svg font “format” (it isn’t a format — it’s a hack) would only be useful if you want to support the oldest of iOS versions: iOS3 and iOS4, but users of these ancient machines would be better off with a system font.

So, to summarize: use an .otf or .tff font to embed in a hybrid app — it has the widest support on mobile platforms. For regular mobile websites, definitely keep using .woff and .woff2.

Remove hints for iOS

Creating an app for the iOS ecosystem? You can drop the hinting information from the font. iOS doesn’t use it, and including it means dragging along unused bytes — sometimes half the font’s size. What a waste!

You can use a tool like pyftsubset (part of the TTX/FontTools toolchain) to drop hints in your build process:

pyftsubset mycustomfont.ttf --no-hinting --name-IDs='*' --unicodes='*'

The option --name-IDs='*' keeps the name table intact, where the credits and licensing info reside, and --unicodes='*' says all characters in the font should be kept.

Don’t remove hinting for Android and other systems though: they do use it, which’ll seriously improve readability on devices with less-than-stellar screens.

A word of warning: we’re now reaching into fonts and ripping out stuff the creators have painstakingly added to improve rendering. Not only can this result in a less readable font, but it’s likely to be prohibited by the font’s license! Be sure to check if you’re allowed to do this.

Remove scripts for languages you don’t support

Fonts can come with support for a wide range of scripts. If you’re targeting the Dutch market, it’ll probably not make sense to include Cyrillic, Vietnamese or Hebrew letters. The character sets for these can be huge, and dropping them can reduce a font’s file size tremendously.

Don’t go overboard though — keep in a wide support for Latin characters to prevent names or words from neighbouring countries to show up in a fallback font, as Bram Stein effectively demonstrates:

Subsetting gone wrong, image courtesy of Bram Stein

We can use pyftsubset again to strip all language support other than full Latin from a font:

pyftsubset mycustomfont.ttf --name-IDs='*' --unicodes='0-024f'

OpenType features: use ’em or remove ’em

Fonts can pack an incredible range of OpenType features designed to make your content more readable. You’re probably familiar with kerning, where specific characters are moved closer to each other so they look better. While kerning is pretty much essential for text to be displayed properly, other features are optional.

Ligatures, for instance, can replace awkward combinations of letters with a more elegant design. Browsers (and thus apps built with Cordova, Phonegap, Ionic or our own Maji) usually have this enabled by default and since it will make your content look better it’s a good idea to leave them in. But your font might also contain small caps, oldstyle figures or stylistic sets. These aren’t all enabled by default, and you might not even need them for your specific purpose. If you do: enable them with the proper CSS, if not: strip these from your font.

Our trusted pyftsubset will drop all “non-vital” OpenType features by default, leaving only the ones essential to a great reading experience:

pyftsubset mycustomfont.ttf --name-IDs='*' --unicodes='*'

If you want to use all the extra features in your font, take a look at Utility OpenType which offers easy to add classes to enable features on a per-element basis. If you’re unsure which OpenType features your font supports, check with your font supplier or use pyftsubset to list them:

pyftsubset mycustomfont.ttf --name-IDs='*' --layout-features='?'

You can also use FontDrop, an online tool that offers extensive info about your font’s features.

Removing bloat from the font file

We’ve used the Swiss army knife of font optimisation for stripping hints and unused languages and layout features, but it can do more. In fact, by default it strips the font of all kinds of unused stuff.

Fonts can be equipped with instructions concerning desktop use — not relevant in a mobile/web environment. They can contain metadata about the tool that was used to design the letter shapes, or to build the font file. It can contain information for ancient OSes, or deprecated OpenType tables kept around for completeness sake.

If it’s safe to drop, pyftsubset will do so by default. So if you’ve ran any of the above instructions you’re good to go.

Are you allowed to modify the font?

To cut a long story short: you’re probably only allowed to do this with open source fonts (Github has a nice list of those). If you licensed the fonts from a foundry, chances are the license agreement prohibits you from hacking up the fonts like this — if you’re even allowed to embed them in an app in the first place!

Your font supplier has taken great care in delivering font files that are right for the job: usually you can just use them as-is, and never worry about having stripped out too much or messing up the font.

If in doubt: just ask the person that supplied the font. The designer on your team must’ve bought a license from somewhere — check with those people.


Check if you really need to include fonts, use .otf or .ttf if you do, and remove bloat and unused features. If you’re allowed to do so, that is. And always make sure you’ve not mangled your font by removing language support or OpenType features that turn out to be used by your app’s users!

Did I miss anything? Got questions? Ping me on Twitter or drop a comment below!


Roel Nieskens

Front-end developer, font guy and computernerd from hell. Tweets with @pixelambacht, writes on and codes on Github.

Bij Kabisa staat privacy hoog in het vaandel. Wij vinden het belangrijk dat er zorgvuldig wordt omgegaan met de data die onze bezoekers achterlaten. Zo zult u op onze website geen tracking-cookies vinden van third-parties zoals Facebook, Hotjar of Hubspot. Er worden alleen cookies geplaatst van Google en Vimeo. Deze worden gebruikt voor analyses, om zo de gebruikerservaring van onze websitebezoekers te kunnen verbeteren. Tevens zorgen deze cookies ervoor dat er relevante advertenties worden getoond. Lees meer over het gebruik van cookies in ons privacy statement.