DEV Community

Cover image for How To Work With The Browser's Local Storage in Blazor
Rasheed K Mozaffar
Rasheed K Mozaffar

Posted on

How To Work With The Browser's Local Storage in Blazor

Introduction 👋🏻

If you've used the browser's dev tools, you have probably seen the Storage tab, this is a storage mechanism that allows websites to store data in the browser's storage. There're different types of storages in the browser, those types are: Cookies Storage, Session Storage and Local Storage, we're interested in the last one.

What's The Local Storage? 💽

The local storage can store large amounts of data, and the data it can store varies, from user preferences, language used in the website, theme, and more complex things such as persisting form data, which means the data will be cached and can be retrieved if the application was closed unintentionally, or maybe the user closed the browser by accident, now that's a different topic, called State Management, so for the time being, we'll focus on how we can interact with the local storage from a Blazor Application.

How is Data Stored in Local Storage? ⚙️

Data is stored as key-value pairs, the data is saved in the browser and is specific to the website that created it, so if a website stored for instance, a language code in the local storage, and you navigated to another website, that language code will not be there anymore until you navigate back to the original application that had created it.

Working With The Local Storage From Blazor

When Blazor was immature and young, the task of working with the local storage required developers to write JavaScript to interact with the browser's local storage. However, Blazor is more grown right now, and thanks to community developed packages, we can now reach the local storage with only C# code.

Setting up The Project 🧠

If you want to follow along, then go through the following steps:
1: Create An Empty Blazor WebAssembly Project

💡 NOTE: The same code works for Blazor Server as well, so if you want to go with that project type, it's also fine.

2: Download the package Blazored.LocalStorage.
To do that, use on of the following methods:

  • .NET CLI Command:



dotnet add package Blazored.LocalStorage --version 4.4.0



Enter fullscreen mode Exit fullscreen mode
  • NuGet Package Manager Console:



NuGet\Install-Package Blazored.LocalStorage -Version 4.4.0



Enter fullscreen mode Exit fullscreen mode
  • NuGet Package Manager: You can use NPM's GUI if you're using Vs for Windows or Vs for Mac

3: Register the service in DI container.
We want to inject instances of the ILocalStorageServices into the components where we want to access the local storage, so inside Program.cs, add this line:




builder.Services.AddBlazoredLocalStorage();



Enter fullscreen mode Exit fullscreen mode

💡 NOTE: Make sure to import Blazored.LocalStorage at the top of Program.cs so you can access the method to register the service.

And we're all done for the set up, now we can do some operations with the local storage from our Blazor Application

Adding an Item To The Local Storage

Inside Index.razor, we'll override the virtual method OnInitializedAsync to add a string message inside the local storage:




@page "/"
@inject ILocalStorageService LocalStorage

<h1>@message</h1>

@code {
    private string message = "";
    protected override async Task OnInitializedAsync()
    {
        await LocalStorage.SetItemAsStringAsync("message", "Hello World!");
        message = await LocalStorage.GetItemAsStringAsync("message");
    }
}



Enter fullscreen mode Exit fullscreen mode

Let's walk through the code now.
Firstly, I'm injecting an instance of ILocalStorageService into the component, I'm calling it LocalStorage, this instance will provide us with methods to set items and retrieve data from the local storage.

Secondly, inside the code directory, I'm creating a variable called message of type string, the reason for this is because I want the string to be fetched from the local storage, and set to the variable message therefore I can display it in an h1.

Thirdly, inside OnInitializedAsync, I'm calling the method SetItemAsStringAsync, this method is very intuitive, it takes two arguments, the key name, and the value, in that context, we're storing a string value, that has a key named message.

Lastly, I'm reaching out to the local storage to retrieve the message value by using GetItemAsStringAsync, this method takes a single argument, that is the name of the key whose value is what we want to retrieve.

If you run the application now, and navigate to the storage tab, click on Local Storage, you should see something similar to this:

Preview of local storage

Storing Complex Objects 🥸

So far we've worked with setting and getting merely string items, which is great, but we can do more than that! Say you want to store a whole object for example, you might want to use SetItemAsync<T>, this method is generic, and T is the type of object you want to store. Here's a code example to help you better understand this idea:




