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.


After briefly surveying available options 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.

  • 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.