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
- NuGet Package Manager Console:
NuGet\Install-Package Blazored.LocalStorage -Version 4.4.0
- 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();
💡 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");
}
}
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:
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; }
}
}
💡 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:
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
}
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");
I guess this is pretty much self explanatory, the output now will change to this:
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!
....