PPowerApps UI
ComponentsLayoutsTemplatesAbout
P
PowerApps UI
Open Source

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

MIT License

Product

  • Components
  • Layouts
  • Templates
  • Starter Kits
  • 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
ComponentsAccordion List

Accordion List

An expandable grouped list: parent rows that reveal child rows on expand, per-group roll-ups, tone-coloured tags, and inline reorder / edit / delete. One flat gallery — it owns no data, so every action raises an event for your handler.

cmpAccordionList.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

This component requires Modern controls to be enabled. Settings → Updates → Preview → Modern controls and themes

Preview

Orders
1
ORD-4417 · Northwind Traders
Placed 14 May, net 30
Shipped
Hex bolt M8, zinc
SKU 44-1180
Picked$142.80
Flange nut M8
SKU 44-2210
Picked$31.20
Torque wrench, 1/2 inch
SKU 71-0043
Picked$1,066.00
2
ORD-4418 · Contoso Manufacturing
Placed 16 May, net 60
Partial
3
ORD-4419 · Fabrikam Supply
Placed 19 May, prepaid
On hold
4
ORD-4420 · Litware Group
Draft, not yet submitted
DraftNo lines yet
Nothing yet. Press a row or an action.

Config

Accent
The toggles map to Config; reorder / edit / delete raise events, shown in the log.

Expandable grouped list with parent rows, child rows revealed on expand, per-group roll-ups, inline reorder, edit and delete actions, expand-all, and full light/dark theming. One flat gallery, so no nested gallery and no flexible-height measurement to fight.

The component owns no data. Groups and Items are inputs, so it has nothing to write to. Every action raises an event and stops; your handler does the reorder, the edit and the delete. Wire the events or the buttons will appear to do nothing.

Properties

Input

PropertyTypeDefault
Groups — The parent rows. Schema: GroupKey (number, unique, never zero), GroupOrder (number), Marker, Title, Subtitle, Tag, Tone, Meta, Locked. Leave Meta blank and the component counts the children instead.Table4 sample orders
Items — The child rows. Schema: ItemKey (number, unique), GroupKey (matches Groups), ItemOrder (number), Marker, Title, Subtitle, Tag, Tone, Meta, Locked. Same column names as Groups on purpose.Table7 sample lines
Config — Look and behaviour. Every field is Coalesced internally, so a partial record is safe. Keys: Theme, Accent, Radius, RowHeight, ShowHeader, ShowTags, ShowMeta, ShowExpandAll, AllowReorder, AllowEdit, AllowDelete.RecordLight, 12, 76, all switches on
Title — Heading above the list. Hidden with the rest of the header when Config.ShowHeader is false.Text"Orders"

Output

PropertyTypeDescription
ExpandedGroupKeyNumberThe GroupKey currently open, or blank when collapsed or when every group is open.
IsExpandAllBooleanTrue while every group is open.
SelectedItemKeyNumberThe ItemKey of the last child row tapped, or blank.
ActionKeyNumberThe GroupKey or ItemKey whose action was pressed. Read inside the four action events.
ActionRowTypeTextThe literal text group or item, so a handler can branch without guessing.
GroupCountNumberHow many parent rows were passed in.
ItemCountNumberHow many child rows were passed in, across every group.

Events

EventDescription
OnSelectGroupA parent row was tapped, after ExpandedGroupKey has already changed.
OnSelectItemA child row was tapped. SelectedItemKey is populated before firing.
OnMoveUpThe move-up action was pressed. Read ActionKey and ActionRowType and renumber your own data.
OnMoveDownThe move-down action was pressed.
OnEditThe edit action was pressed. Open your own editor.
OnDeleteThe delete action was pressed. The component shows no confirmation of its own.

Implementation Details

Tone, not status

Tag is the text on the pill; Tone is the colour. Valid values are positive, warning, danger, info, or blank for neutral, matched case-insensitively. Nothing in the component knows what your statuses mean, which is why the same control renders order states, approval states and task states without configuration.

Locked rows

Locked true on a group or a child hides all four row actions on that row. Use it for records the user may read but not change: a shipped order, a completed phase, a period that has closed.

Expand-all and single-expand

One group opens at a time by default. The expand-all toggle in the header opens every group; tapping any row while everything is open drops back to single-expand on the row tapped.

GroupKey must never be zero

Zero is the internal sentinel for nothing-expanded. A group keyed zero renders permanently expanded and cannot be collapsed. SharePoint's ID column starts at 1, so using ID is safe; a zero-based sequence of your own is not.

Paste behaviour

The instance is written as Control: CanvasComponent with a ComponentName sibling, not as a control type named after the component.

A pasted instance receives blank for every Table and Record input, so the sample data in Groups and Items does not arrive with it. Wire your own tables. Config is Coalesced internally against its documented defaults, so a blank record still renders correctly.

Width does not survive a paste. The instance arrives at 600 whatever the YAML says, while Height comes through intact. Set Width by hand once and it holds.

Performance

Comfortable into the low hundreds of groups. Above that the right answer is server-side paging, not an accordion.

Use cases

ParentChildMeta roll-up
Purchase orderOrder linesLine count and order total
InvoiceLine itemsAmount due
Project phaseAssigneesWorker count and estimated days
Checklist sectionStepsSteps complete of total
TeamMembersHeadcount
Product categoryProductsIn-stock count
Delivery runStopsDistance and window
CourseModulesPercent complete
IncidentTimeline entriesAge and severity

The shape fits anywhere a list has one meaningful level of nesting and the parent carries a number worth seeing before you open it. If your data is flat, use a gallery. If it nests three levels deep, this is the wrong control.

Examples

