Skip to main content
Card is a composite layout component that combines a media image, a structured header (title, subtitle, icons, a detail image, KPI, action buttons), an optional content body, and a footer with primary and secondary actions. All fields are optional except title. The component renders only the sections for which you supply content.

Parameters

title
any View / AttributedString
required
Main heading of the card.
mediaImage
any View / Image?
Full-width image rendered at the top of the card.
description
any View / AttributedString?
Short description text displayed above the title.
subtitle
any View / AttributedString?
Secondary label below the title.
icons
any View / [TextOrIcon]
Row of icons in the card header.
detailImage
any View / Image?
Small image (for example, an avatar) placed in the header alongside the title.
headerAction
any View / FioriButton?
Button rendered at the trailing end of the header.
counter
any View / AttributedString?
Counter text shown in the header (for example, “1 of 3”).
row1
any View
First custom content row inside the card header area.
row2
any View
Second custom content row.
row3
any View
Third custom content row.
kpi
any View / KPIItemData?
KPI value displayed in a dedicated area of the card header.
kpiCaption
any View / AttributedString?
Caption for the KPI value.
cardBody
any View
Arbitrary content rendered below the header. Use this for DataTable views, tag lists, or other custom content.
action
any View / FioriButton?
Primary action button in the card footer.
secondaryAction
any View / FioriButton?
Secondary action button in the footer.
tertiaryAction
any View / FioriButton?
Tertiary action button in the footer.
overflowAction
any View / FioriButton?
Overflow button (defaults to an ellipsis icon).
flexItem
any View
Flexible content area whose position is controlled by flexItemPosition.
flexItemPosition
FlexItemPositionType?
Where the flex item appears: .aboveMainHeader, .aboveTitle, .betweenTitleAndSubtitle, or .belowSubtitle.

ViewBuilder initializer

Card {
    Image("productThumbnail")
        .resizable()
        .aspectRatio(contentMode: .fill)
        .frame(height: 145)
} description: {
    Text("SAP BTP")
} title: {
    Text("Cloud Integration Suite")
} subtitle: {
    Text("Integration & API Management platform")
} icons: {
    IconStack(icons: [
        .icon(Image(systemName: "circle.fill")),
        .icon(Image(systemName: "paperclip")),
        .text("3")
    ])
} headerAction: {
    FioriButton(title: "Edit")
} counter: {
    Text("1 of 5")
} kpi: {
    _KPIItem(KPIItemData.components([
        .unit("$"),
        .metric("26.9"),
        .unit("M")
    ]))
} kpiCaption: {
    Text("Revenue")
} cardBody: {
    VStack(alignment: .leading, spacing: 8) {
        DataTable(model: summaryTableModel)
            .frame(height: 80)
        Divider()
        HStack {
            Tag(verbatim: "Cloud")
            Tag(verbatim: "Integration")
        }
    }
} action: {
    FioriButton(title: "Approve")
} secondaryAction: {
    FioriButton(title: "Decline")
}

Type-based initializer

For cards backed by data models, the type-based initializer is more concise:
Card(
    mediaImage: Image("productThumbnail"),
    description: "SAP BTP",
    title: "Cloud Integration Suite",
    subtitle: "Integration & API Management platform",
    icons: [
        .icon(Image(systemName: "circle.fill")),
        .icon(Image(systemName: "paperclip"))
    ],
    headerAction: FioriButton(title: "Edit"),
    counter: "1 of 5",
    kpi: KPIItemData.components([.unit("$"), .metric("26.9"), .unit("M")]),
    kpiCaption: "Revenue",
    action: FioriButton(title: "Approve"),
    secondaryAction: FioriButton(title: "Decline")
)

Example: product card

struct ProductCard: View {
    let product: Product

    var body: some View {
        Card(
            mediaImage: product.thumbnailImage,
            title: AttributedString(product.name),
            subtitle: AttributedString(product.categoryName),
            icons: [
                .icon(Image(systemName: product.inStock ? "checkmark.circle.fill" : "xmark.circle"))
            ],
            detailImage: Image("brand_logo"),
            kpi: KPIItemData.components([
                .unit(product.currency),
                .metric(product.formattedPrice)
            ]),
            kpiCaption: "Unit price",
            action: FioriButton(title: "Add to cart") { _ in
                cart.add(product)
            },
            secondaryAction: FioriButton(title: "Save") { _ in
                wishlist.add(product)
            }
        )
        .padding(.horizontal)
    }
}

Sub-component styling

All named sections of CardmediaImage, title, subtitle, kpi, cardBody, action, and so on — have corresponding style modifiers that let you override the default Fiori appearance.
Card(title: "Dashboard", subtitle: "Weekly summary")
    .titleStyle { config in
        config.title
            .font(.fiori(forTextStyle: .title2))
            .foregroundStyle(Color.preferredColor(.primaryLabel))
    }
    .actionStyle { config in
        config.action
            .fioriButtonStyle(.borderedProminent)
    }

Build docs developers (and LLMs) love