Advanced Usage
Patterns for reusable styling, component composition, and complex layout scenarios.
Reusable Style Constants
When the same Tailwind utility string appears on multiple components, define it once as a C# constant in your page or a shared static class. This keeps strings literal (so Tailwind scans them) while eliminating repetition.
// In a shared file or at the top of your page @code block
private const string PrimaryBtn =
"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium " +
"hover:bg-blue-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500";
private const string SecondaryBtn =
"px-4 py-2 rounded-md border border-gray-300 text-sm font-medium " +
"hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400";
private const string TextInput =
"w-full rounded-md border border-gray-300 px-3 py-2 text-sm " +
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent";<MButton Class="@PrimaryBtn">Save</MButton>
<MButton Class="@SecondaryBtn">Cancel</MButton>
<MInput Class="@TextInput" placeholder="Enter value" @bind-Value="name" />Dialog with Form
Compose MDialog with EditForm to build modal data-entry flows.
Use MDialogClose to wire a cancel button without manually tracking open state.
<MDialog @bind-Open="isOpen">
<MDialogContent Class="fixed inset-0 flex items-center justify-center z-50">
<MDialogOverlay Class="absolute inset-0 bg-black/40" />
<div class="relative z-10 w-full max-w-md rounded-xl bg-white p-6 shadow-xl">
<MDialogTitle Class="text-lg font-semibold mb-4">Edit Profile</MDialogTitle>
<EditForm Model="model" OnValidSubmit="HandleSubmit">
<div class="flex flex-col gap-4">
<div>
<MLabel For="name" Class="block text-sm font-medium mb-1">Name</MLabel>
<MInput id="name" Class="w-full rounded border px-3 py-2 text-sm" @bind-Value="model.Name" />
</div>
</div>
<div class="mt-6 flex justify-end gap-3">
<MDialogClose Class="px-4 py-2 text-sm rounded border">Cancel</MDialogClose>
<MButton type="submit" Class="px-4 py-2 text-sm rounded bg-blue-600 text-white">Save</MButton>
</div>
</EditForm>
</div>
</MDialogContent>
</MDialog>Tabs with Dynamic Content
MTabs manages the active panel index via @bind-ActiveIndex.
Pair with a @switch or conditional render inside MTabsContent
to show different views per tab.
<MTabs @bind-ActiveIndex="activeTab">
<MTabsList Class="flex border-b border-gray-200 mb-4">
<MTabsTrigger Index="0" Class="px-4 py-2 text-sm font-medium data-[state=active]:border-b-2 data-[state=active]:border-blue-600">Overview</MTabsTrigger>
<MTabsTrigger Index="1" Class="px-4 py-2 text-sm font-medium data-[state=active]:border-b-2 data-[state=active]:border-blue-600">Settings</MTabsTrigger>
</MTabsList>
<MTabsContent Index="0">
@if (activeTab == 0) { <p>Overview content here.</p> }
</MTabsContent>
<MTabsContent Index="1">
@if (activeTab == 1) { <p>Settings content here.</p> }
</MTabsContent>
</MTabs>
@code {
private int activeTab = 0;
}Card Layout for Forms
Use MCard sections to visually group form fields. Combine MLabel,
MInput, and MButton for a consistent form layout without any
external CSS.
<MCard Class="w-full max-w-md rounded-xl border border-gray-200 shadow-sm">
<MCardHeader Class="px-6 pt-6 pb-4 border-b border-gray-100">
<h2 class="text-base font-semibold">Account Details</h2>
</MCardHeader>
<MCardContent Class="px-6 py-5 flex flex-col gap-4">
<div>
<MLabel For="email" Class="block text-sm font-medium text-gray-700 mb-1">Email</MLabel>
<MInput id="email" type="email" Class="w-full rounded-md border px-3 py-2 text-sm" @bind-Value="email" />
</div>
<div>
<MLabel For="pwd" Class="block text-sm font-medium text-gray-700 mb-1">Password</MLabel>
<MInput id="pwd" type="password" Class="w-full rounded-md border px-3 py-2 text-sm" @bind-Value="password" />
</div>
</MCardContent>
<MCardFooter Class="px-6 pb-6 flex justify-end">
<MButton Class="px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium hover:bg-blue-700">Update</MButton>
</MCardFooter>
</MCard>
@code {
private string email = string.Empty;
private string password = string.Empty;
}Dropdown with Custom Trigger
MDropdown accepts any content in its trigger slot. Pass an avatar, icon button,
or badge as the trigger instead of a plain text button.
<MDropdown>
<MDropdownTrigger>
<MAvatar Src="https://example.com/avatar.jpg" Class="w-8 h-8 rounded-full cursor-pointer" />
</MDropdownTrigger>
<MDropdownContent Class="absolute right-0 mt-1 w-48 rounded-lg border border-gray-200 bg-white shadow-lg py-1 z-50">
<MDropdownItem Class="px-4 py-2 text-sm hover:bg-gray-50 cursor-pointer">Profile</MDropdownItem>
<MDropdownItem Class="px-4 py-2 text-sm hover:bg-gray-50 cursor-pointer">Settings</MDropdownItem>
<MSeparator Class="my-1" />
<MDropdownItem Class="px-4 py-2 text-sm text-red-600 hover:bg-red-50 cursor-pointer">Sign out</MDropdownItem>
</MDropdownContent>
</MDropdown>Styled DataTable
Use TableClass, HeaderClass, and per-column CellClass
on MDataTableColumn to apply a consistent table style without wrapper divs.
<MDataTable Items="users" PageSize="10"
TableClass="w-full text-sm border-collapse"
HeaderClass="bg-gray-50 text-xs font-semibold text-gray-500 uppercase tracking-wide">
<MDataTableColumn Title="Name" Property="u => u.Name"
CellClass="px-4 py-3 font-medium text-gray-900" />
<MDataTableColumn Title="Email" Property="u => u.Email"
CellClass="px-4 py-3 text-gray-600" />
<MDataTableColumn Title="Role" Property="u => u.Role"
CellClass="px-4 py-3 text-gray-600" />
</MDataTable>