How to create offline webapps on the iPhone

Recently Google launched their latest mobile version of Gmail optimised for iPhone and Android based browsers. One of the features that stood out was the offline access thanks to the browsers support of html5 application cache.

Documentation on the application cache feature supported in safari iPhone 2.1+ is scarce and of that documentation it doesn’t go into great detail. The best place to learn about this is on the Safari DevCenter under the mobile section, there it has 2 documents introducing the user to offline webapps; the first is a quick rundown on just the manifest file and the second article touches on a few more features available to offline webapps such as the javascript events for updating the cache when the user is online. We’ll delve into these later in the article but first let’s take a look at a working example.

Update: Added additional information about the event summary and the order in which events get executed. Added link to useful tool for sniffing file Content-Types.

To see the offline webapp in action, load the demo on your iPhone, then turn Airplane Mode on, re-open Safari and reload the demo. This time it will fetch the files from the cache that was created on the initial load.

How does it all work?

To get a basic offline webapp up and running is incredibly simple but it does have one caveat which tends to get a lot of people frustrated quickly when for some reason the offline feature isn’t working. We’ll explain that issue shortly.

The first thing we need to do is create what is called a cache manifest file which has references to all the resources we want to save. Whether it be JavaScript, CSS, images or html, below we can view the manifest file used in the demo.

# Offline cache v1
# html files
# css files
# js files
# images

The manifest file is a simple plain text file that holds the file names you wish to cache, the paths are relevant to where you saved the manifest file which is recommended to be in the root of the site. This file will be saved as filename.appcache, where filename is anything you wish. Lines that start with a # are comments and are ignored by the cache.

Referencing the cache file

We now have our cache manifest file setup and named, the next thing we need to do is reference that file in the html so this offline access will function for the specified files in the manifest.

<html manifest="thecssninja.appcache">

The manifest file is referenced in the html tag through the manifest attribute and just points to the filename.

Now the one issue that catches a lot of people when trying this out is not setting up the mime type correctly in their chosen web server. Whether it be IIS, Apache or something else, you’ll need to make sure that the .appcache file is fed through as text/cache-manifest.

For Apache you can add the text/cache-manifest to the mime.types file found in the conf folder in apache and add the following to the bottom of the file.

# html 5 application cache - offline access
text/cache-manifest    appcache

For IIS open up the IIS manager found in Control Panel > Administrative Tools > Internet Information Services double click the Mime Types icon in the manager and scroll down the list until you find the .appcache file and edit the mime type to have text/cache-manifest.

IIS 7.0 Manager Mime Types

That’s it, to get a basic offline webapp to work is incredibly simple all it takes is the manifest file to specify which files to cache, declare it in on the <html> tag using the manifest attribute and you have yourself a site still accessible even if the user has no network connection.

UPDATE: GeoNomad in the comments suggested a great tool to make sure your manifest file is being fed through with the correct mime type, Web Sniffer. I put my manifest file through the Web Sniffer tool, you can see that the Content-Type is set as text/cache-manifest.

Updating the cache

If you want to update the cache you either need to change/add file references to the manifest and then tell the cache to update. Updating a file that the manifest calls will not make the cache reflect the changes. The mozilla developer article on applicationCache makes a good suggestion to version your manifest file as the JavaScript events described below will only fire if there is a change to the manifest file. To update the cache we have:


This object has various stages depending on what the cache is doing which can be checked by using the .status method. There are 6 different stages:

  • Status 0 (UNCACHED) is returned which means that there is no cache available
  • Status 1 (IDLE) is returned means the cache you have is currently the most up-to-date
  • Status 2 (CHECKING) is returned means there is a change in your manifest file and it is checking it for changes
  • Status 3 (DOWNLOADING) is retuned means changes have been found and they are being added to your cache
  • Status 4 (UPDATEREADY) is retuned means your new cache is ready to be updated and override your current cache
  • Status 5 (OBSOLETE) is returned means your cache is no longer valid meaning it has been removed

These available statuses can be attached to the applicationCache object with event listeners so we can tell the web app what to do on what status. In our case we want to update the cache to reflect changes we have done.

var webappCache = window.applicationCache;