@code {
    private Person bob = new()
    {
        FirstName = "Bob",
        LastName = "Smith"
    };
    protected override async Task OnInitializedAsync()
    {
        await LocalStorage.SetItemAsync<Person>("bob", bob);
    }

    private class Person
    {
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
    }
}



Enter fullscreen mode Exit fullscreen mode

💡 NOTE: When you set an object in the local storage, it gets serialized into a string using JSON format, and when you want to retrieve it, it gets the string which is then deserialized into a C# object.

The result should look like this:

Storing objects in the local storage

If you want to get the object, you can call GetItemAsync<T>, where again T is the type to deserialize the object into, in the case of our previous example, it's Person.

Checking If A Key Exists In The Local Storage

In many cases, you want to check if there's already an existing entry in the local storage, I'll give you a real world example:
If you are using Token Based Authentication in your project, then there'll be a Json Web Token which will be used to hold the claims of the user, and that token should be sent along with every request the user makes inside the application to show that they're authorized, the token should be stored in the local storage, so what you can do, you can check if it exists there, if it does, you get it and read its claims to create the user's identity and so on, this way you know that the user is authenticated, so how do you check if the local storage contains a particular item?

Using a method called ContainsKeyAsync, this method takes a key name as a parameter, a string, and it checks if the local storage does in fact contain that key or not, if so, returns true, and false otherwise.

We'll use our friend Bob from the previous example to see this method in action.
When the index page gets initialized, I want to check if Bob is there or not, and display a message based on that:




<h1>@bobState</h1>

@code {
    private string bobState = "";
    private Person bob = new()
    {
        FirstName = "Bob",
        LastName = "Smith"
    };
    protected override async Task OnInitializedAsync()
    {
        await LocalStorage.SetItemAsync<Person>("bob", bob);

        if (await LocalStorage.ContainKeyAsync("bob"))
        {
            bobState = "Bob is HERE!";
        }
        else
        {
            bobState = "Bob is no where to be found :-(";
        }
    }
    // removed for brevity
}



Enter fullscreen mode Exit fullscreen mode

Since we're first adding Bob to the local storage every time the page is initialized, we should see Bob is HERE! printed on the page. However, if we removed Bob before doing the check, we should see a different message, do this by adding the following line after the line that adds Bob to the local storage:




 await LocalStorage.RemoveItemAsync("bob");



Enter fullscreen mode Exit fullscreen mode

I guess this is pretty much self explanatory, the output now will change to this:

Removing Bob from the local storage

Practice Project 🏗️

Perhaps a nice practice can be theming, try to implement a way the user can change the color of the background, and make it a persisted change that even if the project was stopped, the color will remain there in the storage so when the app is reopened, the background color gets set to the color the user chose before closing the application.

Conclusion ✅

In this post, we discovered how we can interact with the browser's local storage by using a third party package, we've looked at how to setup the service and access it from every component where it's needed, and we've fiddled around with the main methods it provides, we wrote, read, and also removed data from the local storage all by writing pure C# code, I highly encourage you to dive deeper and play around with the package to see what else you can do. Good Luck!

....

Top comments (2)

Collapse
 
keshish profile image
Garo Keshishian

I am trying to understand how I can keep my application working offline, allowing users to update, delete and add data. and then synchronize the data back to the server once the connection is restored.
what do you think about the limitations of using Local Storage, is it going to be feasible to work with complex objects?

Collapse
 
rasheedmozaffar profile image
Rasheed K Mozaffar

If your application needs to store complex data structures and objects, local storage might work just fine, because it stores data in key value pairs, and is scoped for all tabs from the same origin. However, you got to keep in mind that local storage is primarily used for storing simple data such as user preference, language codes or session info (jwt tokens or cookies). Another key limitation to understand is that local storage has a size of 5-10mbs per origin, that's the typical size for it.

I reckon for your use case, you might want to check the browser's indexed db, it's got the same capabilities that local storage has (scoped for all tabs from the same origin, persists even if the browser is closed and re opened). The indexed db has a large size suitable for storing a lot of data, that's not necessarily simple like basic key value string pairs. Plus, it's size is typically from a few hundred mbgs to like 1gb, so you can rest assured you won't run out of space.

I hope that answers your question!