SfSkills Domains Docs GitHub

Home / omnistudio / flexcard-container-composition

flexcard-container-composition

Design FlexCard composition: parent/child state flow, layout modes, actions, event wiring, and data source selection. Trigger keywords: flexcard, flex card composition, parent child flexcard, flexcard state, flexcard. NOT for the first-time FlexCard Hello-World, LWC alternatives, or Experience Cloud theming — use admin/flexcard-requirements.

Open the skill folder on GitHub


FlexCard Container Composition

Use this skill when deciding how a FlexCard surface should be split — one card or several, parent/child or siblings, which data source per card, how the pieces talk — and when a correctly-configured child card is inexplicably not calling its data source. Composition choices on this platform are performance choices and, more often than teams expect, security choices.


Data Source Selection

The Data Source wizard offers exactly ten types. Naming yours exactly is what lets a reviewer check the decision.

TypeVerbatim descriptionUse
Integration Procedures"uses an Integration Procedure to return data from multiple internal and external sources"default — one place to cache, centralise errors, and enforce access
Omnistudio Data Mapper"uses a Data Mapper Extract interface to return data from a Salesforce object"single-object read, no aggregation
Apex Remote"uses an Apex Remote class and method to return data"logic that needs code
Apex REST"uses a REST endpoint of an Apex class to return data"an existing Apex REST service
SOQL Query"uses the Salesforce Object Query Language (SOQL) to search an org's Salesforce data"prototyping only
SOSL Search"uses the Salesforce Object Search Language (SOSL) to construct text-based search queries"cross-object text search
Streaming API"uses the Salesforce Streaming API to send notifications of general events"server-pushed events
SDK"uses a method from a Software Development Kit (SDK) to get data to populate fields"SDK-backed integration
Custom"uses sample JSON to set up a Flexcard with temporary data that'll eventually be replaced"design-time scaffolding
Nonefor child FlexCards fed by their parentany Data-Node-fed child

SOQL Query and Custom are prototyping data sources. Neither has anywhere to put field-level security, a Cache Block, requiredPermission, or an error branch. Ship on Integration Procedures or Data Mapper with fieldLevelSecurityEnabled true — and project down to the fields the card actually renders, because the payload lands in the browser whether or not the template shows it.


Parent To Child: Two Mechanisms, And They Do Not Compose

The parent's Flexcard element has three properties that matter:

PropertyPurpose
Flexcard Namewhich child card to embed
Data Nodepass "a record or an array of records to the child" — {record} for the current record, {records} to send all data
Attributespass specific values — attribute name and value, e.g. Id → {Id}

Attributes arrive in the child's Input Map; the child references them as {Parent.Id}.

The override rule, verbatim, and it is the single most useful fact in this skill:

Selecting a parent Data Node silently overrides a child's own data source, configured or not — the single most common cause of "my child card isn't fetching". Quoted in 0.

Selecting a Data Node silently disables the child's own data source. So:

child renders what the parent already has
    -> select Data Node, set child data source to None (for legibility)

child needs MORE or FRESHER data
    -> leave Data Node UNSELECTED, pass an Attribute, let the child fetch

need a parent-supplied record AND an independent fetch
    -> these are siblings, not parent/child. Use a pubsub channel.

A child that "isn't calling its Integration Procedure" is this rule roughly every time. Check the parent's element before investigating the child.


Sibling Communication: The Real pubsub API

import pubsub from "lightning/omnistudioPubsub";
MethodSignature
registerregister(eventName, callbackobj) — registers handlers to a channel
unregisterunregister(eventName, callbackobj) — removes handlers from a channel
firefire(eventName, action, payload) — fires to all handlers on a channel

Three things the signatures hide:

  1. The first argument is a channel. The official example passes

    "testchannel". Individual event names are keys inside the callback object, mapped to bound handlers. Passing an event name and a bare function — the shape every other pubsub library uses — subscribes to a channel nobody publishes to, silently.

  2. Retain the callback object. Teardown requires "both the channel name and

    instance of your event handler objects"; an equivalent fresh object unregisters nothing.

  3. Names are a flat, page-wide namespace. Prefix by workspace —

    quoteworkspace/quoteselected — not by component, and never ship rowselected.


States Are UX, Not Authorization

"A Flexcard state determines what the user can see and do on the card," and conditions select the matching state from data the browser already holds. The evaluation is client-side.

So placing an action only in an AtRisk state hides it; it does not prevent it. Authorization lives on the action's target — the OmniScript's or Integration Procedure's requiredPermission, plus the running user's object, field, and sharing permissions. The two look identical for a well-behaved user, which is why the conflation survives review.

Past about three states, prefer separate cards plus a container: overlapping state conditions are invisible to a reader holding seven layouts in mind.


Publishing Targets Constrain The Design

FlexCards publish to Lightning pages via Lightning App Builder, Community and portal pages via Experience Builder, external content management systems such as Adobe Experience Manager, and custom web containers such as Heroku. Two consequences: use the Navigate action type rather than composing a /lightning/r/... path, and test as a user from the target audience in the target host — the author's preview runs with the author's permissions.


  1. Sketch the surface and mark the boundaries where the *data's audience or

    lifetime* changes. That, not the visual layout, is where the card boundary belongs.

  2. For each card, pick exactly one data source by its exact type name. Default

    to Integration Procedures; use Data Mapper for a single-object read; never ship SOQL Query or Custom.

  3. Decide parent/child versus siblings. Child fed by a Data Node when it

    renders what the parent has; Attributes plus its own fetch when it needs more; siblings on a pubsub channel when the pieces are genuinely independent. Remember Data Node and an independent child fetch do not compose.

  4. Write the event contract before wiring: channel, event, payload shape,

    publisher, consumers, version. Prefix channels by workspace.

  5. Separate immutables from mutables. Input parameters and Attributes carry

    immutables and are evaluated at render; channel events carry anything that must change during the card's life.

  6. Define states and check their conditions do not overlap. Enforce anything

    security-relevant on the action's target, not on the state.

  7. Preview in every required form factor, then verify in the target host as a

    user from the target audience — comparing the network payload, not only the rendered card.


Review Checklist


Worked Examples (see references/examples.md)

Common Gotchas (see references/gotchas.md)

Top LLM Anti-Patterns (full list in references/llm-anti-patterns.md)


Official Sources Used

See references/well-architected.md for the full source list with the specific claim each source grounds.

Dependencies

Package files