Local Storage Utility for Blazor WebAssembly App

Here is a local storage utility that relies on dependency injection which means a line of code must be added to the Program.cs file in the Blazor client/WebAssembly app.

Here is one example of using the LocalStorage utility to store an array of strings:

@page "/localstorage"
@inject Utilities.ILocalStorage LocalStorage

<h3>Local Storage</h3>

<div>
@if (data != null)
{
	foreach (var s in data)
	{
	<p>@s</p>
	}
}
</div>

@code {
	string[] data;

	protected override async Task OnInitializedAsync()
	{
		await base.OnInitializedAsync();
		string[] values = new[] { "token", "username", "email", DateTime.Now.ToString() };

		// save array of user data
		await LocalStorage.SaveStringArrayAsync("user", values);

		// retrieve array of user data
		data = await LocalStorage.GetStringArrayAsync("user");

		// delete user data
		await LocalStorage.RemoveAsync("user");
	}
}

Modify the Project's Program.cs File

The Progam.cs file must be modified as such (just one line of code is added).

// Program.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorExample.Client
{
	public class Program
	{
		public static async Task Main(string[] args)
		{
			var builder = WebAssemblyHostBuilder.CreateDefault(args);

			// This AddSingleton line of code is for Uilities.LocalStorage to function - notice placement!
			builder.Services.AddSingleton<Utilities.ILocalStorage, Utilities.LocalStorage>();

			builder.RootComponents.Add<App>("app");

			builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

			await builder.Build().RunAsync();
		}
	}
}

LocalStorage Utility Code

.NET Core 3.1 and Later Compatible (Compresses)

This code allows saving strings and string arrays. It compresses them with GZipStream before storing them which greatly reduces the space taken up in Local Storage. It relies on no libraries other than those which are included with Blazor WebAssembly. It can be used to store objects with .NET 5 and later by using System.Text.Json to serialize them and save the resulting string. See the .NET 5 and later example for one that uses the .NET 5 JSON serializer.


