Skip to main content

Goal

Show a New Year experience across your app that feels delightful and intentional:
  • Inline surfaces (home header banner, settings header, etc.)
  • Popup surface (optional fullscreen greeting)
  • Multiple variants (snowy banner vs. confetti card vs. fullscreen)
  • Scheduling & limits (time window, max impressions, cooldown)
This recipe focuses on business logic + guards + providers. UI snippets are minimal and only there to show how variants are rendered.

1) Define surfaces + variants


2) Model payload / option / item

Your payload is pure domain data. Options define where/how it can show.

3) Provider: build candidates (no UI involved)

In real life, fetch from Remote Config / API. Here we keep it “business logic only”.

4) Guards: time window + impression limits + deterministic ordering

4.1 A tiny “sync” guard (removes stale items everywhere)

This is the key to “declarative removal”: if a provider stops emitting a candidate, it disappears from slots + queues automatically.

4.2 Eligibility guard (time + cooldown + max impressions + dismissed)


5) Rendering: one outlet, many variants

Use a single PresentumOutlet per surface and branch by variant. (Your UI can be as fancy as you want; Presentum only cares about state.)
If you also want the fullscreen greeting, pair this with the popup host pattern in Popup hosts and make AppSurface.popup the watched surface.

What this enables

  • “Seasonal experiences” as data: add/remove campaigns without rewriting UI
  • Variants without branching app logic: only the outlet branches by variant
  • Fully declarative removal: stop emitting candidates ⇒ the UI disappears everywhere