SilverLABS.SilverSHELL.PWA 1.0.0

SilverLABS.SilverSHELL.PWA

Progressive Web App (PWA) support for SilverSHELL applications.

Features

  • Offline Support - Work without internet connection
  • Installable - Add to home screen on mobile/desktop
  • Service Worker - Automatic caching and background sync
  • App Manifest - Configure PWA appearance and behavior
  • Install Prompt - Custom installation UI
  • Theme Customization - Brand colors and styling
  • Update Notifications - Alert users of new versions

Installation

dotnet add package SilverLABS.SilverSHELL.PWA

Quick Start

1. Configure PWA Services

using SilverSHELL.PWA.Extensions;

var builder = WebAssemblyHostBuilder.CreateDefault(args);

builder.Services.AddSilverShellPWA(options =>
{
    options.AppName = "My Application";
    options.ShortName = "MyApp";
    options.Description = "My awesome PWA application";
    options.EnableOfflineSupport = true;
    options.ShowInstallPrompt = true;
    options.ThemeColor = "#2196F3";
    options.BackgroundColor = "#ffffff";
});

await builder.Build().RunAsync();

2. Add Service Worker

Create wwwroot/service-worker.js:

self.addEventListener('install', event => {
    console.log('Service worker installing...');
    self.skipWaiting();
});

self.addEventListener('activate', event => {
    console.log('Service worker activating...');
});

self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request)
            .then(response => response || fetch(event.request))
    );
});

3. Register Service Worker

In index.html:

<script>
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/service-worker.js')
            .then(reg => console.log('Service worker registered'))
            .catch(err => console.log('Service worker registration failed'));
    }
</script>

4. Add Web Manifest

Create wwwroot/manifest.json:

