PPowerApps UI
ComponentsLayoutsTemplatesAppsAbout
P
PowerApps UI
Open Source

Production-ready UI components and patterns for Power Apps developers.

MIT License

Product

  • Components
  • Layouts
  • Templates
  • Apps
  • About
  • Cheat Sheet
  • Documentation

Developers

  • Getting Started
  • Suggest Idea
  • Submit Component
  • Report Issues

Resources

  • Changelog
  • Newsletter
  • Best Practices
  • Design System
© 2026 PowerApps UI. Open source under MIT License.
Built byRodas Yonass
Privacy Policy•Terms of Service•MIT License
ComponentsKPI Cards

KPI Cards

Responsive KPI cards with icons, sparklines, trend badges, and skeleton loading. Five style variants — Standard, Compact, Minimal, Filled, Chart. Auto-adjusts from 1–4 columns by screen width. Supports number abbreviation and click events.

cmpKPI.yaml
  1. 1. Go to the Components tab (right side panel, next to Screens)
  2. 2. Click New component — this creates a blank component
  3. 3. Click outside the new component to deselect it
  4. 4. Paste the YAML with Ctrl+V / ⌘V

If your users are in a comma-decimal region, wrap the numbers

This chart draws with an SVG built by string concatenation, and Power Fx & converts numbers using the viewer’s locale. In regions that write 123,45 rather than 123.45, SVG reads that comma as a separator between two numbers and the the sparkline polygon and polyline points silently fail to draw. Axes and labels still render, because whole numbers are unaffected.

The fix is to format explicitly at the point of concatenation. In this component that means the sparkline polygon and polyline points:

- Text(Round((Value - 1) / Max(cnt - 1, 1) * w, 1))
+ Text(Round((Value - 1) / Max(cnt - 1, 1) * w, 1), "[$-en-US]0.##")

Do not repair the finished string with Substitute(s, ",", "."). That also rewrites thousands separators in your labels, turning 1,420 into 1.420, and breaks comma-separated font stacks.

Preview

All Assets

118

↑ +14%vs last month

In Use

6

↓ -8%vs last month

Available

111

↑ +12%vs last month

Expiring Soon

1

Warranty expiring in less than 30 days

Standard mode

Properties

Input

PropertyTypeDefault
Data

Table of KPI items. Required: Icon, Value, Label, IconBg, IconColor. Optional: ID, PercentChange, PercentLabel, SparklineData (comma-separated numbers), isHidden.

Table4 sample cards
Style

"Standard" (value + sparkline footer, icon top-right), "Compact" (88px, icon left, inline %), "Minimal" (88px, no icon), "Filled" (colored bg from IconBg), "Chart" (sparkline hero, value secondary).

Text"Standard"
IsLoading

When true, hides the data gallery and shows skeleton placeholder cards instead. Use while fetching data.

Booleanfalse
StyleConfig

Record with colors (cardBg, border, text, textMuted, positive, negative, neutral, skeletonBase, skeletonShine), space, radius, type, heights, and abbreviateThreshold tokens.

RecordLight theme tokens
Icons

Table of {Name, SVG} rows. SVG strings use "COLOR" placeholder replaced at runtime by IconColor via Substitute(). Custom icons can be added.

Table6 built-in icons
ColumnsLayout

Record with Mobile, Tablet, Desktop column counts and MobileBreakpoint (640), TabletBreakpoint (1024), DesktopBreakpoint (1366) widths.

Record{Mobile:1, Tablet:2, Desktop:4}
AnimationEffect

"Pop" (scale in), "Push" (slide), or "None" (instant).

Text"Pop"

Events

EventDescription
OnCardClickFires when any card is tapped. Receives the full KPI record as ClickedItem (ID, Icon, Value, Label, PercentChange, PercentLabel, IconBg, IconColor, SparklineData).

Implementation Details

Five style variants

Standard

Icon top-right in a 44px circle. 190px card. Value at 32px bold, small sparkline in the middle, percent + label footer.

Best for: Full dashboards, detail views

Compact

