How to use Local Storage in Blazor

Introduction

Local storage, a key-value data structure available within modern web browsers, enables applications to store data in the browser's memory. This data persists even after the user closes the browser or navigates away from the page. It serves as a temporary storage solution for non-sensitive static data, reducing server load, and improving performance by minimizing server calls.

Blazor applications can access local storage using JavaScript interop, allowing seamless integration with browser-based storage. In this article, we'll explore how Blazor leverages local storage to enhance user experiences and optimise application performance.

Demo

Here is the demo of the final product, You can see how Blazor utilizes the local storage:

Gif 1: User can Add, Update or Delete data from browser's local storage

What's happening here?

We're developing an application to demonstrate the use of local storage. Our goal is to show how to add, update, and delete data from local storage effectively. To achieve this, I've designed a simple form with three buttons, as shown in demo above. Our application manages a list of programming languages, including fields such as Name, Creator, and Year.

Here's how it works:

  • Adding Data: Users fill out the form with the necessary details and click the "Save" button to add the entry to local storage.
  • Retrieving Records: To fetch records, users provide a key, which is typically the name of the programming language. They then click the "Load" button to retrieve the corresponding data.
  • Deleting Entries: To delete an entry, users simply click the "Delete" button. Local storage removes the record associated with the provided key.
  • Updating Entries: Users can update existing entries by giving the name of the language, later they can edit the Creator or Year fields. If the language name is already present, local storage updates the existing entry Otherwise it creates a new entry.

LocalStorage Service

Let's begin by creating a service to encapsulate local storage functionality. The LocalStorageService class facilitates interaction with the browser's local storage through JavaScript.

using Microsoft.JSInterop;

namespace BlazingLocalStorage.LocalStorage
{
    public class LocalStorageService
    {
        private readonly IJSRuntime _jsRuntime;

        public LocalStorageService(IJSRuntime jsRuntime)
        {
            _jsRuntime = jsRuntime;
        }

        public async Task AddItem(string key, string value)
        {
            await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, value);
        }

        public async Task RemoveItem(string key)
        {
            await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
        }

        public async Task<string> GetItem(string key)
        {
            return await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
        }
    }
}

Listing 1: LocalStorageSerive.cs

Explanation of listing 1: 

  • Constructor: Initializes the LocalStorageService class with an instance of IJSRuntime.
  • AddItem Method: Adds an item to local storage using JavaScript's localStorage.setItem(key, value) function.
  • RemoveItem Method: Removes an item from local storage using JavaScript's localStorage.removeItem(key) function.
  • GetItem Method: Retrieves an item from local storage using JavaScript's localStorage.getItem(key) function.

In order to represent our form fields, let's create a model called ProgrammingLanguage:

    public class ProgrammingLanguage
	{
        public string Name { get; set; }
        public string Creator { get; set; }
        public int Year { get; set; }
    }

Listing 2: ProgrammingLanguage.cs

ProgrammingLanguage Service 

The ProgrammingLanguageService acts as a mediator between the UI and the local storage service. It manages programming languages stored in local storage and provides methods for adding, retrieving, and deleting language entries.

using System.Text.Json;


namespace BlazingLocalStorage.LocalStorage
{
    public class ProgrammingLanguageService
	{
        private readonly LocalStorageService _localStorageService;

        public ProgrammingLanguageService(LocalStorageService localStorageService)
        {
            _localStorageService = localStorageService;
        }

        public async Task AddOrUpdateLanguageAsync(string languageName, ProgrammingLanguage languageToAdd)
        {
            var jsonLanguage = JsonSerializer.Serialize(languageToAdd);
            await _localStorageService.AddItem(languageName, jsonLanguage);
        }

        public async Task DeleteLanguageAsync(string languageName)
        {
            await _localStorageService.RemoveItem(languageName);
        }

        public async Task<ProgrammingLanguage> GetLanguageAsync(string languageName)
        {
            ProgrammingLanguage language = null; ;
            var jsonLanguage = await _localStorageService.GetItem(languageName);
            if (jsonLanguage != null)
            {
                language = JsonSerializer.Deserialize<ProgrammingLanguage>(jsonLanguage);
            }
        
            return language;
        }
    }
}