{
  "name": "My Application",
  "short_name": "MyApp",
  "description": "My awesome PWA",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "icons": [
    {
      "src": "icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Link in index.html:

<link rel="manifest" href="manifest.json" />
<link rel="apple-touch-icon" href="icon-192.png" />
<meta name="theme-color" content="#2196F3" />

Configuration Options

PWAOptions

public class PWAOptions
{
    /// <summary>
    /// Full application name
    /// </summary>
    public string AppName { get; set; } = "SilverSHELL App";

    /// <summary>
    /// Short name for home screen
    /// </summary>
    public string ShortName { get; set; } = "SilverSHELL";

    /// <summary>
    /// Application description
    /// </summary>
    public string Description { get; set; } = "Progressive Web Application";

    /// <summary>
    /// Enable offline functionality
    /// </summary>
    public bool EnableOfflineSupport { get; set; } = true;

    /// <summary>
    /// Show install prompt to users
    /// </summary>
    public bool ShowInstallPrompt { get; set; } = true;

    /// <summary>
    /// Theme color (hex)
    /// </summary>
    public string ThemeColor { get; set; } = "#2196F3";

    /// <summary>
    /// Background color (hex)
    /// </summary>
    public string BackgroundColor { get; set; } = "#ffffff";
}

Advanced Features

1. Custom Install Prompt

@inject IJSRuntime JS
@inject IOptions<PWAOptions> PWAOptions

@if (showInstallPrompt && PWAOptions.Value.ShowInstallPrompt)
{
    <div class="install-prompt">
        <p>Install @PWAOptions.Value.AppName for a better experience!</p>
        <button @onclick="InstallApp">Install</button>
        <button @onclick="DismissPrompt">Not now</button>
    </div>
}

@code {
    private bool showInstallPrompt = true;

    private async Task InstallApp()
    {
        await JS.InvokeVoidAsync("installPWA");
        showInstallPrompt = false;
    }

    private void DismissPrompt()
    {
        showInstallPrompt = false;
    }
}

2. Offline Detection

@inject IJSRuntime JS

<div class="@(isOnline ? "online" : "offline")">
    @(isOnline ? "✅ Online" : "⚠️ Offline")
</div>

@code {
    private bool isOnline = true;

    protected override async Task OnInitializedAsync()
    {
        isOnline = await JS.InvokeAsync<bool>("navigator.onLine");
    }
}

3. Update Notification

@if (updateAvailable)
{
    <div class="update-notification">
        <p>A new version is available!</p>
        <button @onclick="ReloadApp">Update Now</button>
    </div>
}

@code {
    private bool updateAvailable = false;

    private async Task ReloadApp()
    {
        await JS.InvokeVoidAsync("location.reload", true);
    }
}

Service Worker Strategies

1. Cache-First Strategy

self.addEventListener('fetch', event => {
    event.respondWith(
        caches.match(event.request)
            .then(response => response || fetch(event.request))
    );
});

2. Network-First Strategy

self.addEventListener('fetch', event => {
    event.respondWith(
        fetch(event.request)
            .catch(() => caches.match(event.request))
    );
});

3. Stale-While-Revalidate

self.addEventListener('fetch', event => {
    event.respondWith(
        caches.open('dynamic').then(cache => {
            return fetch(event.request).then(response => {
                cache.put(event.request, response.clone());
                return response;
            }).catch(() => cache.match(event.request));
        })
    );
});

Best Practices

  1. Always test offline - Use Chrome DevTools to simulate offline mode
  2. Cache strategically - Only cache essential resources
  3. Version your cache - Update cache name on new releases
  4. Handle updates gracefully - Notify users of new versions
  5. Provide icons - Include 192x192 and 512x512 PNG icons
  6. Use HTTPS - PWAs require secure connections

Documentation

Support

License

This project is licensed under the MIT License.


Made with ❤️ by SilverLABS

No packages depend on SilverLABS.SilverSHELL.PWA.

Version Downloads Last updated
1.2.2-ci.1020 3 10/26/2025
1.2.1 45 10/26/2025
1.2.1-ci.1016 1 10/26/2025
1.2.1-ci.1012 1 10/26/2025
1.2.1-ci.985 1 10/25/2025
1.2.1-ci.977 2 10/25/2025
1.2.1-ci.935 2 10/24/2025
1.2.1-ci.934 6 10/24/2025
1.2.1-ci.932 3 10/24/2025
1.2.1-ci.931 2 10/24/2025
1.2.1-ci.929 2 10/24/2025
1.2.1-ci.928 2 10/24/2025
1.2.1-ci.927 2 10/24/2025
1.2.1-ci.926 2 10/24/2025
1.2.1-ci.924 2 10/24/2025
1.2.1-ci.922 1 10/24/2025
1.2.1-ci.921 2 10/24/2025
1.2.1-ci.920 2 10/24/2025
1.2.1-ci.916 1 10/24/2025
1.2.1-ci.915 2 10/24/2025
1.2.1-ci.914 1 10/24/2025
1.2.1-ci.912 2 10/24/2025
1.2.1-ci.909 1 10/24/2025
1.2.1-ci.907 2 10/24/2025
1.2.1-ci.901 1 10/24/2025
1.2.1-ci.900 2 10/24/2025
1.2.1-ci.896 1 10/24/2025
1.2.1-ci.895 1 10/24/2025
1.2.1-ci.891 2 10/24/2025
1.2.1-ci.889 2 10/24/2025
1.2.1-ci.887 2 10/24/2025
1.2.1-ci.884 1 10/24/2025
1.2.1-ci.881 1 10/24/2025
1.2.1-ci.880 1 10/24/2025
1.2.1-ci.878 2 10/24/2025
1.2.1-ci.849 217 10/22/2025
1.2.1-ci.662 1 10/20/2025
1.2.1-ci.661 1 10/20/2025
1.2.1-ci.658 2 10/20/2025
1.2.1-ci.657 2 10/20/2025
1.2.1-ci.656 1 10/20/2025
1.2.1-ci.655 1 10/20/2025
1.2.1-ci.654 2 10/20/2025
1.2.1-ci.653 2 10/20/2025
1.2.1-ci.652 3 10/20/2025
1.2.1-ci.651 2 10/20/2025
1.2.1-ci.650 3 10/20/2025
1.2.1-ci.649 2 10/20/2025
1.2.1-ci.648 2 10/20/2025
1.2.1-ci.647 2 10/20/2025
1.2.1-ci.646 3 10/20/2025
1.2.1-ci.645 1 10/20/2025
1.2.1-ci.644 2 10/20/2025
1.2.1-ci.643 2 10/20/2025
1.2.1-ci.641 1 10/20/2025
1.2.0 16 10/19/2025
1.1.1 10 10/06/2025
1.1.0 115 09/25/2025
1.0.0 10 10/06/2025