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!
Let’s look at some of the key features of localForage and explore how they help developers create outstanding web applications:
localForage is easy to integrate into your web projects. Here is how to get started:
<script src="https://cdn.jsdelivr.net/npm/localforage@1.9.0/dist/localforage.min.js"></script>.
npm install localforage
or yarn yarn add localforage
. Then, import it into your JavaScript code using:const localforage = require('localforage');.
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.
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));
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));
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));
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.
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);
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.
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.
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.
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.
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.
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.)
}
});
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);
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
}
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
}
}
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.
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.