function updateCache() {

webappCache.addEventListener("updateready", updateCache, false);

In the above example we attach an event listener to the applicationCache looking for the updateready status which means there is a change to the manifest file and our cache is ready to be updated we do so by using the swapCache() method.

There is 2 more event handlers which do not appear in the window.applicationCache.status these are part of the event summary. Those are error & progress. We’ll take a look at how to use the error event, the progress event is basically the same as the downloading event handler.

var webappCache = window.applicationCache;

function errorCache() {
    alert("Cache failed to update");

webappCache.addEventListener("error", errorCache, false);

All the error event handler does is return the specified function we attached in the event listener when the cache is not available or does not exist.

Event summary

In the applicationCache we have various events available so when the cache is being updated, or as mentioned above when something goes wrong. We can attach to those events and keep the user informed on what is happening. These events will execute in specific order based on what is happening with the cache and therefore give us useful hooks to represent that back to the user.

Pete who commented below and needed a way of indicating to the user that the cache was downloading prompted me to look further into the events that get fired and which ones would be useful in this scenario. I created a demo which will indicate to the user that the cache is downloading by showing a loading icon and once the cache had finished downloading, the loader would be hidden.

The event order for this example was as follows:

  • checking – The page loads and the cache is checked for any changes.
  • progress – The download event actually gets fired before this but we don’t need to hook into that. The progress event will fire for each file that is referenced in your manifest until the cache has finished downloading.
  • cached – The cache has successfully downloaded and the cached event is fired so we utilise that to hide the loader.
  • updateready – This event is fired so we can then swap the old cache with the newly downloaded one.

In the above mentioned demo we also attached event listeners to the error and noupdate events so we can handle errors and if the cache is already up-to-date.

Is the user online

There is also a new method on the navigator object called onLine which returns a boolean value if there is a network connection or not.


In the article example the onLine method is used to update the title if the user is on/offline.

Only the beginning

The applicationCache is not yet finalised and is of course subject to change with newer and refined features. I’m sure there will be additions in the upcoming iPhone OS 3.0 release. If there are updates unfortunately these cannot be discussed until the NDA is lifted. But of course this won’t stop us speculating.

Some of the features available in applicationCache on firefox 3.1 and safari 4 will inevitably be added in future releases of the iPhone OS. Such as the NETWORK: and FALLBACK: sections in the manifest file, these allow for the developer to specify a file they wish to never cache or to have a file load if something fails with the first option.

Short sharp facts about the appcache:

Post filed under: css, javascript, xhtml.

Skip to comment form.

  1. Kush says:

    Thanks for the great guide. If I could offer some advice to anyone who is having trouble getting this to work, it would be to make sure that you clear the cache on Safari iPhone after making changes. I was having trouble getting the app to load offline and troubleshooted for several hours to no avail. Finally, I decided to try loading the app offline on OSX with Safari and Firefox and it worked perfectly. That clued me in that it was the iPhone that was the problem. I ended up clearing the cache, cookies, and history on the iPhone and then restarted the phone. Afterward, the app worked perfectly. Thanks again for the guide, it helped a lot.

  2. That’s a good idea. I have taken to renaming the folder I am drawing my site from. For instance, it’s named “beta10”. I make some changes and change it to beta10.1 and so on. I don’t have the cache turned on until after I have all my alterations made to the web site, however. I learned from experience that not all changes recache properly.

  3. snelldl says:

    I’ve used your code – awesome BTW! However I have a question that is slightly OT.

    I am trying to use localStorage for my offline app that I create using a different page in the online site. That is, I want to download data for the offline app using a different page while online. However, none of the data shows up in the the offline version, but I can see it if I go to the url of the page online.

    Any ideas/suggestions?


    • @snelldl

      So If I get this right you’re caching a page that stores data using localStorage. But when you’re offline you can’t access that localStorage data until you go to the page that executes the code to do so? Do you have a demo or something to show whats happening?

  4. snelldl says:

    I got it figured out. Not sure what I was doing wrong.

    However, I do have a different question.

    On the iPhone, when I add my app to the home screen, changes I make to localStorage when I use it don’t get picked up by the online version unless I go in and out of airplane mode at least once. I’ve seen other people with the same problem in various forums.

    Any ideas?

    BTW, I didn’t get your reply in my email.

  5. leo prieto says:

    Dear Ninja, thanks for the how to!

    However I’m having issues with my webapp caching. Thanks to your JS I could discover that the log says “Uncached”, and for some reason my webapp isn’t caching. I’ve double checked everything, even stripping the manifest to the bare essentials, and it still doesn’t seem to work. Any idea why? Thanks.

    Here’s the demo:

  6. Hi.
    At first i have to say you got a great blog and its very useful for everyone. congrats.

    I am facing a big problem using Sencha. there is a lot of JS and CSS. I have put all files in cache.manifest file but still not working. I dont what i can do anymore.

    Have you seen any problem using Sencha Touch?

    Thanks in advance.

  7. mojorisin says:


    I’m having a very strange issue with the mime type setup. I downloaded your example and tested it on my webspace…it works offline…great!

    I’m also having a dedicated webserver. So I started to test the offline feature there. It was not working. I checked the mime.type and the manifest decleration was missing.

    So I added the the line text/cache-manifest manifest to my mime.types file and restarted apache. I double checked with web-sniffer and the output mimetype was correct. However the webapp was still not displaying offline. I ran another test and typed in the URL to the manifest file into my firefox:

    – On my webspace (offline is working) firefox wants me to download the manifest file; The same behavior occurrs when I type in the URL to your manifest file

    – On my webserver (offline not working) firefox just display plain text.

    On both systems web sniffer tells me that the content-type is text/cache-manifest

    What am I doing wrong? Do I have to setup anything else in my apach configuration?

    Thanks in advance

  8. Thanks the tutorial, very helpful – though I am having a problem with my app. I can get it to store offline but the Javascript fails to work?

    Any ideas?

  9. ragini says:

    I ahd dveloped a html page with offline capability uisng the manifest ,it is working fine with firefox but not working on the safari .

  10. Neha says:

    ur app is gr8 but i have a que about web offline. if my html generate dynamic means it not a .html or php file even its a servlet call, where as parameter pass the name of app and server read app name xml files and generated html content and return browers, how can we make these kind of sites webapps offline work? any idea?

  11. Matt Ray says:

    Thank you for putting together a great introductory piece. This was very useful in getting me up and going and I referenced this in my blog post at

  12. Fernando says:

    Hi, thanks for your tutorial, I have a question, how can create a dynamic manifest file, with ASP code, because in my online application the user can active and desactive mode offline and I can’t to erase cached files. I tried with two distinct files, but both files are cached and don’t delete files.


    First Manifest



    Second Manifest



    Also I tried with ASP code in manifest.asp


    But this manifest don’t working.

    Thanks for your help

  13. Thomas says:

    I’m having a hard time adding the Custom Mime type on my server. My website is hosted on Godaddy, IIS 7, Windows.

    There are no Mime Types on the Control Panel. I try creating the MIME with web.config file at the root folder, and all I got was to shut down all my website with an internal message error. I try creating a .htaccess file too but no success… any idea how could I try to fix this???

    • Ryan Seddon says:


      If you can access the IIS manager on your server it’s an easy change. IIS doesn’t use .htaccess so if you want to add a mimetype you can do it through the web.config if the IIS manager is not accessible.

  14. codeviking says:

    A key note.

    I found that iOS (iPhone, notably) appears to not support Apache Last-Modified / If-Modified-Since headers.

    So, for an application cache to work it’s critical that you’re using access based cache expiration headers instead of modification based cache headers.

    Would be curious if you’ve seen the same behavior!



  15. Nice article, Good job.

    Thanks to guide.


  16. Nithin says:

    Very good tutorial. Great job.

  17. PitaJ says:

    WebKit browsers won’t cache it.

    For some reason, Chrome and MobileSafari won’t cache my site.
    So I double checked that the mime-type on it was right and it was.
    Firefox does it fine. When I checked the Chrome Console it said:

    Application Cache Error event: Invalid manifest mime type (text/plain)

    I’ve put it through the web sniffer and it identifies it as text/cache-manifest.

    The cache file is below.

    I can’t figure out what’s wrong. Thanks for your help, PitaJ

    ]# Offline cache v1.0
    ]# html files
    ]# css files
    ]# js files
    ]# images

  18. Iori Fujita says:

    I made a iPhone4 webpage using html5, canvas, audio, javaScript and others. It works well. For a while it wroks off line. But soon it disappears. So I would learn your instructions here to make my site stand alone.
    The page itself is a page for Schoenbrg and Kandinsky.

  19. lmwong says:

    Made the manifest file and added the html code. Loaded it on safari, saved bookmark to home screen so I can run it as a web app, turned on airplane mode, then loaded the bookmark and I get a popup saying: “…could not be opened because it is not connected to the Internet.”

    But it loads fine after I hit “Retry”. Am I not added enough files in the manifest? Anyway of getting rid of this window that pops up? I want it to run just like an app when offline. Thanks.

  20. It could be that 45 of the resources don’t have expiration dates. I usually leave a two year expiration date for the items on my sites. lib2obf_b8.js has a short life as well (once it runs out, it will have to reload)

    I found this using the audit in safari’s developer menu.

  21. I have found the cache initialization is not ‘instant’ on the advices, and usually takes 30 seconds or more before the device realizes that “oh. you want to SAVE this stuff… pfft… here. Baby. there. satisfied?”

    I certainly wish they had just used a web archive system like on the desktop version of safari. So much less complicated.

  22. Jeremiah says:

    Great article, helped a good deal. I have an issue with the status always returning 0. Using several tools to verify my cache.manifest, it returns the correct mime type and has no syntax errors. Trying to troubleshoot the process and there really aren’t a lot of places to set breakpoints. I’m using IIS 7 and the site has the mime type of mime type set correctly to text/cache-manifest. Am I missing something here? I’ve tested the manifest on the latest versions of chrome, safari, and firefox with no success except for being able to determine the 0 zero status.

    • Ryan Seddon says:


      The applicationCache API is very fragile and will break at any given chance it gets. Your error seems like you either have a misspelt asset in your manifest or one that doesn’t exist. Do you have an online version to look at?

  23. Anders says:

    Any idea how to be able to use more than 5MB for?
    Is it per page, per site or per “app” the limit is set?

  24. I don’t know what i’m doing wrong!

    I made the mobile site and used the almost the same code of the DEMO.

    Please, take a look and tell me why images aren’t going to the iphone cache.

    The mobile:


  25. Jorge says:

    I am having lots of problems with this, so if you don’t mind I would like to ask you a few questions. I have checked everything, but my real scenario is complicated, so I have tried to upload your project and it is not working for me:

    The only thing I have changed is the .manifest, because it is the myme termination I added.

    So, my questions would be:

    – Is there something more to check in the server than the myme type?

    – All the files loaded by the initial index must be in the manifest?

    – If I have a video added to the manifest it fails or it just doesn’t cache it?

    – Are you all sure about the 5mb limit?. I have been trying and think that I have seen a dialog asking me to let the app grow the mb limit.

    – Is there any issue related to the app being in fullscreen mode?

    Thank you very much for this great tutorial


    • Ryan Seddon says:


      Your hosted demo is missing a reference to the vomzom.svg file, if any file is missing that’s declared in the manifest it will fail.

      All the files loaded by the initial index must be in the manifest?

      No, just the files you wish to cache so they’re available offline.

      If I have a video added to the manifest it fails or it just doesn’t cache it?

      I believe iOS5 may allow videos and audio to be cached offline, before that it didn’t work.

      Are you all sure about the 5mb limit?. I have been trying and think that I have seen a dialog asking me to let the app grow the mb limit.

      I’m not a 100% sure this is what other people have found to be the case, there may very well be a prompt to increase storage size.

      Is there any issue related to the app being in fullscreen mode?

      Fullscreen mode should be fine, if it’s a UIWebView inside an app offline caching is not available.

  26. Gerry Straathof says:

    The message that comes up when you visit a cached site is to increase the cache storage of your device, not the individual site. I’ve had.mine go up to 50M when doing beta testing. if you installed the Kindle reader you will also get a confirmation.

    The individual site allowance is above 5M on ios5, but i am not sure how much more. the highest mine has gone is 9.75M for a website magazine that was part of a class project.

    I use a php file which builds a manifest file for a site using everything in the folder. it has made it much easier to control changing documents.

    I think the kindle reader and new York times(?) use sql to hold their downloaded data, but have notdirected their codes behaviour with chrome or safari audits…

  27. Jmv says:

    Hi Mr Ninja
    Thanks a lot for all your great work and help!
    I ve installed a launch screen to your demo app on my iPad (ios4.2.1)
    I have the very same problem as many people above: works fine online/offline but if I 1/ go offline 2/suppress the screen in safari , then I get the message ” cannot acess bla bla bla… ” when i restart the webapp, which makes it not very usefull…
    Unless I missed a line, I think you answer has always been up to now: ” i don’t have this problem with my IOS version”. Is it still the case or have you experienced the problem and found a solution? what ios version do you use?
    Thank in advance for you enlightments.

  28. Jmv says:

    Hi Mr Ninja
    (previous post continued)
    I have done a lot of tests with your files on my website ( Thanks to your console-data-logger I have found the following strange facts, please comment if this sounds correct to you:
    – your code makes an offline app from the file ”index.html”, but on the ipad/safari I can access to the offline page only by calling ””, and not ”” that returns an ”no connection” error.
    – then the call back to ”index.html” from ”article.html” never works offline. Putting href=”” doesnt work either because it seems to call ””…
    So I thought I was now ready to make of my own page (a game timer) a webapp. I have done everything as in your code (and copied your console-data-logger with minor modifications), and… ‘:-(( it doesn’t work! The cache apparently downloads the files, but at the end of the process an error is fired. If I put 1 or 2 html files only in the cache the errors fires much faster. I have used the sniffer to verify that my manifest has the correct mime-type: ok. So I have no idea of what is happening… Could you give a look to the problem? Your help would be very very very much appreciated! Thanks in advance!

  29. Brett says:

    This totally kicked ass! Thanks for the great article.

    I just wanted to share that the article.html page links back to

    instead of just

    which causes a “Cannot Open Page” error; no doubt because it thinks that “index.html” is different to “/”. Changing the url back to

    fixes the problem. you might want to correct this in the link itself :)

    thanks again!

    • Ryan Seddon says:


      Thank you! I’ve updated the demo and demo files to add “index.html” to the manifest file so now that link back to “index.html” will work offline correctly.

  30. Jmv says:

    Hi Mr Ninja, it’s me again.
    I have done some more tests I’d like to share with you.
    I have put your code in a new domain name, and everything worked very fine online, offline, all the console message were OK.
    Then I have added a single line in the 2 html files:

    <meta content="yes" name="apple-mobile-web-app-capable">

    This is to make the app open like a stand-alone app, not in SAFARI.
    And then everything went wrong: always an error fired after downloading of the cache.
    So I suppressed the line, changed the cache revision so it updates the files, but it never worked back again… My safari seems to be stuck in a ”problem state” from where it won’t go out. Don’t know what to do…
    Any advice?
    Best regards.

    • Ryan Seddon says:


      I just tested your web app in firefox, chrome and iOS5 safari, they all work offline and the caching works correctly. I can’t see any problems with your code of manifest file (although your manifest file should now have the extension .appcache over .manifest, but that shouldn’t effect anything).

  31. Doug says:

    Thanks for the post. Very helpful. When I read about the Mime type I thought “That’s why it’s not working!” but alas things are still quirky.
    Using an iPad2:
    1. I visite your demo page online
    2. I then go offline and visit the demo page again by entering http://www.thecss... in the navigation bar. I select the demo page from the drop down and the demo webapp comes up fine.
    3. Hit refresh; Safari come up with message. Cannot Open Page: Safari cannot open the page because it is not connected to the internet.
    4. If I enter the whole address in the bar and click ‘Go’ I get the same error message.
    5. If I make a bookmark and click it offline I will get your webpage without the error message.

    My application on the otherhand will only continue to work after being disconnected if I do not navigate away from the page. I can’t bring the page back up with out getting the error message when I have disconnected from the network.

    Is there a setting that will instruct safari to look for a cached website before it tries to connect to the internet? That seems to be the issue.

    Thanks, Doug

  32. Jmv says:

    @Ryan Seddon
    (previous post continued)

    Hi Mr Ninja

    Thank you very much for having looked to my problem.
    So for you everything was ok and for me there was an error.
    I have tried again my own webbapp after not trying for one week and..
    It worked! No error, everything fine! Even as a stand-alone webbapp (with a launch screen and not opening in Safari). Why is it so unpredictable?

    I have found an excellent site that give some explaination of what might be happening here. In short: it has to do with the fact that files send by the web server to safari have themselves some ”lifetime” and are not resend systematically, even if they have changed. Another ”cache” is involved here, the native browser cache, that is different from the ”appCache” we are trying to put in place. So although we change our file on the server side, it may not be changed on the iPad-Safari side, at least for some time, depending on how the lifetime is parametered on the server side! The site gives some advice on how to put some more commands on the .htaccess file to makes sure files are resent each time they are requested on the client side. I haved tried these commands, and also forced the safari cache to empty, but since everything was fine it has not changed anything.

    This excellent site is: , see the chapter named: THE FINE ART OF DEBUGGING, A.K.A. “KILL ME! KILL ME NOW!”

    The file refresh problem I just described might well be the cause of the problems many other users have experienced, driving them nuts! So I hope this post will help them.

    So now i should be happy with my webapp… but not quite. I have implemented some sound effects in an .mp3 file: they work fine online but not offline. Do you have any idea of what might be going on? Maybe caching mp3 is not allowed on ios 4.3? I have found no clear answer on the web.

    Any help on the subject would be highly appreciated


  33. Prodyot says:

    I thank God (if He exists) and or thank CSS Ninja for not closing comments to this post :)
    Awesome tutorial and awesome advances in HTML5.
    Thanks, O’ CSS Ninja.

  34. Mauricio says:

    hello! i’m reading a book called build web apps for iphone with html/javascript and i’m stuck in an error…

    basically, i incremented the offline style for my webapp… i didnt create the htaccess file cause in the book said there was another way…

    in the html tag i used

    and in my manifest.php is like:
    IsFile() &&
    $file != “./manifest.php” &&
    substr($file->getFilename(), 0, 1) != “.”)
    echo $file . “n”;
    $hashes .= md5_file($file);
    echo “# Hash: ” . md5($hashes) . “n”;

    so that way i get all folders dinamically. my problem is:

    everytime when i load the page for the first time, it downloads all the necessary folders files for my application to run offline. and loads the page normally, with all funcionalities working

    but whenever i RELOAD the page, it simply cant get my files.js and .css..
    i’m debugging using safari… and i have no ideia of why is this happening, and in the book dont mention that.

    do you know any possibility of why is that happening???

    (sorry for the bad english, i’m from brazil)

  35. Geddy Straathof says:

    The first thing that comes to mind is not having an expiry date set in your htaccess file for .js and .css prefixes… Without a longer expiry date it will have to reload those.

    Creating the .htaccess file is different than creating the manifest file. They do different things.

  36. Ey men, I am trying to make an html5 app for ipad and needs an offline video. I tryed, studied documentation and googled, but cant find if its posible to have offline html5 video in an ipad!

    All the best,


  37. Geddy Straathof says:

    This would really depend on the size of your video. I would assume the entire website, city video, would be too much for the limitations on whatever device you are viewing it on (usually ten Meg)

  38. Jmv says:

    Hi Mr Ninja

    Now i can access the page again, thanks.
    This post is a follow up of my previous ones (see above). I have 2 news to share concerning cache with IOS.
    1/ I finally upgraded to IOS5: now my webapp works 100% offline, including the sounds, as you had said. So for IOS4.x users: upgrading is a must if you want to make a functionnal webapp with sounds.
    2/ For sounds I have read the same problems in all discussions: IOS will work and cache only 1 sound: if you try to use several files, it gets lost and works an awkward way. Because I need several sounds in my game, I have done as some others: I have concatenated all my sounds in 1 single mp3 file. To use a given sound that start at t0, I use the javascript functions to start the reading at a given time t0 and I stop it after a delay d0 corresponding to the sound duration. This works fine.

    Hope this helps someone.

  39. Diane says:

    Thank you for the information. I was looking for this for a long time

  40. Yoona says:

    What to do if my site is gallery like consisting of a hundred of images and I want it to be accessible offline (so users could browse through my gallery) and on mobile (iPhone). Caching all these images, wouldn’t this be heavy for a mobile? What do you suggest having this kind of setup? or it’s just the way it is and will work fine? Please help me. Thank you.

    • Ryan Seddon says:

      @Yoona –

      You’d probably hit the appcache limit which is 25mb and trying to download 100s of images is a bad idea. I’d probably try and offline cache the thumbnails and then have messaging when users are offline to say higher resolution images are only available when online.

  41. This can be done several ways depending on your goal. For example, you can have the images as separate files, which can take a while to load and store. You can also do what the iOS photo app does, which is create a large file of thumbnails, five or so wide and rather long (maybe not as long as the iPhoto one)

    You can then display only one portion of the image… This may not be recommended for larger images, storing a series in a strip, but you can try it for yourself.

    You can also optimize the images so they are resized appropriately for the device a user is browsing on.

    I have successfully stored twenty five large images (full width/height) but I find it takes longer for the device to juggle things before it allows you to save it for offline reading. (You can’t visit/save… It’s more visit, wait a bit, save)

    If you have a large portfolio with a huge number of images you may want to make several smaller dedicated portfolios.

    Individually I have found images that are less that 160k works well and fewer than twenty or so.

    If you absolutely require a huge number of images, try to remember the patience of the visitor. While you are okay with the longer download for a huge portfolio, I have found others to be less willing to wait.