Building Offline Experiences with HTML5 AppCache and IndexedDB

Users expect their Web sites and apps to work
well even when the network isn’t available. With data increasingly stored in
the cloud, developers want to enable fluid experiences that allow access to
data when there is no connectivity; when devices are disconnected
from the network or when they encounter dead spots in coverage.

In this post, we show how to create well-behaved offline sites
and apps using the following HTML5 features:

  • AppCache to store file resources locally and access them offline as URLs
  • IndexedDB to store structured data locally so you can access and query it
  • DOM Storage to store small amounts of text information locally
  • Offline events to detect if you’re connected to the network
Image of printout of recipe.

Example: Offline support for anywhere access

Let’s say you go shopping with a printout of a recipe
from your favorite food site but when you’re at the market, you can’t
find some key ingredients.

Imagine that when you were home using your mobile PC to browse the recipes site, a portion
of the site was automatically downloaded for offline usage. This
enables you to take your mobile PC to the store, access the site, and search for a new recipe at the market. The
best part
is that you can do this without being connected to a
network. As a consumer, you’d appreciate the site more
because it just worked when and where you needed it.

Offline search results for the word 'cake' using the recipe site. style="max-width: 100%;" src="" />
Status bar showing offline state
Offline search results for the word ‘cake’ using the recipe site.

As a developer, you can enable these types of scenarios with a combination of offline
, IndexedDB,
DOM Storage,
and WebSockets
(or XHR).
Before exploring the individual technologies, let’s explore the benefits.

For Metro style apps and Web sites, offline technologies allow you to handle connectivity
failures. Imagine that your user is filling out a form and he loses network connectivity.
What should your Web site or Metro style app do? A connection free development mindset
allows your app to work correctly independent of whether it is connected to the
network or not. Your app will just work correctly.

In more sophisticated scenarios, Web sites and apps allow users to create new content
and to store new data, even though the application is completely offline. Imagine,
Outlook Web Access (OWA), Hotmail, or GMail working seamlessly when offline, as
Outlook does today.

Offline technologies can also improve overall performance by serving cached resources
locally, pre-caching future information, and shifting processing power from the
cloud (or the network) to the client device. The more information you’re able to
cache locally, search locally, and compute locally, the fewer resources are needed
from the server and the faster your users’ experience will be.

The expectations of having a Metro style app work offline are higher than having
Web sites work offline. Because they’re deployed using self-contained packages from
the store, users expect them to have some type of offline functionality (e.g. games,
books, recipes, etc.). Even if these apps are not able to create or access new content,
previous content should be visible (e.g. Contacts, Meetings, feeds, magazines, etc.).

Cache file resources locally using AppCache

AppCache enables you to create long lived local caches of downloaded file resources;
resources you can access while offline or use while online to improve performance.
Imagine that a three year old child uses a laptop to download an interactive Web
game (KidsBook) from your home network. If the application’s resources are stored
locally, the child can continue to play the game in the car where there is no network

If KidsBook was implemented using AppCache, the game would have cached the necessary
resources (JavaScript, HTML, CSS, audio, video, etc.) for the game to be played
when it was first downloaded and later while disconnected from the network. This
allows the child to remain entertained, even though the device itself has no network

AppCache creation flow.
AppCache creation flow.

To see how to enable an interactive Web game to work offline, check out the
example on the IE Test Drive

AppCache uses a manifest file to specify resource URI’s in order to cache content
from a Web site. The caching happens behind the scenes after the browser displays
the Web page, which allows resources defined in the manifest file to be downloaded.
This guarantees that resources are downloaded to the local machine, as a unit in
a single transaction, to create a local cache. If a single resource fails to download,
no cache is created. To update content stored in a cache, update the manifest file
on your server. When the user next accesses the site, the browser compares the manifest
file on the server with the last cached copy. If the cached copy of the manifest
is different from the server copy, a new version of the cache is created using the
content defined in the updated manifest file.

AppCache also allows Internet Explorer and Metro style apps to access cached resources
offline using traditional URL’s. This allows you to type a URL in the browser window
and access this information without any network connectivity. In addition, offline
pages can resolve URIs using the local cached information. For code samples take
a look at the
HTML5 Application Cache (“AppCache”)
section in the
IE10 Developer’s Guide

