Telerik blogs

See how localForage can take localStorage up a notch to deliver a smooth and personalized user experience.

Do you sometimes wish your favorite websites would remember your login details or that your to-do list app would sync when you switch devices? While localStorage, a core browser feature, offers a solution to these issues, it has limitations. localForage takes localStorage to the next level, helping developers build web applications that deliver a smooth and personalized user experience.

While localStorage provides a way for web applications to store data locally on a user’s device, as stated earlier, it still has limitations. For instance, it only stores data as strings, which forces the developer to jump through conversion hoops for complex data structures like objects or arrays. Also, its synchronous operations can cause your application to slow down by blocking the main thread during data access.

Moreover, most browsers limit localStorage to 5 MB per domain (origin). This can be a significant limitation for applications that need to store a large amount of data locally. This is where localForage comes in, offering a more advanced and feature-rich solution for managing local storage.

Regarding storage space, localForage uses IndexedDB, a browser API that offers a more robust storage solution than localStorage. IndexedDB, in theory, offers much larger storage capacities, often exceeding several gigabytes depending on the browser and device. Buckle up as we explore the possibilities of localForage, exploring its capabilities and how it can transform your web applications!

Key Features of localForage

Let’s look at some of the key features of localForage and explore how they help developers create outstanding web applications:

  • Asynchronous API (promises): localForage uses promises to handle asynchronous data operations. This prevents blocking the main thread, leading to smoother user experiences and improved application responsiveness.
  • Support for various data types: localForage seamlessly stores and retrieves objects, arrays, blobs and other native JavaScript objects. This eliminates the need for manual serialization and deserialization, simplifying data management.
  • Storage backend flexibility: localForage intelligently selects the most suitable storage backend based on browser capabilities. It prioritizes IndexedDB due to its higher storage capacities and support for object storage and falls back to WebSQL or localStorage if necessary. This provides optimal performance and compatibility across various browsers.
  • Fallback mechanism: If advanced storage backends such as IndexedDB are not available, localForage handles this smoothly. It effortlessly switches to localStorage, so data remains intact even in older browsers.

Getting Started with localForage

localForage is easy to integrate into your web projects. Here is how to get started:

Installation

  • CDN: Include the localForage library directly in your HTML using a CDN link.
<script src="https://cdn.jsdelivr.net/npm/localforage@1.9.0/dist/localforage.min.js"></script>.
  • Package managers: Install localForage using npm npm install localforage or yarn yarn add localforage. Then, import it into your JavaScript code using:
const localforage = require('localforage');.

Basic Usage

If you are already familiar with the basics of localStorage, then localForage will feel like a comfortable upgrade. localForage offers a similar syntax for setting, getting and removing data using methods like setItem, getItem and removeItem. The key difference lies in the data types and operations.

  • Saving data with setItem: Use the setItem method to store data under a specific key. localForage’s setItem method allows you to persist data locally on a user’s device. This data is stored offline, meaning it’s available even without an internet connection. This method supports a wide range of JavaScript object types for storage, such as strings, numbers, blobs, plain objects, arrays and several typed arrays like Float32Array and Uint8ClampedArray. It’s important to note that when using localStorage and WebSQL backends, binary data will be serialized before being saved and retrieved. This will incur a size increase when saving binary data.
localforage
  .setItem("username", "johndoe")
  .then(() => console.log("Data saved successfully!"))
  .catch((error) => console.error("Error saving data:", error));
  • Retrieving data with getItem: Use the getItem method to retrieve data associated with a key as shown in the example below:
localforage
  .getItem("username")
  .then((username) => console.log("Retrieved username:", username))
  .catch((error) => console.error("Error retrieving data:", error));
  • Removing data with removeItem: Use the removeItem method to delete data stored under a key:
localforage
  .removeItem("username")
  .then(() => console.log("Data removed successfully!"))
  .catch((error) => console.error("Error removing data:", error));

Advanced Usage of localForage

We have explored the fundamentals of localForage and seen how it is a clear improvement over traditional localStorage. However, localForage’s true potential lies beyond the basic functionalities. Imagine a toolbox overflowing with powerful tools just waiting to be used.

This section will be your guide to unlocking these advanced features. We will explore how to easily store and retrieve complex data structures, master asynchronous operations for a smooth user experience, and explore additional functionalities like key management and events.

  • Complex data structures: The process allows storing and retrieving objects and arrays directly, eliminating the need for manual string conversion with JSON.stringify, as you would use with the traditional localStorage API.
const user = { name: "Jane Doe", email: "jane.doe@example.com" };
localforage.setItem("user", user);
  • Asynchronous features with promises: localForage embraces asynchronous operations, a key concept in modern web development. This means data access doesn’t block the main thread, allowing your application to run smoothly and remain responsive. The question is, how do you manage these asynchronous tasks effectively? Promises!
localforage
  .getItem("user")
  .then((user) => {
    console.log("Welcome back,", user.name);
    return localforage.setItem("lastLogin", new Date());
  })
  .then(() => console.log("Login timestamp updated!"))
  .catch((error) => console.error("Error:", error));

In the code snippet above, we can see how to chain multiple asynchronous localForage operations using promises. It retrieves a user object using getItem, then proceeds to welcome the user by their name.

