Skip to content

Add a Live Activity

Checked against c1b2fa4 on 2026-11-10. The files it covers are unchanged since.

A Live Activity (iOS) or ongoing notification (Android) is an activity surface. It runs while the surface exists: render it to start the activity, remove it to end it. examples/timer shows one while the stopwatch is running or paused.

Wrap it in whatever condition means “something is going on”. In the timer, that’s {#if active}:

examples/timer/App.svelte
<div class="activity" data-niso-surface="activity">
<div class="row">
<div>
<p class="title">{running ? 'Stopwatch' : 'Paused'}</p>
{#if running}
<time class="big" data-timer-start={origin}>{clock(elapsed)}</time>
{:else}
<time class="big">{clock(elapsed)}</time>
{/if}
</div>
<div class="actions">
<button onclick={toggle}>{running ? 'Pause' : 'Resume'}</button>
<button onclick={lapOrReset}>{running ? 'Lap' : 'Reset'}</button>
</div>
<p class="count">{laps.length ? 'Lap ' + (laps.length + 1) : ''}</p>
</div>
<progress value={elapsed % 60000} max="60000" data-timer-start={running ? origin : undefined}
data-timer-period={running ? 60000 : undefined}></progress>
</div>

examples/timer/App.svelte, line 115

The <time data-timer-start> counts by itself, so the activity doesn’t need an update every second; the page changes the template only when the user pauses, resumes or laps.

The compact and minimal Dynamic Island are three more surfaces: island.leading, island.trailing and island.minimal. The expanded island shows island.expanded if the page has one, and otherwise the activity surface itself.

examples/timer/App.svelte
<div class="island" data-niso-surface="island.leading"><span class="dot on" class:paused={!running}></span></div>
<div class="island trailing" data-niso-surface="island.trailing">
{#if running}
<time data-timer-start={origin}>{clock(elapsed)}</time>
{:else}
<time>{clock(elapsed)}</time>
{/if}
</div>
<div class="island minimal" data-niso-surface="island.minimal">

examples/timer/App.svelte, line 134

On Android, the activity’s <button>s become the notification’s actions: pressing one clicks the button in the page (niso_dom_activate), so the timer’s Pause and Lap work from the notification. The notification maps the template onto its fixed slots: the first text is the title, a timer is its chronometer, a <progress> its progress bar, and the island.trailing surface’s text the status bar chip. Android 16 promotes it to a Live Update.

On iOS the Live Activity runs no page, so its buttons are left out.

Terminal window
hosts/apple/build.sh timer # iOS: NSSupportsLiveActivities is in Platforms/ios.plist
hosts/android/build.sh timer

Start the stopwatch and lock the screen. On iOS nothing appears if the user has turned Live Activities off for the app: the host checks areActivitiesEnabled first.