Overall, AppCache provides some advantages over HTTP’s caching. HTTP caching doesn’t
guarantee that cached resources will be available after the TIF (Temporary Internet
Files) is cleared. Also, HTTP caching doesn’t correctly resolve URLs while offline.
However, HTTP caching can be used to optimize AppCache behavior by specifying the
lifetime of a cached resource. This will determine if a resource is downloaded from
the Web or copied from the cache when a new version of a local cache is created.

Metro style applications can benefit from AppCache by locally caching Web resources
that are accessed by iframes, which allows that content to be accessed offline.

Cache large structured data locally using IndexedDB

IndexedDB is a local
database designed to store JavaScript objects in the local machine, allowing fast
indexing and searching of objects. The recipe site presented earlier includes
a database with sixteen recipes extracted from a parent site. Imagine
using an RSS feed, a WebSocket, or an XHR connection to periodically update this
database. This would allow your users to have access to the latest recipes even
when they have no network connectivity.

IndexedDB enables you to manipulate and index JavaScript objects directly. An advantage
of using indexedDB to search for information locally is that it reduces your computing
costs by not forcing you to always search in the cloud. This assumes you’re able
to maintain the relevance of the data that is cached in the local system.

Shows a list of recipes stored on the local machine accessible via IndexedDB.
Status bar showing offline state
Shows a list of recipes stored on the local machine accessible via IndexedDB.

IndexedDB is a technology created around
database concepts. Like many Web platform technologies, it is designed
to provide a low-level API that can be used by various library abstractions built
on top of it. The following table compares IndexedDB development concepts with similar
concepts from the well understood
Relational model

Concept Relational DB IndexedDB
Database Database Database
Tables Tables contain columns and rows objectStore contains Javascript objects and keys
Query Mechanism, Join, and Filters SQL Cursor APIs, Key Range APIs, and Application Code
Transaction Types & Locks Lock can happen on databases, tables, or rows on READ_WRITE Transactions Lock can happen on database on VERSION_CHANGE transaction, on an objectStores on
READ_ONLY and READ_WRITE transactions. There is no object level locking.
Transaction Commits Transaction creation is explicit. Default is to rollback unless I call commit. Transaction creation is explicit. Default is to commit unless I call abort or there
is an exception that is not caught.
Property Lookups SQL Indexes are required to query object properties directly
Records/Data Normal form and single valued properties De-normal form and can have multi-valued properties

When using IndexedDB, you’ll create
which contain
object stores
(Contacts, Emails, Meetings, etc.). These object stores
contain the JavaScript objects that are required by your application (Contacts –
First Name, Last Name, Address, etc.). Each JavaScript object is expected to have
a unique identifier accessible via a
. In addition, object stores will contain
on properties that can be used to query the dataset (Emails
– Subjects, Dates, etc.). Filters will be used to organize or reduce the result
set via
on indexes or object store.

The following code snippet shows how to read a book record from a “Library” database:

var oRequestDB ="Library");