Icon left-aligned in a 44px circle. 88px card. Value and inline percent on one row. No sparkline.

Best for: Above tables, tight layouts

Minimal

Identical to Compact but no icon — label and value flush-left. Maximum information density.

Best for: Mobile, sidebars, dense UIs

Filled

Card background = IconBg, all text = IconColor, icon hidden, border removed. Same data columns — no extra fields needed.

Best for: Hero rows, exec summaries

Chart

Sparkline is the hero — full-width, 96px tall, bleeds edge-to-edge. Label + value + inline percent are compact at top. No icon.

Best for: Time-series dashboards, trend focus

Sparklines

Sparklines appear automatically in Standard and Chart modes when SparklineData is non-blank. They are intentionally omitted from Compact, Minimal, and Filled — sparklines need room to communicate. Pass a comma-separated string per record: "10,25,18,42,38,56". Color is determined by PercentChange: positive = green, negative = red, null/0 = gray.

Number abbreviation

Values ≥ abbreviateThreshold (default 10000) are abbreviated to 10.4K. Values ≥ 1,000,000 become 1.2M. Set abbreviateThreshold: 0 to disable.

Skeleton loading

Set IsLoading = true to hide the data gallery and show cntSkeletonOverlay — a mirrored gallery with the same WrapCount and TemplateSize rendering placeholder bars. Skeleton cards match the active style.

Responsive grid & dynamic height

WrapCount is driven by App.Width vs ColumnsLayout breakpoints. Component height is calculated from visible card count ÷ columns × card height, so it never clips when cards wrap to multiple rows.

Examples

Chart mode — trend-first layout

All Assets

118↑ +14%

In Use

6↓ -8%

Available

111↑ +12%
cmpKPI.Style = "Chart"

// SparklineData is comma-separated numbers
// sourced from a collection built on load:
App.OnStart:
    ClearCollect(colKPITrend,
        {
            ID: 1,
            Label: "All Assets",
            Value: 118,
            SparklineData: Concat(
                colWeeklySnapshots,
                Text(AssetCount), ","
            )
        }
    )

// Sparkline color follows PercentChange:
//   > 0 → green  |  < 0 → red  |  0 → gray

Skeleton loading state

// Show skeleton while data loads
cmpKPI.IsLoading = IsEmpty(colKPIData)

// Typical pattern:
App.OnStart:
    Set(varKPILoading, true);
    ClearCollect(colKPIData, KPISource);
    Set(varKPILoading, false)

cmpKPI.IsLoading = varKPILoading

Basic static data

All Assets

118

↑ +14%vs last month

In Use

6

↓ -8%vs last month

Available

111

↑ +12%vs last month

Expiring Soon

1

Warranty expiring in less than 30 days
cmpKPI.Data = Table(
    {
      ID: 1, Icon: "Box", Value: 118,
      Label: "All Assets",
      PercentChange: 14,
      PercentLabel: "vs last month",
      IconBg: "#EBF5FF",
      IconColor: "#2196F3",
      SparklineData: "10,25,42,38,61,70"
    },
    {
      ID: 2, Icon: "Warning", Value: 1,
      Label: "Expiring Soon",
      PercentLabel: "< 30 days",
      IconBg: "#FFEBEE", IconColor: "#F44336",
      SparklineData: ""
    }
)

Live data — SharePoint connection

Total Value

$45.2K

↑ +14%vs last month

Open Orders

234

↓ -8%vs last month
// Named formula — auto-refreshing
nfKPIData =
    ForAll(
        GroupBy(Assets, "Status", "Group"),
        {
            ID: CountRows(Group),
            Icon: Switch(Status,
                "Available", "Package",
                "In Use", "TrendLines",
                "Box"
            ),
            Value: CountRows(Group),
            Label: Status,
            PercentChange: Round(
                (CountRows(Group) - varPrev)
                    / varPrev * 100, 0
            ),
            PercentLabel: "vs last month",
            IconBg: Switch(Status,
                "Available", "#E8F5E9",
                "#EBF5FF"
            ),
            IconColor: Switch(Status,
                "Available", "#4CAF50",
                "#2196F3"
            ),
            SparklineData: ""
        }
    )