Here’s the twist: It does not stop there. It chains another asynchronous operation using .then to update the user’s last login timestamp with the current date using setItem. Finally, promises provide a catch block to handle any potential errors during these operations.

  • Retrieving stored keys: Sometimes, you might need a complete picture of the data stored in localForage. In such cases, the keys() method is handy. It retrieves a promise that resolves with an array containing all the keys currently in the localStorage.
localforage
  .keys()
  .then((keys) => {
    console.log("Available keys:", keys);
    // Use the retrieved keys for further operations
  })
  .catch((error) => console.error("Error retrieving keys:", error));

The code snippet above shows how to use the keys() method. It retrieves an array of all keys and logs them to the console. You can then use these keys for various purposes, such as iterating through stored data or deleting specific entries.

  • Clearing local storage: There might be scenarios where you need to wipe out the data stored using localForage completely. The clear() method provides a convenient way to do this.
localforage
  .clear()
  .then(() => console.log("Local storage cleared successfully!"))
  .catch((error) => console.error("Error clearing local storage:", error));

This code snippet shows how to use the clear() method. It clears all data stored in localStorage and logs a success message upon completion. Remember to use this method with caution, as it permanently removes all stored data and is irreversible.

  • Listening for events with localForage: localForage allows you to listen for specific events related to data storage and retrieval operations. This can help synchronize data or handle updates in real time. To listen for events, you can use this syntax:
localforage.on("setItem", (data) => {
  console.log("Data set:", data);
});

// Similarly, you can listen for other events

Using these advanced functionalities, you can unlock the full potential of localForage and create robust and efficient web applications that effectively manage local storage.

Practical Applications of localForage

Having explored the advantages of localForage in comparison to traditional localStorage and its basic and advanced usage, let’s dive deeper and experience its true potential by exploring practical applications. Here, we will see how localForage can be used to achieve various functionalities, from caching API responses for improved performance to enabling offline-first experiences and more.

Caching API Responses

You can improve performance and enable offline functionality by caching API responses in local storage. This allows the application to retrieve data without an internet connection, providing a more seamless experience.

fetch("https://api.example.com/data")
  .then((response) => response.json())
  .then((data) => {
    localforage.setItem("apiData", data);
    return data;
  })
  .catch((error) => {
    // Handle API fetch error
    return localforage.getItem("apiData"); // Attempt to retrieve cached data
  })
  .then((cachedData) => {
    if (cachedData) {
      console.log("Using cached data");
      // Use cached data for UI rendering
    } else {
      console.log("No cached data available");
      // Handle the case where no cached data exists (inform user, etc.)
    }
  });

Saving User Preferences and Settings

localForage is ideal for storing user preferences and settings, allowing users to personalize their experience. These customized settings can be retrieved and applied in subsequent visits, enhancing user comfort and familiarity.

const themePreference = localStorage.getItem("theme") || "light"; // Check localStorage fallback
localforage.setItem("theme", themePreference);
document.body.classList.add(themePreference);

const userSettings = {
  language: "en",
  fontSize: "16px",
};
localforage.setItem("userSettings", userSettings);

Building Offline-First Web Applications

With localForage, developers can build web applications that prioritize offline capabilities. By leveraging local storage of essential data, these applications can function well even when an internet connection is unavailable. This enables users to continue interacting with the app and create or update data locally, understanding that all changes will be synchronized with the server upon reconnection.

// Example: Offline to-do list application
const todoList = localforage.getItem("todos") || [];

function addTodo(text) {
  todoList.push({ text, completed: false });
  localforage.setItem("todos", todoList);
  // Update UI with the new todo item
}

function markTodoComplete(index) {
  todoList[index].completed = !todoList[index].completed;
  localforage.setItem("todos", todoList);
  // Update UI to reflect the completed status
}

// Upon regaining connectivity, synchronize local data with the server
function synchronizeTodos() {
  // Send local todo list to server for storage
  localforage.setItem("todos", []); // Clear local list after successful sync
}

Implementing Local State Management

localForage can be used for local state management within single-page applications (SPAs). It allows you to store application state data locally, enabling features like persisting form data and user preferences across page refreshes within the SPA.

// Example: Local state management for a shopping cart in an SPA
const shoppingCart = localforage.getItem("cart") || [];

function addToCart(productId) {
  const existingItem = shoppingCart.find((item) => item.id === productId);
  if (existingItem) {
    existingItem.quantity++; //only increment quantity if product already exists in the localforage
  } else {
    shoppingCart.push({ id: productId, quantity: 1 });
  }
  localforage.setItem("cart", shoppingCart);
  // Update UI to reflect the updated cart
}

function removeFromCart(productId) {
  const itemIndex = shoppingCart.findIndex((item) => item.id === productId);
  if (itemIndex !== -1) {
    shoppingCart.splice(itemIndex, 1);
    localforage.setItem("cart", shoppingCart);
    // Update UI to reflect the removed item
  }
}

Conclusion

It is quite obvious that localForage is a game changer. Thanks to its asynchronous nature, localForage provides more seamless application responsiveness, relieving frustrations from blocked threads. Its ability to store and retrieve complex data structures directly eliminates the need for manual conversion, simplifying data management. Furthermore, its intelligent smart selection and fallback mechanism for the storage backend offers optimal performance and compatibility across various browsers.


About the Author

Christian Nwamba

Chris Nwamba is a Senior Developer Advocate at AWS focusing on AWS Amplify. He is also a teacher with years of experience building products and communities.

Related Posts

Comments

Comments are disabled in preview mode.