Skip to content
View lessons Login

Launch sale! Get 30% OFF expires in 126:46:22

3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Storing Data with Ionic Storage

Storing Data with Ionic Storage

Let’s say you’ve created an Ionic application where users can create shopping lists. A user downloads your application, spends 5 minutes adding their list and then closes the application… and all their data is gone.

Quite often when creating mobile applications you will want to store data that the user can retrieve later. A lot of the time this is done by storing the data somewhere that can be accessed through a remote server (think Facebook, Twitter, etc.) which would require an Internet connection and fetching the data remotely (which we will discuss in the next lesson). In many cases though, we may also want to store some data locally on the device itself.

There are a few reasons we might want to store some data locally:

  • The application is completely self-contained and there is no interaction with other users, so all data can be stored locally
  • We need to store data locally for some specific functionality like remembering logged in users
  • We could skip unnecessary calls to a server by storing preference settings locally
  • We could skip unnecessary calls to a server by caching other data locally
  • We want to sync online and offline data so that the user can continue using the application even when they are offline (Evernote is a good example of this)

Ionic applications run in a browser, so we don’t have access to the storage options that native applications do. We do still have access to the usual browser storage options that websites have access to though like Local Storage and IndexedDB. These options might not always be ideal (for reasons I will cover shortly), but we can also access native data storage by using Capacitor which can overcome the shortfalls of browser-based data storage. However, native storage options are only available when your app is built natively for iOS and Android, if you are building a PWA then you will only be able to rely on browser storage.

There are plenty of different options out there for storing data locally, but we are going to focus on the main ones when it comes to Ionic applications.

Local Storage

This is the most basic storage option available, which allows you to store up to 10MB worth of data in the user’s browser. Remember, Ionic applications technically run inside of an embedded browser even when they are built as iOS or Android applications.

Local storage gets a bit of a bad rap and is generally considered to be unreliable. I think the browser’s local storage can be a viable option and it is reasonably stable and reliable, but, it is possible for the data to be wiped, which means for a lot of applications it’s not going to be a great option. Even if it worked 99% of the time, that’s still not good enough for storing most types of data.

In general, you should only use it where data loss would not be an issue, and it shouldn’t ever be used to store sensitive data (like a password). This doesn’t mean that local storage is insecure - you could for example store an authentication token like a JWT (JSON Web Token) in local storage which is a common practice. It is not easy for an attacker to steal data from local storage, but, if your application did become vulnerable to something like an XSS attack data store in local storage could potentially be stolen, so this needs to be taken into consideration. My general stance is that nobody except experts should be handling authentication/security issues like this anyway - if you need authentication/authorisation in your application you should use some kind of authentication service that handles it for you.

A JWT is a good example of the type of thing that suits being stored in local storage. This would allow you to tell if a user was already logged in or not, but if the data is lost it’s not really a big deal because the user will just need to enter in their username and password again to re-authenticate.

If you are just using local storage to cache data from a server it would also not be a big issue if the data is lost since it can just be fetched from the server again. Local storage is also great to use when we are just learning and building practice applications or prototypes because we don’t really care if the data gets wiped.

If you were storing all of a user’s notes in local storage and local storage gets wiped… not so good.

Local Storage is a simple key-value system, and can be accessed through the globally available localStorage object:

localStorage.setItem('someSetting', 'off');
const someSetting = localStorage.getItem('someSetting');

This is the native (as in native to web browsers, not iOS or Android native) way to set and retrieve local storage data. We won’t be accessing local storage this way in Ionic applications.

Ionic Storage

Fortunately, for most storage requirements we don’t really need to worry about the implementation details. Ionic provides its own storage service that allows us to define which storage mechanism we want to use. It provides us with a consistent API to use no matter which storage mechanism is being used underneath. We are going to look at the basic usage first, but as you will see in a moment we can also extend Ionic Storage to use SQLite as the storage mechanism.

To use it, all you have to do is install it:

npm install @ionic/storage-angular
Do **NOT** add the following code block to your project

You may come across some libraries that recommend setting up a library/package by adding the module for that package to your imports, like this:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import * as CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
import { Drivers } from '@ionic/storage';
import { IonicStorageModule } from '@ionic/storage-angular';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
IonicModule.forRoot(),
IonicStorageModule.forRoot({
// eslint-disable-next-line no-underscore-dangle
driverOrder: [CordovaSQLiteDriver._driver, Drivers.IndexedDB],
}),
AppRoutingModule,
],
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}

However, with the standalone approach this is not how we set this up (it is if you are using @NgModule instead of standalone).

For situations like this, where the package may not provide a standalone default, we can use the importProvidersFrom function like this:

import { bootstrapApplication } from '@angular/platform-browser';
import {
RouteReuseStrategy,
provideRouter,
withPreloading,
PreloadAllModules,
} from '@angular/router';
import {
IonicRouteStrategy,
provideIonicAngular,
} from '@ionic/angular/standalone';
import CordovaSQLiteDriver from 'localforage-cordovasqlitedriver';
import { Drivers } from '@ionic/storage';
import { IonicStorageModule } from '@ionic/storage-angular';
import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';
import { importProvidersFrom } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
provideIonicAngular(),
provideRouter(routes, withPreloading(PreloadAllModules)),
importProvidersFrom(
IonicStorageModule.forRoot({
driverOrder: [CordovaSQLiteDriver._driver, Drivers.IndexedDB, Drivers.LocalStorage],
}),
),
],
});

Then, using the Storage service will look something like this:

import { inject, Injectable, resource, ResourceRef } from '@angular/core';
import { Storage } from '@ionic/storage-angular';
type Checklist = {};
@Injectable({
providedIn: 'root',
})
export class StorageService {
private ionicStorage = inject(Storage);
private storage = resource({
loader: () => this.ionicStorage.create(),
});
loadedChecklists: ResourceRef<Checklist[]> = resource({
params: () => this.storage.value(),
loader: ({ params }) => params.get('checklists') ?? [],
});
saveChecklists(checklists: Checklist[]) {
const storage = this.storage.value();
if (storage) {
storage.set('checklists', checklists);
}
}
}
STANDARD STANDARD

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).