Listing 3: ProgrammingLanguageService.cs

  • AddOrUpdateLanguageAsync Method: Adds or updates a programming language entry in local storage. It calls the AddItem method of LocalStorageService to add or update a programming language in the local storage. It has to serialize the languageToAdd object into JSON before sending it as parameter to AddItem method.
  • DeleteLanguageAsync Method: It calls the RemoveItem method of LocalStorageService which removes a programming language from the local storage.
  • GetLanguageAsync Method: It calls the GetItem method of  LocalStorageService which retrieves a programming language from the local storage based on the provided languageName. Then we need to deserializes the JSON into a ProgrammingLanguage object.

The component

The UI component, LanguageManagement.razor, enables users to add, edit, and remove programming language entries. It interacts with the ProgrammingLanguageService to perform these operations seamlessly.

@page "/"
@using LocalStorage
@inject ProgrammingLanguageService LanguageService

<div class="row">
    <div class="col-md-4">
        <h3>Add, Edit or Remove Language</h3>
        <div class="form-group">
            <label for="name">Name:</label>
            <input type="text" class="form-control" @bind="Language.Name" id="name" />
        </div>
        <div class="form-group">
            <label for="creator">Creator:</label>
            <input type="text" class="form-control" @bind="Language.Creator" id="creator" />
        </div>
        <div class="form-group">
            <label for="year">Year:</label>
            <input type="text" class="form-control" @bind="Language.Year" id="year" />
        </div>
        <div class="form-group mt-3">
            <button class="btn btn-primary" @onclick="SaveLanguage">Save</button>
            <button class="btn btn-danger" @onclick="DeleteLanguage">Delete</button>
            <button class="btn btn-secondary" @onclick="LoadLanguage">Load</button>
        </div>
    </div>
</div>

@code {
    private ProgrammingLanguage Language = new ProgrammingLanguage();

    private async void SaveLanguage()
    {
        await LanguageService.AddOrUpdateLanguageAsync(Language.Name, new ProgrammingLanguage()
        {
            Name = Language.Name,
            Creator = Language.Creator,
            Year = Language.Year
        });
    }

    private async void DeleteLanguage()
    {
        await LanguageService.DeleteLanguageAsync(Language.Name);
        Language.Name = Language.Creator = string.Empty;
        Language.Year = 0;
    }

    private async void LoadLanguage()
    {
        Language = await LanguageService.GetLanguageAsync(Language.Name);
        StateHasChanged();
    }
}

Listing 4: LanguageManagement.razor

  • Component Routing and Injection: The first three lines of code specify the component's route and the namespace where the ProgrammingLanguageService is located. Additionally, you need to inject the ProgrammingLanguageService into the component.
  • HTML Markup: The HTML markup defines a form with input fields for entering the name, creator, and year of a programming language. It also includes buttons for saving, deleting, and loading languages.
  • @code Block:
    • The Language object holds the data of the programming language being manipulated in the form.
    • The SaveLanguage method is invoked when the user clicks the "Save" button. It calls the AddOrUpdateLanguageAsync method of the ProgrammingLanguageService to add or update the language in the local storage.
    • The DeleteLanguage method is invoked when the user clicks the "Delete" button. It calls the DeleteLanguageAsync method of the ProgrammingLanguageService to remove the language from the local storage.
    • The LoadLanguage method is invoked when the user clicks the "Load" button. It calls the GetLanguageAsync method of the ProgrammingLanguageService to retrieve the language from the local storage and populates the form fields with its data.

The UI looks like image 1 shown below.

Image 1: User clicks on Save button to add data to the local storage

Conclusion

Local storage operates entirely on the client side, reducing the need for frequent server calls to fetch or update data. This makes it an ideal choice for storing user preferences, application settings, theme preferences, language preferences, and display options that need to be accessed across sessions.

Now that we understand what to store in local storage, it's important to be aware of what not to store. Local storage is not a secure storage solution for sensitive or confidential information, such as passwords, credit card details, or personally information.

By leveraging local storage in Blazor applications, you can enhance user experiences. With proper usage, local storage can significantly improve the responsiveness of web applications.


Similar Articles