1 · How the release welcome data is structured

rancher/dashboard PR 19324 (cnotv, branch feature/18898-release-welcome-wizard, 3 commits on master), checked on 2026-10-08. This page covers only the data: where the content lives, how it reaches the modal, and what decides whether the modal shows. The code review comes later.

In one paragraph

The modal has no single content object. The content is spread over three places: lists of translation keys in shell/config/release-welcome.ts, the English text for those keys in en-us.yaml under releaseWelcome.*, and text written directly in the templates (titles, subtitles, URLs). Whether the modal shows depends on one user preference, read-release-welcome, which stores the last minor version the user has seen (for example "2.16"). Each card component reads its list from the config file, then calls t(key) on every entry.

The three places the data lives

① Lists of keys

shell/config/release-welcome.ts

  • WHATS_NEW_FEATURES: { id, titleKey, descriptionKey }[], 4 entries
  • PRIME_PRODUCTS: string[] of i18n keys, 8 entries
  • PRIME_BENEFITS: string[] of i18n keys, 4 entries
  • PRIME_URL, SCC_URL, SUPPORT_HANDBOOK_URL, REGISTRATION_ROUTE

② Text

shell/assets/translations/en-us.yaml

  • releaseWelcome.*, 54 new lines
  • titles and subtitles with {vendor} and {version}
  • whatsNew.features.<id>.title/description
  • prime.* (Go Prime card), registration.* (registration card)

③ Show/hide state

shell/utils/release-welcome.ts + shell/store/prefs.ts

  • pref read-release-welcome: the last minor version seen, e.g. "2.16"
  • set to the current minor as soon as the modal opens
  • user menu → What's New reopens it

How the modal opens: login to rendered cards

store loadManagement (shell/store/index.js:872), only when isRancher
runs addReleaseNotesNotification, then fetchAndProcessDynamicContent(...) (not awaited), then showReleaseWelcomeIfNew(commit, dispatch, getters). The modal and dynamic content start at the same time and don't depend on each other.
↓
shouldShowReleaseWelcome(getters)
version = semver.coerce(getVersionData().Version) → "2.16" (none on dev builds → never shows) · not shown in single product mode · shown if read-release-welcome is empty or semver.lt(lastRead, "2.16.0")
↓
openReleaseWelcome(commit, dispatch)
commit('modal/openModal', { component: ReleaseWelcomeDialog, modalWidth: '900px', closeOnClickOutside: true }), then prefs/set read-release-welcome = "2.16". The read state is saved when the modal opens, not when it closes.
↓
ReleaseWelcomeDialog.vue works out the variant
isPrime = isRancherPrime() (from /rancherversion RancherPrime) · canRegister = isPrime && isAdminUser && features/get(SCC)
↓
WhatsNewCard
always; hidden when WHATS_NEW_FEATURES is empty. Each feature renders as t(titleKey) + t(descriptionKey), plus a releaseNotesUrl link
PrimeRegistrationCard
only if canRegister. Benefits come from PRIME_BENEFITS, and the handbook link is added when benefit === SUPPORT_BENEFIT
PrimePromoCard
only if !isPrime. Products from PRIME_PRODUCTS as RcTag chips, CTA → PRIME_URL

Who sees which cards

InstallUserTitleSubtitleCards
CommunityanyonetitleCommunitysubtitleCommunityWhat's new + Go Prime
Primeadmin + SCC featuretitlePrimesubtitlePrime + "register on the Registration page" linkWhat's new + Registration
Primeadmin without SCC / standard usertitlePrimesubtitlePrimeWhat's new only
Single product (Harvester) / dev buildanyonenever shown, and the user menu item is hidden too

Everything the modal renders, as one object

The PR doesn't define this object. This is the content the components put together from ① + ② + the templates, resolved to English. If the modal is going to take content from elsewhere, this is the shape it needs (see page 3). highlighted = text written directly in a template or a constant, not in a list.

{
  "title":    "Welcome to Rancher Community",            // i18n titleCommunity | titlePrime ({vendor})
  "subtitle": "Rancher Community v2.16 is up and running. Here's what's new…",
  "whatsNew": {
    "version": "2.16",                                   // releaseWelcomeVersion()
    "releaseNotesUrl": "https://github.com/rancher/rancher/releases/tag/v2.16.0",  // store getter
    "features": [                                         // WHATS_NEW_FEATURES → t(titleKey), t(descriptionKey)
      { "id": "navigation",          "title": "Improved navigation",            "description": "Jump to any cluster with the new switcher…" },
      { "id": "tables",              "title": "Smarter tables",                 "description": "Tables across the UI are super-charged…" },
      { "id": "kubernetes",          "title": "Broader Kubernetes support",     "description": "Provision and manage clusters on Kubernetes 1.33…" },
      { "id": "hosted-provisioning", "title": "Unified hosted provisioning",    "description": "EKS, AKS and GKE provisioning now runs on…" }
    ]
  },
  "prime": {                                            // Community only
    "title": "Ready for production? Go Prime.",
    "description": "Everything in the community edition, plus 24×7 enterprise support…",
    "products": ["Rancher Manager", "RKE2, k3k & K3s", "SUSE Continuous Integration & Delivery", "SUSE Security",
                 "SUSE Storage", "SUSE Virtualization", "SUSE Observability", "Application Collection"],
    "cta": { "label": "Explore SUSE Rancher Prime", "url": "https://www.suse.com/products/rancher/" }
  },
  "registration": {                                     // Prime admin with SCC only
    "title": "Register to activate your Prime benefits",
    "description": "Registering with SUSE Customer Center unlocks:",
    "benefits": [
      { "text": "24×7 support: open cases directly from your account",
        "link": { "label": "Support handbook", "url": "https://www.suse.com/support/handbook/" } },  // magic-key match in the template
      { "text": "Application Collection: curated, signed apps ready to deploy" },
      { "text": "Hardened images from the Prime trusted registry" },
      { "text": "Rancher Academy: free certification courses for your team" }
    ],
    "cta": { "label": "Open SUSE Customer Center", "url": "https://scc.suse.com/" }
  }
}

What this means for connecting dynamic content

1

The lists hold keys, not text

shell/config/release-welcome.ts · the 3 card components
Shapes the design

Every card calls t() on keys. Dynamic content sends plain text (announcements have title/message strings, not keys), so remote data can't go into these lists as they are. The fix is a step that turns everything into text first (keys → strings for the default, strings as they are for remote), and cards that render text. That step is the object above.

2

The handbook link depends on a magic key

PrimeRegistrationCard.vue · benefit === SUPPORT_BENEFIT
Shapes the design

Which benefit gets the "Support handbook" link is decided by comparing the benefit's i18n key to a constant in the component. Remote text has no key to compare against, so the data has to carry the link itself: { text, link?: { label, url } }.

3

The lists aren't tied to a version

WHATS_NEW_FEATURES
FYI

The bundled list is "what's new in the release this build ships", and needs editing every minor release. Remote content is shared by every installed version, so each remote entry has to say which version it's for (announcements already do this with a semver version field).

4

Read state is per minor version and saved when the modal opens

utils/release-welcome.ts · openReleaseWelcome
FYI

The value "2.16" is compared with semver.lt, so patch releases never show the modal again. Content changes don't either: new text in dynamic content for an already-read 2.16 is only seen through the user menu. That's probably fine, and page 3 keeps it.

5

Titles, subtitles, the version pill and the routes stay local

ReleaseWelcomeDialog.vue
FYI

These depend on the vendor (private label), the running version and the Prime/admin checks, so they belong to the UI rather than to remote content. Page 3 leaves them as i18n and only makes the card contents remote.