Build an Offline Demo of your Ember App with Ember CLI Mirage

Our client wanted a demo version of their Ember app to take to trade shows, use for sales demos, etc. Ideally, it would run on a laptop with no internet connection and not require the use of any developer tools. We decided to run the Ember app against a mocked backend.

## Mirage

After briefly surveying [available options](,-fixtures,-and-factories) for demo backends, we selected [Ember CLI Mirage], for a couple reasons:

– It runs entirely in the browser, with no actual server required.
– It doesn’t depend on Ember Data (which our app doesn’t use).

[Alex] and I spent a couple days spiking out whether it could do everything we needed, and then integrated it with our app. The [documentation]( is pretty good, so I won’t go into all the setup. Instead, here are some highlights from our implementation.

## Keep a Tidy Configuration

For starters, in order to isolate demo mode from our existing _development_, _test_, and _production_ environments, we disabled mirage globally, and put its configuration behind an environment variable:

ENV['ember-cli-mirage'] = {
  enabled: false // off everywhere but demo

if (process.env.DEMO_MODE) {
  ENV['ember-cli-mirage'] = {
    enabled: true

  ENV.APP.FEATURE_X = false;

This enables us to run with `DEMO_MODE=true ember server`, or after placing the command in package.json’s _scripts_ section, `yarn demo`.

## Test It

We have Mirage disabled by default in our _test_ environment in order to not interfere with our large suite of existing tests, but we start it [manually]( for two new types of tests:

**Demo Acceptance Tests**: These click around the app as it runs against the mirage backend with its fixture data, proving that it works as desired.

**Data Validation Tests**: As the primary backend changes over time, demo mode’s fixture data could easily fall out of sync. To detect this granularly, we walk over all the demo data, serialize each record, and verify that it produces a valid client model. (We happen to have [strict models](, [generated]( from backend types.)

Rarely-used demonstration features can easily be neglected and rot; these tests will help protect ours from breaking as the rest of the app evolves.

## Make a Portable Build

When configured correctly, an Ember app can run from disk, with no webserver required. It’s a little tricky to get routing and asset paths to work correctly with `file://` URLs; here’s the magic incantation to drop in the _demo_ config section:

ENV.rootURL = '';
ENV.locationType = 'hash';

– [rootURL]( _”identifies the path at which an Ember application is served so Ember’s router can know which path segments to ignore.”_
– [locationType]( governs how Ember interacts with the browser URL.

You may find that these changes break some hardcoded ` server.create(‘record’), 25000);`
– Deploy your demo build _within_ your development build: `ember build && DEMO_MODE=1 ember build –output-path dist/demo`
– Use [`window.navigator.onLine`]( ) to approximate whether the app is online, in order to handle special cases around other network dependencies.
– Create an [electron app](!

## Try it Out!

I encourage you to try mirage in your Ember project, even if you have no need for an offline demo. It makes a pretty great [development API]( The [documentation]( is good, the maintainer is responsive, and the slack channel (`#ec-mirage` on the [Ember Community Slack]( has been very helpful.

[Ember CLI Mirage]:
[Ember Data Factory Guy]:

  • Esteban says:

    Hello! In my company we are constantlly using Ember.js apps as our frontend, and I cannot but highly discourage window.navigator.onLine! For an offline-first application we developed built under the assumption the app would be used in very bad network connectivity, we came to realize that the window.navigator.onLine approach gave false positives constantly, even though the app was fully offline.
    Our approach, despite not being the most elegant one was simply a silly polling service that would either make HEAD calls to a google domain, or (in case the tablet was in a closed network) to a server we controlled.


    • John Ruble John Ruble says:

      Hi Esteban,

      Thanks for reading, and thanks for the tip! I’m aware that `window.navigator.onLine` isn’t bulletproof, but it has worked well for the (limited) needs of this app.

      In the real world, network connectivity isn’t black and white, and pinging the actual resource you need to talk to will always be more accurate.

      You may be interested in an approach we took on another project: hooking into Ember Data and `$.ajax` to queue and retry failed requests:

  • Comments are closed.