// LocalStorage.cs
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Utilities
{
	public interface ILocalStorage
	{
		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public Task RemoveAsync(string key);

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public Task SaveStringAsync(string key, string value);

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public Task<string> GetStringAsync(string key);

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public Task SaveStringArrayAsync(string key, string[] values);

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public Task<string[]> GetStringArrayAsync(string key);
	}

	public class LocalStorage : ILocalStorage
	{
		private readonly IJSRuntime jsruntime;
		public LocalStorage(IJSRuntime jSRuntime)
		{
			jsruntime = jSRuntime;
		}

		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public async Task RemoveAsync(string key)
		{
			await jsruntime.InvokeVoidAsync("localStorage.removeItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save a string value to browser local storage. Working with strings is more efficient than working with objects.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public async Task SaveStringAsync(string key, string value)
		{
			var compressedBytes = await Compressor.CompressBytesAsync(Encoding.UTF8.GetBytes(value));
			await jsruntime.InvokeVoidAsync("localStorage.setItem", key, Convert.ToBase64String(compressedBytes)).ConfigureAwait(false);
		}

		/// <summary>
		/// Get a string value from browser local storage. Working with strings is more efficient than working with objects.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public async Task<string> GetStringAsync(string key)
		{
			var str = await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
			if (str == null)
				return null;
			var bytes = await Compressor.DecompressBytesAsync(Convert.FromBase64String(str));
			return Encoding.UTF8.GetString(bytes);
		}

		/// <summary>
		/// Save an array of string values to browser local storage. Working with strings is more efficient than working with objects.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public async Task SaveStringArrayAsync(string key, string[] values)
		{
			await SaveStringAsync(key, values == null ? "" : string.Join('\0', values));
		}

		/// <summary>
		/// Get an array of string values from browser local storage. Working with strings is more efficient than working with objects.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public async Task<string[]> GetStringArrayAsync(string key)
		{
			var data = await GetStringAsync(key);
			if (!string.IsNullOrEmpty(data))
				return data.Split('\0');
			return null;
		}
	}

	internal class Compressor
	{
		public static async Task<byte[]> CompressBytesAsync(byte[] bytes)
		{
			using (var outputStream = new MemoryStream())
			{
				using (var compressionStream = new GZipStream(outputStream, CompressionLevel.Optimal))
				{
					await compressionStream.WriteAsync(bytes, 0, bytes.Length);
				}
				return outputStream.ToArray();
			}
		}

		public static async Task<byte[]> DecompressBytesAsync(byte[] bytes)
		{
			using (var inputStream = new MemoryStream(bytes))
			{
				using (var outputStream = new MemoryStream())
				{
					using (var compressionStream = new GZipStream(inputStream, CompressionMode.Decompress))
					{
						await compressionStream.CopyToAsync(outputStream);
					}
					return outputStream.ToArray();
				}
			}
		}
	}
}

.NET 5 and Later Compatible

Here is the code for the LocalStorage utility for .NET 5 and later. It relies on no libraries other than those which are included with Blazor WebAssembly. It does NOT compress the data. For .NET Core 3.1 only, see this code.

// LocalStorage.cs
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Utilities
{
	public interface ILocalStorage
	{
		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public Task RemoveAsync(string key);

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public Task SaveStringAsync(string key, string value);

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public Task<string> GetStringAsync(string key);

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public Task SaveStringArrayAsync(string key, string[] values);

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public Task<string[]> GetStringArrayAsync(string key);

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public Task SaveObjectAsync(string key, object value);

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public Task<T> GetObjectAsync<T>(string key);
	}

	public class LocalStorage : ILocalStorage
	{
		private readonly IJSRuntime jsruntime;
		public LocalStorage(IJSRuntime jSRuntime)
		{
			jsruntime = jSRuntime;
		}

		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public async Task RemoveAsync(string key)
		{
			await jsruntime.InvokeVoidAsync("localStorage.removeItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public async Task SaveStringAsync(string key, string value)
		{
			await jsruntime.InvokeVoidAsync("localStorage.setItem", key, value).ConfigureAwait(false);
		}

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public async Task<string> GetStringAsync(string key)
		{
			return await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public async Task SaveStringArrayAsync(string key, string[] values)
		{
			if (values != null)
				await jsruntime.InvokeVoidAsync("localStorage.setItem", key, string.Join('\0', values)).ConfigureAwait(false);
		}

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public async Task<string[]> GetStringArrayAsync(string key)
		{
			var data = await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
			if (!string.IsNullOrEmpty(data))
				return data.Split('\0');
			return null;
		}

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public async Task SaveObjectAsync(string key, object value)
		{
			string json = System.Text.Json.JsonSerializer.Serialize(value);
			byte[] data = System.Text.Encoding.UTF8.GetBytes(json);
			string b64 = System.Convert.ToBase64String(data);
			await SaveStringAsync(key, b64);
		}

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public async Task<T> GetObjectAsync<T>(string key)
		{
			string b64 = await GetStringAsync(key);
			if (b64 == null)
				return null;
			byte[] data = System.Convert.FromBase64String(b64);
			string json = System.Text.Encoding.UTF8.GetString(data);
			return System.Text.Json.JsonSerializer.Deserialize<T>(json);
		}
	}
}

Here is an example of using this LocalStorage utility to store an object:

@page "/localstorage"
@inject Utilities.ILocalStorage LocalStorage
<h3>Local Storage Example</h3>

<div>
    @if (data != null)
    {
        <p>@data.username</p>
        <p>@data.email</p>
        <p>@data.jwt</p>
        <p>@data.created</p>
    }
</div>

@code {
    User data;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        User value = new User { username = "username",
                                email = "email@gmail.com",
                                jwt = "fhasu9f3whfi8fdakid",
                                created = DateTime.Now };

        await LocalStorage.SaveObjectAsync("user", value);

        data = await LocalStorage.GetObjectAsync<User>("user");

        await LocalStorage.RemoveAsync("user");
    }
}
// User.cs
using System;

namespace BlazorExample.Shared
{
    [Serializable]
    public class User
    {
        public string username;
        public string email;
        public string jwt;
        public DateTime created;
    }
}

ASP.NET Core 3.1 Only Compatible Version

This is the earlier version of this utility which uses the now deprecated BinaryFormatter to serialize objects. It has been deprecated since .NET 5. Apparently, it was a security risk at least in .NET 5. It does NOT compress data as the first example above does.

// LocalStorage.cs
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Utilities
{
	public interface ILocalStorage
	{
		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public Task RemoveAsync(string key);

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public Task SaveStringAsync(string key, string value);

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public Task<string> GetStringAsync(string key);

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public Task SaveStringArrayAsync(string key, string[] values);

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public Task<string[]> GetStringArrayAsync(string key);

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public Task SaveObjectAsync(string key, object value);

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public Task<T> GetObjectAsync<T>(string key);
	}

	public class LocalStorage : ILocalStorage
	{
		private readonly IJSRuntime jsruntime;
		public LocalStorage(IJSRuntime jSRuntime)
		{
			jsruntime = jSRuntime;
		}

		/// <summary>
		/// Remove a key from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		public async Task RemoveAsync(string key)
		{
			await jsruntime.InvokeVoidAsync("localStorage.removeItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save a string value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The string value to save to local storage.</param>
		public async Task SaveStringAsync(string key, string value)
		{
			await jsruntime.InvokeVoidAsync("localStorage.setItem", key, value).ConfigureAwait(false);
		}

		/// <summary>
		/// Get a string value from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The string previously saved to local storage.</returns>
		public async Task<string> GetStringAsync(string key)
		{
			return await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
		}

		/// <summary>
		/// Save an array of string values to browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <param name="values">The array of string values to save to local storage.</param>
		public async Task SaveStringArrayAsync(string key, string[] values)
		{
			if (values != null)
				await jsruntime.InvokeVoidAsync("localStorage.setItem", key, string.Join('\0', values)).ConfigureAwait(false);
		}

		/// <summary>
		/// Get an array of string values from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The array of string values previously saved to local storage.</returns>
		public async Task<string[]> GetStringArrayAsync(string key)
		{
			var data = await jsruntime.InvokeAsync<string>("localStorage.getItem", key).ConfigureAwait(false);
			if (!string.IsNullOrEmpty(data))
				return data.Split('\0');
			return null;
		}

		/// <summary>
		/// Save an object value to browser local storage.
		/// </summary>
		/// <param name="key">The key to use to save to and retrieve from local storage.</param>
		/// <param name="value">The object to save to local storage.</param>
		public async Task SaveObjectAsync(string key, object value)
		{
			string str = string.Empty;
			var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
			using (var ms = new System.IO.MemoryStream())
			{
				bf.Serialize(ms, value);
				var bytes = ms.ToArray();
				foreach (var b in bytes)
					str += b.ToString("X2");
			}
			await SaveStringAsync(key, str);
		}

		/// <summary>
		/// Get an object from browser local storage.
		/// </summary>
		/// <param name="key">The key previously used to save to local storage.</param>
		/// <returns>The object previously saved to local storage.</returns>
		public async Task<T> GetObjectAsync<T>(string key)
		{
			try
			{
				string str = await GetStringAsync(key);
				if (str == null)
					return null;
				var bytes = new byte[str.Length / 2];
				for(int i = 0; i < bytes.Length; i++)
				{
					string val = str.Substring(i * 2, 2);
					bytes[i] = byte.Parse(val, System.Globalization.NumberStyles.HexNumber);
				}
				using (var ms = new System.IO.MemoryStream(bytes))
				{
					var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
					ms.Position = 0;
					return (T)bf.Deserialize(ms);
				}
			}
			catch (System.Exception ex) { throw ex; }
		}
	}
}

Coding Video

https://youtu.be/iYjpv9yCelo


Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site.
OK