The component owns no data — every action raises an event with ActionKey and ActionRowType, so the Patch stays in your app.

Basic usage

Bind Groups and Items to two collections — the component builds the parent/child tree.

OrdersExpand all
1
ORD-4417 · Northwind Traders
Shipped
Hex bolt M8, zinc
Picked$142.80
Flange nut M8
Picked$31.20
2
ORD-4418 · Contoso Manufacturing
Partial
cmpAccordionList.Groups: colOrders
cmpAccordionList.Items:  colOrderLines
cmpAccordionList.Title:  "Purchase orders"
cmpAccordionList.Width:  Parent.Width
cmpAccordionList.Height: 600

Reorder handler

OnMoveUp / OnMoveDown fire with the row's key and type — you find the neighbour and swap orders.

OrdersExpand all
1
ORD-4417 · Northwind Traders
Shipped
Hex bolt M8, zinc
Picked
Flange nut M8
Picked
2
ORD-4418 · Contoso Manufacturing
Partial
cmpAccordionList.OnMoveUp: |
    If(
        cmpAccordionList.ActionRowType = "group",
        With(
            { me: LookUp(colOrders, GroupKey = cmpAccordionList.ActionKey) },
            With(
                { prv: Last(Sort(Filter(colOrders,
                    GroupOrder < me.GroupOrder), GroupOrder)) },
                Patch(colOrders, prv, { GroupOrder: me.GroupOrder });
                Patch(colOrders, me,  { GroupOrder: prv.GroupOrder })
            )
        ),
        With(
            { me: LookUp(colOrderLines, ItemKey = cmpAccordionList.ActionKey) },
            With(
                { prv: Last(Sort(Filter(colOrderLines,
                    GroupKey = me.GroupKey,
                    ItemOrder < me.ItemOrder), ItemOrder)) },
                Patch(colOrderLines, prv, { ItemOrder: me.ItemOrder });
                Patch(colOrderLines, me,  { ItemOrder: prv.ItemOrder })
            )
        )
    )

Delete handler

OnDelete hands you ActionKey / ActionRowType — the host decides whether a parent delete cascades.

OrdersExpand all
1
ORD-4417 · Northwind Traders
Shipped
Hex bolt M8, zinc
Picked$142.80
Flange nut M8
Picked$31.20
2
ORD-4418 · Contoso Manufacturing
Partial
cmpAccordionList.OnDelete: |
    If(
        cmpAccordionList.ActionRowType = "group",
        RemoveIf(colOrderLines, GroupKey = cmpAccordionList.ActionKey);
        RemoveIf(colOrders,     GroupKey = cmpAccordionList.ActionKey),
        RemoveIf(colOrderLines, ItemKey  = cmpAccordionList.ActionKey)
    )

Edit handler → popup

OnEdit fires for a group or an item (ActionRowType tells which) — you open your own editor and Patch the title on Save.

OrdersExpand all
1
ORD-4417 · Northwind Traders
Shipped
Hex bolt M8, zinc
Picked
Flange nut M8
Picked
2
ORD-4418 · Contoso Manufacturing
Partial
Edit item 101
Hex bolt M8, zinc
CancelSave
cmpAccordionList.OnEdit: |
    Set(gblEditKey,  cmpAccordionList.ActionKey);
    Set(gblEditType, cmpAccordionList.ActionRowType);
    Set(gblEditTitle,
        If(gblEditType = "group",
            LookUp(colOrders,     GroupKey = gblEditKey).Title,
            LookUp(colOrderLines, ItemKey  = gblEditKey).Title
        )
    );
    Set(gblEditOpen, true)   // your popup binds to gblEdit*

Read-only viewer

Turn the Allow* flags off in Config for a display-only list — no action column renders.

OrdersExpand all
1
ORD-4417 · Northwind Traders
Shipped
Hex bolt M8, zinc
Picked$142.80
Flange nut M8
Picked$31.20
2
ORD-4418 · Contoso Manufacturing
Partial
cmpAccordionList.Config: {
    Theme:        "light",
    ShowTags:     true,
    ShowMeta:     true,
    AllowReorder: false,
    AllowEdit:    false,
    AllowDelete:  false
}

SharePoint

Two lists in a parent and child relationship, with a plain indexed number column as the foreign key rather than a Lookup column. Index both key columns.

Bind the component to collections, never directly to the lists. The collections are the seam between your schema and the component's, and they are where a status becomes a Tone.

ClearCollect(
    colOrders,
    ForAll(
        Filter('Orders', Archived = false) As o,
        {
            GroupKey:   o.OrderKey,
            GroupOrder: o.OrderSeq,
            Marker:     Text(o.OrderSeq),
            Title:      o.Title,
            Subtitle:   o.Customer,
            Tag:        o.Status.Value,
            Tone:       Switch(
                            o.Status.Value,
                            "Shipped", "positive",
                            "Partial", "warning",
                            "On hold", "danger",
                            ""
                        ),
            Meta:       "",
            Locked:     o.IsLocked
        }
    )
)

A Choice column is a record, so read .Value. Leaving it off gives a blank Tag with no error.

Three things to keep in mind. Filter on an indexed column delegates while ForAll and Switch do not, which is why they wrap a Filter that has already narrowed the result. Patch the list and the collection in the same handler, or patch the list and reload, since patching only the collection leaves the screen right and the list wrong. And SharePoint has no cascade, so a parent delete must remove its children first or they are orphaned permanently.

Full walkthrough with column schemas, lazy child loading and delegation limits: Parent and child SharePoint lists in a Canvas accordion.

Get new components and templates when they drop

One email when something new ships. Unsubscribe anytime.

Community

Use the toolbar to format · or type markdown directly