oRequestDB.onsuccess = function (event) {

db1 = oRequestDB.result;

if (db1.version == 1) {

txn = db1.transaction(["Books"], IDBTransaction.READ_ONLY);

var objStoreReq = txn.objectStore("Books");

var request = objStoreReq.get("Book0");

request.onsuccess = processGet;



Information contained in object stores is always accessed for read or write in the
context of a
. There are
three types
of transactions:

  • VERSION_CHANGE – used to create or update object store and indexes. Because VERSION_CHANGE
    transactions lock the complete database and prevent concurrent operations, they
    are not recommended to read and write records into the database.
  • READ_WRITE – Allows records contained in object stores to be added, read, modified,
    and deleted.
  • READ_ONLY – Allows records contained in object stores to be read.

The asynchronous API model provided by IndexedDB leverages the
/response model supported by many Web APIs, such as XHR. Requests
are submitted to the local IndexedDB process and results are handled by client’s

event handlers. In addition, there is no explicit mechanism
to commit a transaction. Transactions are committed when there are no more pending
requests on the server and no pending results on the client. Furthermore, it is
up to your application to handle exceptions and error events. In many cases, if
an exception or error event is not handled, the transaction is aborted.

In summary, IndexedDB is an optimized mechanism for querying data objects via indexes.
It provides sites with the APIs to access large amounts of related data via cursors
and to filter data using KeyRange objects. The pattern we believe developers will
follow is to have a “master” database with all the user records living in the cloud
and a local IndexedDB database with a subset of records to facilitate fast searches
and offline data access.

Store small textual data locally with DOM Storage & Offline/Online Events

Sites can use DOM storage and connectivity events to handle small amounts of textual
data and detect poor connectivity. Imagine a game that uses these technologies to
track the user’s score while offline. Imagine what would happen if you got a high
score when there was no network connectivity. Would the Web page hang or crash?

Because this data is textual in nature and it doesn’t require a lot of space, you
can use DOM
to store the information locally, without requiring network connectivity,
and upload it at a later time when the network is available. DOM Storage supports
more data than cookies and doesn’t require data encoding. In addition, DOM Storage
doesn’t send data to the server on each request, and can be scoped to domain or
sessions access.

Screen shot of a game that uses DOM Storage to record the high scores.

Using this technology is as simple as accessing the windows.localStorage object.
In this object, you can check or add name/value pairs in the form of properties.
The following code snippet shows how to store the game score locally using localStorage:

<script type="text/javascript">

var lStorage;


function init() {

if (window.localStorage) {

lStorage = window.localStorage;


if (typeof lStorage.score == 'undefined')

lStorage.score = 0;


document.getElementById('score').innerHTML = lStorage.score;




function save() {

if (lStorage) {

lStorage.score = getGameScore();




<body onload="init();">


Your last score was: <span id="score">last score insert in init()</span>



In addition, the
events will help you detect when you have network access so you can push the data
to your server. For example, you can detect when you are online and update the database
with content from the server using WebSockets or XHR.

This is as simple as checking for the state of the
property. The following code shows how to register for
online and offline events:

function reportConnectionEvent(e) {

if (!e) e = window.event;

if ('online' == e.type) {

alert('The browser is ONLINE.');


else if ('offline' == e.type) {

alert('The browser is OFFLINE.');




window.onload = function () {

status = navigator.onLine; //retrieve connectivity status

document.body.ononline = reportConnectionEvent;

document.body.onoffline = reportConnectionEvent;


In Metro style apps, we’re providing an additional API,
, that allows you to store more data types locally
and enables them to roam across multiple machines.

The key point is to design your application or Web site with the idea that your
connectivity may disappear at any time and you need to be able to handle that situation
smoothly. Implementing a data pattern that stores information locally before sending
it to the cloud will allow you to deal with problematic network connections.

Update local data with WebSockets and XHR

For some scenarios, your customer data will continue to live in the cloud in order to be
easily accessible from any device. Therefore, you need to ensure that cached data
remains relevant, current, and up-to-date. In order to do that you need to create
channels to synchronize data between the cloud and your app. You can leverage href="">WebSockets
and XHR to facilitate this synchronization. This requires you to package your data
into transferable formats (e.g. XML or JSON), use XHR or Websockets to transfer
those resources to the client, and then use XML or JSON parsers to create JavaScript
objects that will be stored in the IndexedDB database. This can also be used to
upload information stored on DOM Storage to the server.


Network connectivity is not always reliable; however, your apps need to be. Using
these offline technologies to anticipate network shortage will make your apps even
better in many consumer scenarios and situations. Furthermore, you can take advantage
of a huge opportunity to differentiate your site and Metro style apps by allowing
them to work properly offline. This will increase their usage and create a larger
opportunity for your service. Leverage the various offline technologies specified
here (AppCache, IndexedDB, DOM Storage, and others) to cache as much information
as possible locally.

For more information, check out the BUILD presentation Building offline access in Metro
style apps and Web sites using HTML5
, which also describes the role of the
File API
to handle offline scenarios.

—Israel Hilerio, Principal Program Manager, Internet Explorer

Comments (24)

  1. hart says:

    This is very good, but when will be available ie10pp3 for win7?

    Will be available ie10 final for vista?

  2. Good Listener says:

    Cool! What’s the ETA on speech API?

  3. Tim says:

    Its official – if you develop your web sites and applications for IE… you are about to get into a world of hurt!…/browser-market-pollution-iex-is-the-new-ie6

    72 versions!!!!!! For the love of God I hope MSFT truncates their browser-to-OS tie-in when it comes to lifecycles.  Only tie in support to the LAST 2 released browser versions available for that OS. ONLY!

  4. Mariam Wilson says:

    Thanks for providing us with IndexedDB. Incidentally, can this idea be extended to Network level? Like I have two browsers (or two PCs under same Network) and if I have cached some app's data in one browser, can it be accessible in other browser to reduce the load?

  5. @Tim says:

    STFU & stop trolling.

  6. Tim says:

    No browser version should *EVER* be supported for 10 years! – that is just absurd for an application!

  7. Tim says:

    @@Tim, I love Internet Explorer but I am a poor guy. I cannot afford to buy Windows 7. That's why complaining.

  8. Gérard Talbot says:

    @Israel Hilerio[MSFT]

    document.getElementById('score').innerHTML = lStorage.score;

    The "score" span has a text node and only a text node; so, there is no need – none whatsoever – to resort to innerHTML in your code. If you target DOM 3 Core compliant browsers, then textContent() is good. If you target DOM 2 CharacterData compliant browsers, then childNodes[0].nodeValue will be good, appropriate, suited and more efficient in such case. But again, there is no need to use innerHTML if there is no HTML to edit in the first place, to begin with.

    innerHTML is one of my most misused, overused and abused method found on the web, including MSDN and in IE blog code posts.


    if (!e) e = window.event;

    Strictly speaking, the code assumes that the global event object exists (and can be queried via window.event) even though it is not a sure thing; it's not a big issue but purists would recommend rather

    if(!e && window.event) {e = window.event;};

    The only reason to do this is if you need to cater for IE8 and lower. Therefore, best would be

    document.getElementById('score').childNodes[0].nodeValue = lStorage.score;


    window.onload = function () {

    status = navigator.onLine; //retrieve connectivity status

    document.body.ononline = reportConnectionEvent;

    document.body.onoffline = reportConnectionEvent;


    Please explain why this isn't part of the init() function; why such function is not called, is not triggered when and only when document.body.onload is fired, why it is fired when the window object is loaded. Window and document are not identical objects and are not loaded synchronously.

    Gérard Talbot

  9. George says:

    So regarding Paul Irish's post about the 72 versions of IE – I completely agree that this will be a mess and total turn off for developers regarding IE if it isn't changed fast.

    If you haven't read it, here's the article:

    Having the browser tied to the OS was the silliest thing ever and only ever done to get the D.O.J. off MSFT's back when they went to court.  Realistically supporting a software application that will change every year, for 10 whole years is utterly futile and although Microsoft has offered this in the past on their OS (and flagship office suite) I think its high time to cut that cord on the browser.

    As a Web App developer I've already changed my support statements to only support the last 2 RTM versions of IE.  It was a really tough call, but a necessary evil because I can't keep supporting IE6 or IE7 – and as soon as IE10 ships, support for IE8 will drop too.

    I'd highly recommend that Microsoft determine their long term solution to this because although they can waste time and throw money/resources at supporting 10 years worth of IE releases the rest of the world can not – and won't.


  10. CSSFriend says:

    Yeah my friend had images set to no borders in the CSS and Internet Explorer showed a border around the images..  My details about my operating system and laptop brand are listed below if that is helpful.

    Brand: Acer

    Operating System: Windows 7 64-bit

    Internet Explorer version: Internet Explorer 9 with all the updates

    Plugins in IE9: Flash, silverlight, windows media player plugin, etc.

    installed addons: None.

  11. hAl says:

    Nog maar weer eens een bug reportje:

    In IE9 mobile werkt @fontface niet correct.

    Getest met deze pagina:…/WebFonts

    Fonts zijn heel anders dan met de IE9 desktop engine.

  12. hAl says:


    Provide an URL for that imange border issue so we can see how the page should be rendered.

  13. hAl says:

    New try. Serves me right for copying a dutch post.

    Another  bug report:

    In IE9 mobile @fontface does not function correct.

    Tested with this page :…/WebFonts

    Fonts look very different compared to the rendering of the IE9 desktop engine.

  14. CSSFriend says:

    @hAI it was on the owner told me he had the CSS set to no borders for images. I told him to add the attribute border="0" to the html for the image which he did.. but still if it was in the CSS for no borders there really shouldn't be any borders!

  15. Klimax says:

    @Tim 27 Sep 2011 11:17 AM:

    Sorry Tim, but article contains at least one HUGE error rendering much of it wrong.

    There is no and never will be 72 browser versions at once. There are only 5. IE6,7,8,9 and future 10. You never ever test against emulation unless forced, because of pgrading to never browser while using site coded against older version.

    You test against two or three versions – IE8 (XP), IE9 (Vista,7) and IE10 (7,8;maybe Vista).

    Any other misinformation to promote?

  16. Victor says:

    @Klimax – Its not mis-information and Paul Irish posted a very informative article describing the "projected" browser versions over the next 10 years based on Microsoft's current support policies.

    Currently (today) I personally have to support IE9, IE8, and IE7 (we dropped IE6).  However even in that set of 3 browsers, there's nothing much stopping the user from switching browser compatibility modes… and if I develop a JavaScript plugin/widget… I need to test for quirks vs. standards in all 3 browsers if I happen to touch code that triggers a regression bug/change in one of the new "embedded emulators".

    The situation *already* sucks.  With Microsoft's plan to continue shipping yearly releases but still support versions shipped with (or during the lifespan of) an OS then there will come a time when I *should* need to support 10 versions of IE.

    I say *should* because they will still be supported by Microsoft at the OS level – however I can tell you now that as a developer I have no intention of *ever* supporting more than 3 versions of IE – regardless of OS version.  When IE10 comes out with Windows 8, I will be dropping support for IE7 (ok, not quite, there will be a 30 day transition period)

    Paul was just flagging this as a "Hey! this needs to be fixed!" item so that those at Microsoft that can do something about this problem can put their heads together to start working on a solution.

  17. hAl says:


    It seems the stylesheet on that site has borders set for .post img style.

    Would that not be the issue?

  18. GoodWill says:

    A little off-topic: Please take a look at…/09-21OEMFallDevices.mspx or any microsoft-presspass webpage, at the top there written "Click here to Install Silverlight" while there is no Silverlight component on the page and I have installed the latest SL5RC in my computer but still !! Please fix this critical issue 🙂

  19. Tina says:

    Sometimes, we receive links to download files as a plain-text email. Supposedly, the file type is PDF or some other which launches inside the bowser via corresponding add-on, and our intent is to download the file in that scenario, we would have to wait for the file to load completely then it can be saved.

    There must be number of other scenarios as well, which may require an option to manually enter the URL in download manager. Please provide us with a "Create Download" button in download manager, so the user can enlist the download by manually entering the URL to save a file, resource or the entire webpage within the download manager.

    Also, introduce the paste shortcut Ctrl+V in download manager for a single entry and Ctrl+Shift+V for batch entries.

  20. Prior Semblance says:

    That 72 browser version "info" is complete crap and I find it depressing that people actually believe it.  Theres 3 versions of IE you need to worry about right now (7,8,9) and I don't see that number going higher than 4 ever. In all likelyhood the increased quality in IE9/10 means sites are less likely to even need per-version tweaking, so in most cases 4 versions in the future would be easier than 2-3 today.  They also imply that people always auto-update other browsers, which is certainly not the case with firefox.

  21. the_dees says:

    Yes, 72 is a nonsensical number.

    You decide for one broser (version) mode and then you support that line of modes through the browsers.

    And most of the time that's not necessary.

    Unfortunately Library authors will have to take all versions into account, but that's part of their job anyway, isn't it?

  22. Harry Richter says:

    There is already a solution for your problem:

    Right-click the link and choose "Save as…"!


  23. Klimax says:

    @Victor 28 Sep 2011 1:42 PM:

    "However even in that set of 3 browsers, there's nothing much stopping the user from switching browser compatibility modes…"

    BS! As soon as you say X-UA-Comp, the user loses that option completely, unless he goes to dev tools, which are not exactly visbile.And by now I would expect people to use intelligently libs like jquery or PIE (and monitoring development, so they have bugfixes ready).

    Anyway Microsoft support is mainly relevant to corps with private web applications, not general public websites. (unless of course they are in some markets like SK or China…)

  24. DanglingPointer says:

    Using IE10 dev, can anyone get hit signin on and see if its working?? With browser mode IE10 and doc mode 8 and 10 its not working. However with browser mode 9 and document mode 8,9 and 10 its working!!

    Please check if you can reproduce this error and push it to their feedback/bug report system (connect), if its the problem with IE10 and post_back method! otherwise it looks like Telligent haven't revised their code for IE10 🙂