cmpKPI.Data = nfKPIData

Data Schema

Data record

FieldTypeRequiredNotes
IDNumberRecommendedUnique identifier used for OnCardClick context.
IconTextYesName matching an Icons table entry e.g. "Box", "TrendLines", "Warning".
ValueNumberYesMain KPI number. Auto-abbreviated if ≥ abbreviateThreshold.
LabelTextYesCard title. Labels containing value/price/cost auto-format Value as currency.
PercentChangeNumberNoTrend %. Positive = green ↑, negative = red ↓. Blank to hide. Also drives sparkline color.
PercentLabelTextNoFooter sub-text e.g. "vs last month". Blank to hide.
IconBgTextYesHex background for icon circle. In Filled, becomes the entire card background.
IconColorTextYesHex color for SVG stroke. In Filled, becomes all text and percent color.
SparklineDataTextNoComma-separated numbers e.g. "10,25,18,42". Shown in Standard and Chart only. Empty string hides sparkline.
isHiddenBooleanNoSet true to hide a card without removing it from the data source.

Architecture

20 controls — 5 Text, 8 Classic/Button, 4 Image, 4 GroupContainer, 1 Gallery (×2 for skeleton).

7 input properties — Data, Style, IsLoading, StyleConfig, Icons, ColumnsLayout, AnimationEffect.

1 event — OnCardClick.

Default size — Parent.Width × dynamic height. Card height: 88px (Compact/Minimal) or 190px (Standard/Filled/Chart).

cmpKPI (Parent.Width × dynamic height)
├── cntStatsRow (GroupContainer — AutoLayout, Visible=!IsLoading)
│   └── galKPIs (Gallery — vertical, WrapCount by breakpoint)
│       └── conKPICard (GroupContainer — ManualLayout, DropShadow.Light)
│           ├── txtLabel (ModernText — uppercase, 12px semibold)
│           ├── cntValueRow (GroupContainer — AutoLayout horizontal)
│           │   ├── txtValue (ModernText — Bold, FillPortions=1, abbreviation formula)
│           │   └── txtPercentInline (Text — Compact/Minimal/Chart only)
│           ├── imgSparkline (Image — SVG, clr resolved from PercentChange, Standard+Chart)
│           ├── cntKPIFooter (GroupContainer — Standard+Filled only)
│           │   ├── txtFooterPercent (ModernText — ↑/↓, semibold)
│           │   └── txtFooterSub (ModernText — "vs last month", 12px)
│           ├── btnIconBg (Classic/Button — circle, hidden in Filled/Minimal/Chart)
│           ├── imgIconKPI (Image — inline SVG, hidden in Filled/Minimal/Chart)
│           └── btnCardOverlay (Classic/Button — transparent, OnSelect→OnCardClick)
│
└── cntSkeletonOverlay (GroupContainer — Visible=IsLoading)
    └── galSkeleton (Gallery — Sequence(8), same WrapCount+TemplateSize)
        └── conSkeletonCard (GroupContainer)
            ├── btnSkeletonLabel   (Classic/Button — 10px bar)
            ├── btnSkeletonValue   (Classic/Button — 24px bar)
            ├── btnSkeletonChart   (Classic/Button — 96px full-width, Chart only)
            ├── btnSkeletonFooter  (Classic/Button — 10px bar, Standard only)
            └── btnSkeletonIcon    (Classic/Button — 44px circle, Standard/Compact/Filled)

            Sparkline visibility matrix:
            Standard → imgSparkline at Y=106, H=36, W=card-48, X=20
            Chart    → imgSparkline at Y=card.H-96, H=96, W=card (edge-to-edge)
            Compact / Minimal / Filled → imgSparkline hidden

Get new components and templates when they drop

One email when something new ships. Unsubscribe anytime.

Used in these templates & layouts

App HomeTemplateApproval WorkflowTemplateDashboard Landing PageTemplate

Community

Use the toolbar to format · or type markdown directly