Service Worker

The Service Worker controller provides useful methods to facilitate interaction with the service worker lifecycle. It enables you to manually update the service worker and notify users when updates are available, particularly useful when the skipWaiting option is set to false.

This component is essential for:

  • Notifying users about available application updates

  • Providing manual update controls

  • Ensuring users get the latest features without forced reloads

  • Creating a smooth update experience without interrupting user workflow

When skipWaiting is enabled, the service worker automatically activates when updated. When disabled, you have more control over when the update is applied, which is useful to avoid interrupting user activities.

Usage

Basic Update Button

<div {{ stimulus_controller('@pwa/service-worker') }}>
    <button
        id="update-btn"
        {{ stimulus_action('@pwa/service-worker', 'update', 'click') }}
        style="display:none;"
    >
        Update Available - Click to Refresh
    </button>
</div>

<script>
    document.addEventListener('pwa--service-worker:update-available', () => {
        document.getElementById('update-btn').style.display = 'block';
    });
</script>

Update Notification Banner

<div {{ stimulus_controller('@pwa/service-worker') }}>
    <div id="update-banner" class="update-notification" style="display:none;">
        <p>A new version of this app is available!</p>
        <button {{ stimulus_action('@pwa/service-worker', 'update', 'click') }}>
            Update Now
        </button>
        <button onclick="document.getElementById('update-banner').style.display='none'">
            Later
        </button>
    </div>
</div>

<script>
    document.addEventListener('pwa--service-worker:update-available', () => {
        document.getElementById('update-banner').style.display = 'block';
    });
</script>

<style>
    .update-notification {
        position: fixed;
        bottom: 20px;
        left: 50%;
        transform: translateX(-50%);
        background: #007bff;
        color: white;
        padding: 15px 20px;
        border-radius: 8px;
        box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        z-index: 1000;
    }

    .update-notification button {
        margin: 5px;
        padding: 8px 15px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    }
</style>

Toast Notification with Countdown

<div {{ stimulus_controller('@pwa/service-worker') }}>
    <div id="update-toast" class="toast" style="display:none;">
        <span id="toast-message">New version available!</span>
        <button {{ stimulus_action('@pwa/service-worker', 'update', 'click') }}>
            Update
        </button>
    </div>
</div>

<script>
    document.addEventListener('pwa--service-worker:update-available', () => {
        const toast = document.getElementById('update-toast');
        const message = document.getElementById('toast-message');

        toast.style.display = 'flex';

        // Optional: Auto-hide after 10 seconds
        let countdown = 10;
        const interval = setInterval(() => {
            countdown--;
            message.textContent = `New version available! Auto-dismissing in ${countdown}s`;

            if (countdown <= 0) {
                clearInterval(interval);
                toast.style.display = 'none';
            }
        }, 1000);
    });
</script>

Integration with User Settings

<div {{ stimulus_controller('@pwa/service-worker') }}>
    <!-- Settings page -->
    <div class="settings">
        <h2>Application Settings</h2>

        <label>
            <input type="checkbox" id="auto-update" checked>
            Automatically install updates
        </label>

        <button id="manual-update-btn" style="display:none;">
            Update Available
        </button>
    </div>
</div>

<script>
    document.addEventListener('pwa--service-worker:update-available', () => {
        const autoUpdate = document.getElementById('auto-update').checked;
        const updateBtn = document.getElementById('manual-update-btn');

        if (autoUpdate) {
            // Auto-update: show brief notification and update
            console.log('Auto-updating to new version...');
            // Trigger update automatically
            document.querySelector('[data-action*="update"]').click();
        } else {
            // Manual update: show button
            updateBtn.style.display = 'block';
            updateBtn.textContent = 'Update Available - Click to Install';
        }
    });
</script>

Parameters

None

Actions

update

Triggers the service worker update process. When called:

  1. The new service worker becomes active

  2. The page automatically refreshes to load the updated version

  3. Users immediately see the latest features and fixes

{{ stimulus_action('@pwa/service-worker', 'update', 'click') }}

Targets

None

Events

pwa--service-worker:update-available

Dispatched when a new version of the service worker is detected and ready to be installed.

No payload

This event is fired only when:

  • A new service worker has finished installing

  • The new service worker is waiting to activate

  • The skipWaiting option is set to false in the service worker configuration

Example usage:

document.addEventListener('pwa--service-worker:update-available', () => {
    // Show update notification
    showUpdateBanner();

    // Log for debugging
    console.log('Service worker update available');

    // Optional: Ask user preference
    if (confirm('New version available. Update now?')) {
        // Trigger update
        document.querySelector('[data-action*="update"]').click();
    }
});

Best Practices

  1. Clear communication: Always inform users that updating will refresh the page

  2. Save user work: If your app has forms or unsaved data, warn users before updating

  3. Provide options: Let users choose to update now or later

  4. Visual feedback: Use prominent but non-intrusive UI elements like banners or toasts

  5. Graceful timing: Consider detecting idle time before prompting for updates

  6. Persistent notification: Keep the update notification visible until user acts on it

Configuration Reference

This component works in conjunction with the service worker configuration. To enable manual updates, ensure skipWaiting is set to false:

/config/packages/pwa.yaml
pwa:
    serviceworker:
        enabled: true
        src: "sw.js"
        skip_waiting: false  # Allows manual control over updates

For more information on service worker configuration, see the Service Worker Configuration page.

Advanced Example: Update Manager

Here's a complete example of an update manager with user preferences and smart update timing:

<div {{ stimulus_controller('@pwa/service-worker') }}>
    <div id="update-manager" style="display:none;">
        <div class="update-card">
            <h3>📦 Update Available</h3>
            <p>A new version of the app is ready to install.</p>

            <div class="update-options">
                <button
                    class="btn-primary"
                    {{ stimulus_action('@pwa/service-worker', 'update', 'click') }}
                >
                    Update Now
                </button>
                <button class="btn-secondary" onclick="snoozeUpdate()">
                    Remind Me in 1 Hour
                </button>
            </div>
        </div>
    </div>
</div>

<script>
    let updateSnoozed = false;

    document.addEventListener('pwa--service-worker:update-available', () => {
        if (updateSnoozed) return;

        // Check if user is idle (no interaction for 30 seconds)
        let idleTime = 0;
        const checkIdle = setInterval(() => {
            idleTime++;
            if (idleTime >= 30) {
                clearInterval(checkIdle);
                showUpdateManager();
            }
        }, 1000);

        // Reset idle time on user interaction
        ['mousedown', 'keypress', 'scroll', 'touchstart'].forEach(event => {
            document.addEventListener(event, () => {
                idleTime = 0;
            }, { once: true });
        });
    });

    function showUpdateManager() {
        document.getElementById('update-manager').style.display = 'block';
    }

    function snoozeUpdate() {
        document.getElementById('update-manager').style.display = 'none';
        updateSnoozed = true;

        // Re-enable after 1 hour
        setTimeout(() => {
            updateSnoozed = false;
            showUpdateManager();
        }, 3600000);
    }
</script>

Last updated

Was this helpful?