| name | shiny-stores |
| description | Generate and configure Shiny Stores for .NET - cross-platform key/value stores with source-generated property binding for mobile, desktop, and Blazor WebAssembly |
| auto_invoke | true |
| triggers | ["IKeyValueStore","AddShinyStores","AddShinyWebAssemblyStores","StoreKeys","Shiny.Stores","BindAttribute","Shiny.Extensions.Stores","Shiny.Extensions.Stores.Web"] |
Shiny Stores Skill
You are an expert in Shiny Extensions Stores, a .NET library providing cross-platform key/value store abstraction with source-generated property binding.
When to Use This Skill
Invoke this skill when the user wants to:
- Use cross-platform key/value stores (settings, secure storage, memory)
- Persist properties to a backing store using the
[Bind] source generator
- Use key/value stores in Blazor WebAssembly (localStorage)
- Create custom key/value store implementations
Library Overview
Documentation: https://shinylib.net/extensions/stores/
Repository: https://github.com/shinyorg/Shiny.Extensions
Packages: Shiny.Extensions.Stores, Shiny.Extensions.Stores.Web
Built-in Store Keys
Stores are registered as keyed singletons in DI using StoreKeys constants:
| Key | Platform | Implementation |
|---|
StoreKeys.Default ("settings") | Android | SharedPreferences |
StoreKeys.Default ("settings") | iOS/macOS | NSUserDefaults |
StoreKeys.Default ("settings") | Windows | ApplicationData.LocalSettings |
StoreKeys.Default ("settings") | Blazor | localStorage |
StoreKeys.Secure ("secure") | Android | EncryptedSharedPreferences |
StoreKeys.Secure ("secure") | iOS/macOS | Keychain |
Setup
services.AddShinyStores();
services.AddShinyWebAssemblyStores();
On mobile/desktop you do not need a post-build UseShinyStores() call.
Shiny.Stores.Default / Shiny.Stores.Secure are self-bootstrapping: on first
access they lazily create the platform-native store (SharedPreferences /
NSUserDefaults / Keychain / DPAPI / MemoryKeyValueStore). AddShinyStores()
just registers those same instances into DI so keyed IKeyValueStore injections
share them.
For Blazor (where the store needs IJSRuntime from the built provider) call
host.Services.UseShinyStores() after host.Build() to snapshot the keyed
IKeyValueStore registrations into the static accessor.
Static Shiny.Stores Accessor
The simplest way to read/write — self-bootstraps on first access. No
initialization required for mobile/desktop.
Shiny.Stores.Default.Set("theme", "dark");
var theme = Shiny.Stores.Default.Get<string>("theme");
Shiny.Stores.Secure.Set("token", "abc123");
Shiny.Stores.Keyed("my-store").Set("k", "v");
Overrides / Tests / Custom Keys
Shiny.Stores.Register(StoreKeys.Default, new MemoryKeyValueStore());
Shiny.Stores.Register("redis", new RedisKeyValueStore(...));
serviceProvider.UseShinyStores();
Shiny.Stores.Initialize(serviceProvider);
Shiny.Stores.Reset();
DI-Style Access
public class SettingsService(
[FromKeyedServices(StoreKeys.Default)] IKeyValueStore settings,
[FromKeyedServices(StoreKeys.Secure)] IKeyValueStore secure
)
{
public void SaveTheme(string theme) => settings.Set("theme", theme);
public string GetTheme() => settings.Get<string>("theme") ?? "light";
}
Source-Generated [Bind] Properties
The DI source generator (from Shiny.Extensions.DependencyInjection) recognizes [Bind] on partial properties and emits getter/setter bodies that round-trip through the static Shiny.Stores accessor.
using Shiny;
[Singleton]
public partial class AppSettings
{
[Bind]
public partial string Theme { get; set; }
[Bind("secure")]
public partial string Token { get; set; }
[Bind(Key = "ui_density")]
public partial int Density { get; set; }
}
No INotifyPropertyChanged, no runtime reflection. Generated property bodies call Shiny.Stores.Default/Secure/Keyed(...).Get<T>(...) and .Set(...).
Store Extension Methods
store.Get<T>(key, defaultValue);
store.GetRequired<T>(key);
store.SetOrRemove(key, value);
store.SetDefault<T>(key, value);
store.IncrementValue(key);
Code Generation Instructions
- Use
AddShinyStores() for mobile/desktop, AddShinyWebAssemblyStores() for Blazor
- For persistent settings, prefer
[Singleton] + [Bind] partial properties over manual Set/Get calls
- The class with
[Bind] properties must be partial; properties must also be partial
- For sensitive data (tokens, credentials), pass
"secure" to [Bind("secure")]
- Use
Shiny.Stores.Default/Secure/Keyed(...) for direct ad-hoc access
Best Practices
- Use
[Bind] for settings classes — eliminates boilerplate, no INPC needed, AOT-clean
- Target the secure store — always use
[Bind("secure")] for sensitive values
- Mobile/desktop needs no post-build call —
Shiny.Stores self-bootstraps on first access. UseShinyStores() is only needed for Blazor (because IJSRuntime requires the built provider) or when you've registered custom keyed IKeyValueStores in DI that you want snapshotted into the static accessor
- Use
Stores.Register in tests — pair with Stores.Reset() between tests to swap in MemoryKeyValueStore or any custom double