Using OTA Update UI
Four drop-in components cover the whole update lifecycle: a blocking gate for mandatory updates, a prompt modal for optional ones, an inline download-progress row, and a "restart to update" banner — all reading the reactive useUpdates() state, so there's nothing to wire up beyond defineUpdates().
@sigx/lynx-updates-ui is the drop-in companion to the headless @sigx/lynx-updates package. The components are built from @sigx/lynx-daisyui building blocks (Modal, Progress, Button, Alert), so everything follows your daisy theme.
Quick start
Configure updates once with defineUpdates(), then drop the components into your tree. UpdateGate wraps your app; UpdatePrompt and UpdateReadyBanner live anywhere inside it.
import { component } from '@sigx/lynx';
import { defineUpdates } from '@sigx/lynx-updates';
import { UpdateGate, UpdatePrompt, UpdateReadyBanner } from '@sigx/lynx-updates-ui';
defineUpdates({
provider: { url: 'https://updates.example.com/manifest.json' },
mode: 'manual',
});
const App = component(() => () => (
<UpdateGate description="A required update is being installed.">
{/* your app */}
<Screens />
{/* optional updates: ask, then restart or wait for next launch */}
<UpdatePrompt applyOn="next-launch" />
<UpdateReadyBanner />
</UpdateGate>
));
How the components divide the work
The three coexist without overlap because each handles a different slice of the update state:
<UpdateGate>blocks only for mandatory updates (state.mandatory === true).<UpdatePrompt>and<UpdateReadyBanner>only ever show for non-mandatory updates.<UpdateProgress>is a standalone inline bar you place wherever you want to surface download progress.
A typical optional-update flow
In a 'manual' mode app, the prompt + banner pair gives users full control:
- An optional update becomes available →
<UpdatePrompt>appears. Update downloads it; Later suppresses re-prompts for that update id (persisted). - With
applyOn="next-launch"(the default), the download stages and<UpdateReadyBanner>appears once it's ready. Restart applies immediately (in-place reload); Later keeps the staged update for the next cold launch. - With
applyOn="restart", the prompt applies immediately after download instead.
Dismissals
"Later" on <UpdatePrompt> persists across launches via @sigx/lynx-storage (key __sigx_updates_dismissed:<id>), so the same update is never re-offered. You can read or set this yourself:
import { isDismissed, dismiss } from '@sigx/lynx-updates-ui';
if (!(await isDismissed(manifest.id))) showCustomPrompt();
await dismiss(manifest.id); // suppress future prompts for it
On web preview and in tests (no native Storage module) dismissals degrade to session-only suppression — still correct, just not persisted.
Notes
- Requires
@sigx/lynx-updates(configured viadefineUpdates()) and@sigx/lynx-daisyui(with its styles in your CSS pipeline). @sigx/lynx-storageis installed with this package and linked automatically at prebuild — there's nothing to set up for "Later" persistence.- Every component reads the reactive
useUpdates()state, so they react to the headless package's state machine automatically.
See also
- Components — the full catalog.
- API reference — props, slots, and the dismissal helpers.
- OTA Updates — the headless package these components sit on.
