Skip to content

Feast Contextual Nudge Braze Banners#16254

Open
andresilva-guardian wants to merge 12 commits into
mainfrom
afs/feast-contextual-nudge-braze-banners
Open

Feast Contextual Nudge Braze Banners#16254
andresilva-guardian wants to merge 12 commits into
mainfrom
afs/feast-contextual-nudge-braze-banners

Conversation

@andresilva-guardian

@andresilva-guardian andresilva-guardian commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What does this change?

We integrate Braze Banners into the FeastContextualNudge component, so that when a user is signed in and Braze has a banner ready for the corresponding placement slot, we render the Braze banner in place of the native static nudge.

Concretely, we:

  • Add five new BrazeBannersSystemPlacementId entries (FeastContextualNudge1FeastContextualNudge5) so we can address up to five distinct nudge positions per article.
  • Update ArticleRenderer to distribute up to five placement slots evenly across all recipe sections. We calculate an interval = ceil(sections.length / 5) and assign a nudgeIndex (1–5) to every section that falls on a multiple of that interval, passing nudgeIndex and idApiUrl down to the component.
  • Update FeastContextualNudge to accept nudgeIndex and idApiUrl as props, call useBraze, and, if Braze returns a banner for the computed placement ID, render BrazeBannersSystemDisplay instead of the default nudge card.
  • Add a GetContext message type to BrazeBannersSystem and a context prop to BrazeBannersSystemDisplay, so the Braze banner iframe can request page, recipe and user context (recipe details, pageId, nudgeIndex, darkMode, etc.) to personalise its content.
  • Consolidate --feast-nudge-heading and --feast-nudge-subtext CSS custom properties into a single --feast-nudge-text variable, as requested by the Design team.
  • Update the Storybook story args to include the new nudgeIndex and idApiUrl props.

Page-aware placement loading

  • Add ISLAND_PLACEMENT_MAP - a map from gu-island component names to their Braze placement IDs. getPagePlacements() queries the server-rendered DOM at runtime and returns only the placements whose island element is present, so we never request placements for components that cannot appear on the current page.
  • Update buildBrazeMessaging to call getPagePlacements() before refreshBanners and skip the refresh entirely when no relevant placements are on the page.

Rate-limit observability

  • refreshBanners now logs an info message listing every placement ID and count before the request, and emits a warning when the caller exceeds Braze's limit of 10 placements per refresh.

Stale-cache handling

  • Add a module-level stalePlacements Set that tracks placement IDs whose last requestBannersRefresh was rejected by Braze's rate-limiter (5 tokens per session, refilling every 3 minutes). The error callback marks the affected placements as stale and logs which ones were added; the success callback clears them.
  • Export isPlacementStale(id) for island components to query the stale state of a specific placement before calling getBanner().
  • Add PLACEMENT_SUPPRESS_ON_STALE - a flat Partial<Record<BrazeBannersSystemPlacementId, boolean>> that controls per-placement suppression behaviour when stale. Banner and EndOfArticle are set to true (suppress - avoids showing outdated MRR campaigns); the five FeastContextualNudge placements are set to false (fall through to the native card). The ISLAND_PLACEMENT_MAP remains a pure DOM-detection structure and carries no suppression logic.
  • canShowBrazeBannersSystem checks stalePlacements before calling getBanner(), and FeastContextualNudge performs the same check before attempting to render a Braze banner.

Why?

The MRR team needs to be able to personalise the copywriting and CTA of the Feast contextual nudge based on the reader's context - for example whether they already have the Feast app installed, whether they hold a Feast subscription, and what country they are browsing from. The native static nudge cannot account for these variables. By delegating rendering to Braze when a banner is available for the signed-in user, we enable a richer and more relevant signed-in experience.

We cap the number of Braze placement slots at five because the Braze Banners API allows up to ten placements per refresh request, and we want to leave headroom for other placements on the page. Rather than placing a nudge at every recipe section, we distribute the five slots evenly so they are spread across the full length of the article.

Page-aware loading avoids burning rate-limit tokens on placements that cannot appear on the current page. Stale-cache handling ensures that, when rate-limit tokens are exhausted, components either suppress the cached banner (for time-sensitive MRR placements) or fall through to a native fallback (for Feast nudges), rather than silently showing potentially outdated content or hiding the slot entirely.

Screenshots

Before After
Screenshot 2026-06-29 at 09 30 54 Screenshot 2026-06-29 at 09 32 30

@andresilva-guardian andresilva-guardian added the feature Departmental tracking: work on a new feature label Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

@andresilva-guardian andresilva-guardian added the run_chromatic Runs chromatic when label is applied label Jun 29, 2026
@github-actions github-actions Bot removed the run_chromatic Runs chromatic when label is applied label Jun 29, 2026
@andresilva-guardian andresilva-guardian marked this pull request as ready for review June 29, 2026 08:34
@andresilva-guardian andresilva-guardian requested a review from a team as a code owner June 29, 2026 08:34
@github-actions

Copy link
Copy Markdown

Hello 👋! When you're ready to run Chromatic, please apply the run_chromatic label to this PR.

You will need to reapply the label each time you want to run Chromatic.

Click here to see the Chromatic project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants