Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Expander & Accordion - Blazor Usage

The Blazor components mirror the MAUI ones — same concepts, same property names where the platform allows. This page covers what is different.

Frameworks
Blazor
<Expander HeaderText="Shipping" HeaderDetail="Arrives Tuesday">
<p>123 Fake Street</p>
<p>Springfield</p>
</Expander>

With a header of your own, both slots have to be named:

<Expander>
<Header>
<strong>Custom header markup</strong>
</Header>
<ChildContent>
<p>Body.</p>
</ChildContent>
</Expander>

Animation is the same flags enum, written the C# way:

<Expander HeaderText="Everything at once"
Animation="ExpanderAnimation.Height | ExpanderAnimation.Slide | ExpanderAnimation.Fade"
SlideFrom="ExpanderSlideFrom.Top"
AnimationDuration="250"
AnimationEasing="cubic-bezier(0.2, 0, 0, 1)">
<p>…</p>
</Expander>

AnimationDuration is an int of milliseconds and AnimationEasing is a CSS timing function.

The reveal is pure CSS — the panel is a grid transitioning grid-template-rows between 0fr and 1fr — so there is no measuring, no JS interop, and content that changes size while open still lays out normally. prefers-reduced-motion: reduce disables the transitions.

The chrome parameters take CSS strings rather than typed colours and doubles, and the two fills are named HeaderBackground and ContentBackground rather than …BackgroundColor:

<Expander HeaderText="Coloured border"
BorderColor="#7C3AED"
BorderThickness="2px"
CornerRadius="18px"
HeaderBackground="#EDE9FE"
HeaderTextColor="#4C1D95"
ContentPadding="16 20">
<p>…</p>
</Expander>

Padding parameters accept bare numbers as pixels, so "16 20" means 16px 20px. Leave a parameter unset and the component falls back to the matching --shiny-* design token, which is what lets a theme swap reach inside it.

@bind-IsExpanded for two-way binding; OnExpanded and OnCollapsed for the transitions. ExpandAsync(), CollapseAsync() and ToggleAsync() drive it from code.

<Expander HeaderText="Advanced" @bind-IsExpanded="showAdvanced" OnExpanded="LoadAsync">
<p>…</p>
</Expander>

The header is a role="button" element carrying aria-expanded and aria-controls, focusable with Tab and activated by Enter or Space. A collapsed panel is inert and aria-hidden, so nothing inside it can be tabbed into or read out.

<Accordion SelectionMode="AccordionSelectionMode.Single"
AllowCollapseAll="false"
@bind-ExpandedIndex="index"
CornerRadius="14px">
<Expander HeaderText="Account">…</Expander>
<Expander HeaderText="Billing">…</Expander>
</Accordion>

The rules are identical to MAUI: SelectionMode, AllowCollapseAll, two-way ExpandedIndex, ExpandedIndexes, ExpandAll(), CollapseAll(), ExpandItem(index), and motion/chrome parameters that are defaults reaching only the items that did not set them themselves. OnItemExpanded and OnItemCollapsed carry the expander, its model, its index and the new state.

A plain @foreach of expanders is the natural Blazor form, and the models stay strongly typed. Set Item so OnItemExpanded can hand the model back, and @key so the list diffs cleanly.

<Accordion SelectionMode="AccordionSelectionMode.Single"
LoadContentOnDemand="true"
OnItemExpanded="@(e => Track((Faq)e.Data!))">
@foreach (var faq in faqs)
{
<Expander @key="faq" Item="faq" HeaderText="@faq.Question" HeaderDetail="@faq.Category">
<p>@faq.Answer</p>
</Expander>
}
</Accordion>

There is also an Items parameter with ItemHeader / ItemContent templates for when the shape is only known at runtime. Its templates are RenderFragment<object>, so the @foreach above is the better choice whenever you have a type to name.