OptionalaboveOptionalclassOptionalemptyOptionalemptyOptionalemptyEmpty-state copy. Defaults are generic ("You're all caught up"), which is wrong for a host that can be empty for more than one reason — an unread filter with nothing unread says something different from a feed with no notifications at all. emptyAction renders below the copy.
Which bucket to show. The feed loads it on mount and on every change.
OptionalinfinitePull further pages as the bottom comes into view. Off by default: a compact popover shows a fixed slice, a full list scrolls through everything.
OptionalitemRender at most this many rows (a popover's "last ~20"). Omit for no cap.
OptionalmaxCap the scroll viewport to this many rows — the rest scroll. Measured, since
rows vary in height (1–2 line detail plus an optional expiry line). Omit to
let the list fill its host. "rows" only: the measurement reads a flat
<ul>, which the grouped card variant doesn't have.
Run a notification's action. The feed does NOT mark read for you — the host
decides, because "opening" means different things per app (the portal
deep-links to its notifications page; tracking opens an export link or the
session-RPE sheet). Fired by a row click in the "rows" variant, and by the
row's CTA pill in "cards" (where a click expands instead).
OptionalonA "cards" row was expanded (collapsed → expanded only). Lets the host
treat opening a detail as reading it. Ignored by the "rows" variant.
OptionalonOptionalselectableSelection mode: rows carry a selection indicator and a tap picks the row rather than opening it. The host owns the set and the bulk actions — the feed only renders the state.
OptionalselectedOptionalsuppressHide the "you're all caught up" state even when the list is empty — for a host whose aboveList means the surface isn't actually empty.
OptionalvariantNotificationFeedVariant — defaults to "rows".
Content pinned above the list (the portal's live roster-upload cards).