
Syncfusion Blazor Buttons
- 229 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-buttons for development tasks
About
syncfusion-blazor-buttons: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-buttons
Syncfusion Blazor Buttons by the numbers
- 229 all-time installs (skills.sh)
- +13 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,729 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/blazor-ui-components-skills --skill syncfusion-blazor-buttonsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 229 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-buttons for development tasks
Files
Syncfusion Blazor Buttons Components
Table of Contents
- When to Use This Skill
- Components Overview
- Common Setup and Installation
- Documentation and Navigation Guide
- Common Patterns Across Components
- Shared Configuration
- Common Use Cases
---
Components Overview
The Syncfusion.Blazor.Buttons package provides 11 comprehensive button components:
Core Buttons
1. Button (SfButton) - Standard button with multiple types, styles, icons, and states 2. ButtonGroup (SfButtonGroup) - Grouped button controls with selection modes 3. CheckBox (SfCheckBox) - Tri-state checkbox with checked, unchecked, and indeterminate states 4. RadioButton (SfRadioButton) - Radio button for mutually exclusive selections 5. ToggleSwitch (SfSwitch) - On/off toggle switch control
Interactive Buttons
6. DropdownMenu (SfDropDownButton) - Button with dropdown menu options 7. SplitButton (SfSplitButton) - Button with primary action and dropdown menu 8. ProgressButton (SfProgressButton) - Button with built-in progress indication
Floating Actions
9. FloatingActionButton (SfFab) - Circular floating action button 10. SpeedDial (SfSpeedDial) - FAB with expandable action items (linear/radial)
Chip Components
11. Chip (SfChip) - Compact elements for tags, filters, selections, and actions
Package: Syncfusion.Blazor.Buttons and Syncfusion.Blazor.SplitButtons
---
Common Setup and Installation
All button components share the same installation and setup process:
NuGet Packages
Install the required packages based on component type:
# For Button, Chip, FAB, ToggleSwitch
dotnet add package Syncfusion.Blazor.Buttons
# For DropdownMenu, SplitButton, ProgressButton, ButtonGroup, SpeedDial
dotnet add package Syncfusion.Blazor.SplitButtons
# Required theme package
dotnet add package Syncfusion.Blazor.ThemesSetup Steps (Common for All)
1. Add Namespaces (~/_Imports.razor):
@using Syncfusion.Blazor
@using Syncfusion.Blazor.Buttons
@using Syncfusion.Blazor.SplitButtons2. Register Service (~/Program.cs):
using Syncfusion.Blazor;
builder.Services.AddSyncfusionBlazor();3. Add Theme and Script (~/index.html or ~/App.razor):
<head>
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>
</head>---
Documentation and Navigation Guide
Button Component
📄 Read: references/button-features.md
- Getting started and installation
- Basic button implementation
- Button types (flat, outline, round, primary, toggle)
- Button styles (primary, success, info, warning, danger, link)
- Icons and icon positioning
- Button sizes (small, normal)
- Repeat buttons
- Tooltips
- HTML attribute support
- Native event handling
📄 Read: references/button-styling.md
- CSS class customization
- Appearance customization
- Block buttons
- Rounded corners
- RTL (right-to-left) support
- Theme integration
- Color schemes and variations
- Responsive design patterns
📄 Read: references/button-advanced-features.md
- Accessibility (WCAG compliance, keyboard navigation, ARIA attributes)
- Screen reader support
- Disabled state handling
- Toggle button functionality
- Server vs Web App implementation differences
- Performance optimization
- Best practices and common pitfalls
---
RadioButton Component
📄 Read: references/radio-button-features.md
- Getting started and installation
- Basic radio button implementation
- Radio button grouping with Name property
- Label configuration and positioning
- State and value management
- Two-way data binding (@bind-Checked)
- Value property for identification
- Pre-selecting default values
- Form integration patterns
📄 Read: references/radio-button-styling.md
- CssClass for custom styling
- HtmlAttributes dictionary usage
- EnableRtl for right-to-left layouts
- Disabled state configuration
- EnablePersistence for state storage
- Custom CSS class patterns
- Theme customization techniques
- Responsive design approaches
📄 Read: references/radio-button-advanced-features.md
- ValueChange event handler
- ChangeArgs event data structure
- CheckedChanged callback implementation
- Created lifecycle event
- Event handling patterns
- ARIA attributes for accessibility
- Keyboard navigation support
- Best practices and common patterns
---
CheckBox Component
📄 Read: references/checkbox-features.md
- Getting started and installation
- Basic checkbox implementation
- Checkbox states (checked, unchecked, disabled)
- Label configuration
- Change event handling
- Two-way data binding (@bind-Checked)
- Multiple checkboxes and checkbox groups
- Indeterminate (tristate) state
📄 Read: references/checkbox-styling.md
- CSS class customization
- Appearance customization
- Theme integration (Bootstrap5, Material, Fabric, Tailwind)
- Color variations (primary, success, warning, danger)
- Size customization
- RTL (right-to-left) support
- Disabled appearance
- Label styling and spacing
📄 Read: references/checkbox-advanced-features.md
- Disabled state handling and conditional disabling
- Accessibility (WCAG, ARIA attributes, screen readers)
- Keyboard navigation (Tab, Space)
- Form integration and validation
- State management patterns
- Best practices and common patterns
---
Button Group Component
📄 Read: references/button-group-features.md
- Getting started and installation
- Basic button group implementation
- Horizontal and vertical orientation
- Selection modes (single, multiple, none)
- Nesting button groups
- Native event handling
- Item configuration and data binding
📄 Read: references/button-group-styling.md
- Appearance customization
- Button types and styles within groups
- Icons in button groups
- Size variations
- Custom CSS classes
- Theme integration
📄 Read: references/button-group-advanced-features.md
- Accessibility features
- Keyboard navigation
- Selection state management
- Event handling patterns
- Best practices for button groups
---
Chip Component
📄 Read: references/chip-features.md
- Getting started and installation
- Basic chip implementation
- Chip types (input, filter, choice, action)
- Icon integration
- Delete functionality
- Selection handling
- Events (click, delete, created, beforeItemRender)
- Dynamic chip collections
- Rendering from data sources
📄 Read: references/chip-styling.md
- Appearance customization
- Color variations
- Size options
- Custom CSS classes
- Icon and avatar styling
- Theme integration
- Responsive design
📄 Read: references/chip-advanced-features.md
- Accessibility support
- Keyboard navigation
- Custom template support
- State management patterns
- Event handling
- Best practices
---
Dropdown Menu Component
📄 Read: references/dropdown-menu-features.md
- Getting started and installation
- Basic dropdown menu implementation
- Popup items configuration (text, ID, icon, separator)
- Icons in menu items
- Item click events
- Opening and closing behavior
- Position customization
- Dynamically adding/removing items
- Grouped popups with ListView integration
- Dialog opening on item click
- DropdownList vs DropdownButton comparison
📄 Read: references/dropdown-menu-styling.md
- Appearance customization
- Caret icon customization
- Hiding dropdown arrow
- Icon and width customization
- Popup width customization
- Rounded corners
- RTL support
- Animation effects
- Template customization
- Theme integration
📄 Read: references/dropdown-menu-advanced-features.md
- Accessibility features
- Disabled state handling
- Native event handling
- Keyboard navigation
- Focus management
- Best practices and patterns
---
Floating Action Button (FAB) Component
📄 Read: references/fab-features.md
- Getting started and installation
- Basic FAB implementation
- Position options (TopLeft, TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottomCenter, BottomRight)
- Icons configuration
- Content customization
- Target element binding
- Click event handling
- Show/hide behavior
- Visibility control
📄 Read: references/fab-styling.md
- Appearance styles (primary, success, info, warning, danger)
- Size variations (small, medium, large)
- Icon positioning
- Color customization
- CSS class customization
- Disabled appearance
- Theme integration
📄 Read: references/fab-advanced-features.md
- Accessibility support
- Keyboard navigation
- Event handling patterns
- State management
- Best practices for FAB usage
---
Progress Button Component
📄 Read: references/progress-button-features.md
- Getting started and installation
- Basic progress button implementation
- Spinner types and positions (left, right, top, bottom, center)
- Progress states (Begin, Progress, End, Fail)
- Duration configuration
- Content customization during progress
- Events (OnBegin, Progress, End, Fail)
- Stopping progress programmatically
- Hiding spinner
- Progress tracking
📄 Read: references/progress-button-styling.md
- Appearance customization
- Button types and icons
- CSS class customization for progress states
- Progress indicator styling
- Spinner customization
- Content changes during progress
- Theme integration
📄 Read: references/progress-button-advanced-features.md
- Accessibility features
- Trace events for debugging
- Native event handling
- State management patterns
- Error handling
- Best practices for progress buttons
---
Speed Dial Component
📄 Read: references/speeddial-features.md
- Getting started and installation
- Basic SpeedDial implementation
- Display modes (Linear, Radial)
- Action items configuration
- Position options (9 positions)
- Open and close behavior
- Item click events
- Open modes (Hover, Click)
- Direction options (Up, Down, Left, Right, Auto)
- Radial settings (offset, direction, startAngle, endAngle)
- Icon configuration (open/close icons)
📄 Read: references/speeddial-styling.md
- Appearance customization
- Icons (open icon, close icon, item icons)
- Color variations
- Item templates
- CSS class customization
- Animation effects
- Theme integration
📄 Read: references/speeddial-advanced-features.md
- Accessibility support
- Keyboard navigation
- Event handling (all events)
- State management patterns
- Modal backdrop
- Best practices
---
Split Button Component
📄 Read: references/split-button-features.md
- Getting started and installation
- Basic split button implementation
- Dropdown menu items configuration
- Primary button action
- Item selection events
- Icons in items
- Separator support
- Opening and closing behavior
- Event handling
📄 Read: references/split-button-styling.md
- Appearance customization
- Button styles
- Icons and icon positioning
- Size variations
- Custom CSS classes
- RTL support
- Theme integration
📄 Read: references/split-button-advanced-features.md
- Accessibility features
- Disabled state handling
- Keyboard navigation
- Native event handling
- Best practices
---
Toggle Switch Component
📄 Read: references/toggle-switch-features.md
- Getting started and installation
- Basic toggle switch implementation
- Checked and unchecked states
- Two-way data binding (@bind-Checked)
- On/Off label configuration
- Change events
- Value binding
- Name attribute for forms
- Default state configuration
📄 Read: references/toggle-switch-styling.md
- Appearance customization
- Size variations
- Label customization (OnLabel, OffLabel)
- Color schemes
- Custom CSS classes
- RTL support
- Material theme considerations (no text support)
- Theme integration
📄 Read: references/toggle-switch-advanced-features.md
- Accessibility support
- Keyboard navigation
- Disabled state handling
- Form integration
- State management patterns
- Best practices
---
Common Patterns Across Components
Event Handling Pattern
All button components support standard Blazor event handling:
<SfButton OnClick="HandleClick">Click Me</SfButton>
@code {
private void HandleClick()
{
// Handle button click
}
}Styling Pattern
All components support CSS class customization:
<SfButton CssClass="e-primary custom-button">Styled Button</SfButton>Icon Integration Pattern
Most components support icon CSS classes:
<SfButton IconCss="e-icons e-plus-icon">Add Item</SfButton>Disabled State Pattern
All interactive components support disabled state:
<SfButton Disabled="@isDisabled">Button</SfButton>RTL Support Pattern
All components support right-to-left rendering:
<SfButton EnableRtl="true">RTL Button</SfButton>---
Shared Configuration
Theme Options
All button components support Syncfusion themes:
- Bootstrap5 (default):
bootstrap5.css - Material:
material.css - Fabric:
fabric.css - Tailwind:
tailwind.css - Fluent:
fluent.css
Common CSS Classes
Standard CSS utility classes work across components:
- e-primary: Primary action styling
- e-success: Success/positive action
- e-info: Informational action
- e-warning: Warning/caution action
- e-danger: Danger/negative action
- e-link: Hyperlink styling
- e-small: Small size variant
- e-flat: Flat style (no background)
- e-outline: Outline style (border only)
- e-round: Circular/rounded style
Accessibility Features
All components include:
- ARIA attributes
- Keyboard navigation support
- Screen reader compatibility
- Focus indicators
- WCAG 2.1 compliance
---
Common Use Cases
Standard Actions
Use Button for primary, secondary, and tertiary actions in forms, dialogs, and pages.
Multi-Selection
Use CheckBox for multiple independent selections, list items, and preferences.
Exclusive Selection
Use RadioButton for single selection from multiple options, preferences, settings, and survey questions.
Grouped Actions
Use ButtonGroup for related actions like text alignment, view modes, or filter options.
Tags and Filters
Use Chip for tags, contact lists, filter selections, and removable items.
Menu Actions
Use DropdownMenu for contextual menus with multiple options.
Dual Actions
Use SplitButton when you need a primary action with additional options.
Long Operations
Use ProgressButton for operations that take time (file upload, form submission, API calls).
Primary Page Actions
Use FloatingActionButton for the primary action on mobile or dashboard pages.
Multiple Quick Actions
Use SpeedDial when you need multiple related actions accessible from a single FAB.
On/Off States
Use ToggleSwitch for binary settings, feature toggles, and preferences.
---
Quick Start Examples
Basic Button
<SfButton CssClass="e-primary">Primary Action</SfButton>Basic CheckBox
<SfCheckBox TChecked="bool" Label="Accept Terms"></SfCheckBox>CheckBox with Two-Way Binding
<SfCheckBox @bind-Checked="isEnabled" Label="Enable Feature"></SfCheckBox>
@code {
private bool isEnabled = false;
}Multiple Checkboxes
<SfCheckBox TChecked="bool" Label="Option 1"></SfCheckBox>
<SfCheckBox TChecked="bool" Label="Option 2"></SfCheckBox>
<SfCheckBox TChecked="bool" Label="Option 3"></SfCheckBox>Tristate CheckBox
<SfCheckBox EnableTriState="true" Checked="null" Label="Select All" TChecked="bool?"></SfCheckBox>Basic RadioButton
<SfRadioButton Label="Option 1" Name="group" Value="option1" @bind-Checked="@selected"></SfRadioButton>
<SfRadioButton Label="Option 2" Name="group" Value="option2" @bind-Checked="@selected"></SfRadioButton>
<SfRadioButton Label="Option 3" Name="group" Value="option3" @bind-Checked="@selected"></SfRadioButton>
@code {
private string selected = "option1";
}RadioButton Group
<h4>Choose Your Plan</h4>
<SfRadioButton Label="Free" Name="plan" Value="free" @bind-Checked="@plan"></SfRadioButton>
<SfRadioButton Label="Pro" Name="plan" Value="pro" @bind-Checked="@plan"></SfRadioButton>
<SfRadioButton Label="Enterprise" Name="plan" Value="enterprise" @bind-Checked="@plan"></SfRadioButton>
@code {
private string plan = "free";
}Button with Icon
<SfButton IconCss="e-icons e-plus-icon" CssClass="e-primary">
Add New
</SfButton>Toggle Switch
<SfSwitch @bind-Checked="isEnabled" OnLabel="On" OffLabel="Off"></SfSwitch>
@code {
private bool isEnabled = true;
}Dropdown Menu
<SfDropDownButton Content="Actions">
<DropDownMenuItems>
<DropDownMenuItem Text="Edit"></DropDownMenuItem>
<DropDownMenuItem Text="Delete"></DropDownMenuItem>
</DropDownMenuItems>
</SfDropDownButton>Floating Action Button
<div id="target" style="min-height:200px; position:relative;">
<SfFab Target="#target" IconCss="e-icons e-plus" Content="Add"></SfFab>
</div>Chip Collection
<SfChip EnableDelete="true">
<ChipItems>
<ChipItem Text="Tag 1"></ChipItem>
<ChipItem Text="Tag 2"></ChipItem>
<ChipItem Text="Tag 3"></ChipItem>
</ChipItems>
</SfChip>---
Navigation Tips
- For installation and basic setup, refer to any component's features file
- For styling and appearance, refer to the styling files for each component
- For accessibility and advanced features, refer to the advanced-features files
- For specific component features, navigate to that component's documentation sections above
---
Support and Resources
- Official Demos: https://blazor.syncfusion.com/demos/buttons/
- API Documentation: https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.Buttons.html
- GitHub Examples: https://github.com/SyncfusionExamples/Blazor-Getting-Started-Examples/
- System Requirements: https://blazor.syncfusion.com/documentation/system-requirements
- Licensing: Commercial license required for production use
---
Remember: Always start with the appropriate component's features file for implementation guidance, then move to styling and advanced features as needed.
Button Component - Advanced Features
Table of Contents
- Accessibility Features
- Disabled State Handling
- Toggle Button Functionality
- Server vs Web App Differences
- Performance Optimization
- Best Practices
- Testing Strategies
- Security Considerations
---
Accessibility Features
WCAG Compliance
The Syncfusion Blazor Button component is designed to be WCAG 2.1 Level AA compliant.
Keyboard Navigation
Buttons support standard keyboard interactions:
| Key | Action |
|---|---|
| Enter | Activates the button |
| Space | Activates the button |
| Tab | Moves focus to next focusable element |
| Shift+Tab | Moves focus to previous focusable element |
<SfButton OnClick="HandleClick" CssClass="e-primary">
Keyboard Accessible Button
</SfButton>
@code {
private void HandleClick()
{
Console.WriteLine("Button activated via mouse or keyboard");
}
}ARIA Attributes
aria-label
Provide accessible names for icon-only buttons:
<SfButton HtmlAttributes="@ariaLabel"
IconCss="e-icons e-close-icon"
CssClass="e-round">
</SfButton>
@code {
private Dictionary<string, object> ariaLabel = new()
{
{ "aria-label", "Close dialog" }
};
}
<style>
.e-close-icon::before { content: '\e7fc'; }
</style>aria-pressed
For toggle buttons, indicate pressed state:
<SfButton HtmlAttributes="@toggleAttributes"
IsToggle="true"
@onclick="ToggleState"
CssClass="@(isPressed ? "e-active" : "")">
@(isPressed ? "Pressed" : "Not Pressed")
</SfButton>
@code {
private bool isPressed = false;
private Dictionary<string, object> toggleAttributes => new()
{
{ "aria-pressed", isPressed.ToString().ToLower() }
};
private void ToggleState()
{
isPressed = !isPressed;
}
}aria-disabled
For disabled buttons:
<SfButton Disabled="@isDisabled"
HtmlAttributes="@disabledAttributes">
Disabled Button
</SfButton>
@code {
private bool isDisabled = true;
private Dictionary<string, object> disabledAttributes => new()
{
{ "aria-disabled", "true" }
};
}aria-describedby
Provide additional context:
<SfButton HtmlAttributes="@describedBy" CssClass="e-danger">
Delete Account
</SfButton>
<div id="delete-description" style="display: none;">
This action cannot be undone. All your data will be permanently deleted.
</div>
@code {
private Dictionary<string, object> describedBy = new()
{
{ "aria-describedby", "delete-description" }
};
}Screen Reader Support
Meaningful Button Text
<!-- Good: Descriptive -->
<SfButton>Submit Application</SfButton>
<!-- Bad: Generic -->
<SfButton>Click Here</SfButton>Context for Icon Buttons
<SfButton IconCss="e-icons e-save-icon"
HtmlAttributes="@saveLabel">
<span class="visually-hidden">Save</span>
</SfButton>
@code {
private Dictionary<string, object> saveLabel = new()
{
{ "aria-label", "Save document" }
};
}
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.e-save-icon::before { content: '\e74e'; }
</style>Color Contrast
Ensure WCAG AA compliance (4.5:1 for normal text, 3:1 for large text):
<SfButton CssClass="high-contrast e-primary">
High Contrast Button
</SfButton>
<style>
.high-contrast.e-btn {
/* Ensure sufficient contrast */
background-color: #0066cc; /* Dark enough for white text */
color: #ffffff;
}
.high-contrast.e-btn:focus {
outline: 3px solid #ffbf47; /* Visible focus indicator */
outline-offset: 2px;
}
</style>Focus Indicators
Visible Focus State
<SfButton CssClass="custom-focus e-primary">
Focus Indicator
</SfButton>
<style>
.custom-focus.e-btn:focus {
outline: 3px solid #4A90E2;
outline-offset: 3px;
box-shadow: 0 0 0 4px rgba(74, 144, 226, 0.2);
}
.custom-focus.e-btn:focus:not(:focus-visible) {
outline: none;
box-shadow: none;
}
</style>Skip Link Styling
<SfButton CssClass="skip-link" @onclick="SkipToContent">
Skip to main content
</SfButton>
@code {
private void SkipToContent()
{
// Navigate to main content
}
}
<style>
.skip-link.e-btn {
position: absolute;
top: -40px;
left: 0;
z-index: 100;
}
.skip-link.e-btn:focus {
top: 0;
}
</style>Role and Live Regions
For dynamic content updates:
<SfButton OnClick="LoadContent">Load Content</SfButton>
<div role="status" aria-live="polite" aria-atomic="true">
@if (isLoading)
{
<span>Loading content...</span>
}
else if (content != null)
{
<span>Content loaded successfully</span>
}
</div>
@code {
private bool isLoading = false;
private string content = null;
private async Task LoadContent()
{
isLoading = true;
await Task.Delay(2000);
content = "New content";
isLoading = false;
}
}---
Disabled State Handling
Basic Disabled State
<SfButton Disabled="@isDisabled" CssClass="e-primary">
@(isDisabled ? "Disabled" : "Enabled")
</SfButton>
<SfButton OnClick="ToggleDisabled">
Toggle State
</SfButton>
@code {
private bool isDisabled = true;
private void ToggleDisabled()
{
isDisabled = !isDisabled;
}
}Conditional Disabling
<EditForm Model="@formModel" OnValidSubmit="HandleSubmit">
<DataAnnotationsValidator />
<InputText @bind-Value="formModel.Email" />
<SfButton Disabled="@(!context.Validate())"
CssClass="e-primary">
Submit
</SfButton>
</EditForm>
@code {
private FormModel formModel = new();
private void HandleSubmit()
{
// Process form
}
public class FormModel
{
[Required, EmailAddress]
public string Email { get; set; }
}
}Async Operation Disabling
<SfButton Disabled="@isProcessing"
OnClick="ProcessData"
CssClass="e-primary">
@(isProcessing ? "Processing..." : "Process Data")
</SfButton>
@code {
private bool isProcessing = false;
private async Task ProcessData()
{
isProcessing = true;
try
{
await Task.Delay(3000); // Simulate async operation
// Process data
}
finally
{
isProcessing = false;
}
}
}Custom Disabled Styling
<SfButton Disabled="@isDisabled" CssClass="custom-disabled e-primary">
Disabled Button
</SfButton>
<style>
.custom-disabled.e-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
background-color: #cccccc;
color: #666666;
}
</style>
@code {
private bool isDisabled = true;
}---
Toggle Button Functionality
Basic Toggle Button
<SfButton IsToggle="true"
@onclick="OnToggle"
CssClass="@(isToggled ? "e-active" : "")">
@(isToggled ? "ON" : "OFF")
</SfButton>
@code {
private bool isToggled = false;
private void OnToggle()
{
isToggled = !isToggled;
}
}Toggle with Icon Change
<SfButton IsToggle="true"
IconCss="@iconCss"
Content="@content"
@onclick="TogglePlayPause"
@ref="toggleBtn"
CssClass="e-primary">
</SfButton>
@code {
private SfButton toggleBtn;
private string iconCss = "e-icons e-play";
private string content = "Play";
private void TogglePlayPause()
{
if (content == "Play")
{
content = "Pause";
iconCss = "e-icons e-pause";
}
else
{
content = "Play";
iconCss = "e-icons e-play";
}
}
}
<style>
.e-play::before { content: '\e324'; }
.e-pause::before { content: '\e326'; }
</style>Toggle Group
<div class="toggle-group">
@foreach (var option in options)
{
<SfButton IsToggle="true"
@onclick="() => SelectOption(option)"
CssClass="@(selectedOption == option ? "e-active" : "")">
@option
</SfButton>
}
</div>
@code {
private string[] options = { "Option 1", "Option 2", "Option 3" };
private string selectedOption = "Option 1";
private void SelectOption(string option)
{
selectedOption = option;
}
}
<style>
.toggle-group {
display: flex;
gap: 5px;
}
</style>State Persistence
@inject ProtectedLocalStorage LocalStorage
<SfButton IsToggle="true"
@onclick="ToggleWithPersistence"
CssClass="@(isToggled ? "e-active" : "")">
@(isToggled ? "Enabled" : "Disabled")
</SfButton>
@code {
private bool isToggled = false;
protected override async Task OnInitializedAsync()
{
var result = await LocalStorage.GetAsync<bool>("toggleState");
isToggled = result.Success ? result.Value : false;
}
private async Task ToggleWithPersistence()
{
isToggled = !isToggled;
await LocalStorage.SetAsync("toggleState", isToggled);
}
}---
Server vs Web App Differences
WebAssembly Specific
@* WebAssembly App *@
<SfButton OnClick="ClientSideClick">
Client-Side Click
</SfButton>
@code {
private void ClientSideClick()
{
// Executes entirely on client
Console.WriteLine("Client-side execution");
}
}Server-Side Rendering
@* Blazor Server *@
<SfButton OnClick="ServerSideClick">
Server-Side Click
</SfButton>
@code {
private async Task ServerSideClick()
{
// Executes on server, UI updates via SignalR
await Task.Delay(100);
Console.WriteLine("Server-side execution");
}
}Prerendering Considerations
@inject NavigationManager Navigation
<SfButton OnClick="HandleClick" Disabled="@isPrerendering">
@buttonText
</SfButton>
@code {
private bool isPrerendering = true;
private string buttonText = "Loading...";
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
isPrerendering = false;
buttonText = "Click Me";
StateHasChanged();
}
}
private void HandleClick()
{
// Handle click
}
}---
Performance Optimization
Debouncing Clicks
<SfButton OnClick="DebouncedClick">
Debounced Button
</SfButton>
@code {
private System.Threading.Timer debounceTimer;
private const int DebounceDelay = 300;
private void DebouncedClick()
{
debounceTimer?.Dispose();
debounceTimer = new System.Threading.Timer(_ =>
{
InvokeAsync(() =>
{
// Execute action
Console.WriteLine("Debounced action executed");
StateHasChanged();
});
}, null, DebounceDelay, Timeout.Infinite);
}
public void Dispose()
{
debounceTimer?.Dispose();
}
}Throttling Clicks
<SfButton OnClick="ThrottledClick">
Throttled Button
</SfButton>
@code {
private DateTime lastClickTime = DateTime.MinValue;
private const int ThrottleMs = 1000;
private void ThrottledClick()
{
var now = DateTime.Now;
if ((now - lastClickTime).TotalMilliseconds >= ThrottleMs)
{
lastClickTime = now;
// Execute action
Console.WriteLine("Throttled action executed");
}
}
}Prevent Double-Click
<SfButton OnClick="PreventDoubleClick"
Disabled="@isProcessing">
@(isProcessing ? "Processing..." : "Submit")
</SfButton>
@code {
private bool isProcessing = false;
private async Task PreventDoubleClick()
{
if (isProcessing) return;
isProcessing = true;
try
{
await ProcessAsync();
}
finally
{
isProcessing = false;
}
}
private async Task ProcessAsync()
{
await Task.Delay(2000);
}
}Virtualization for Many Buttons
@using Microsoft.AspNetCore.Components.Web.Virtualization
<Virtualize Items="@buttons" Context="button">
<SfButton OnClick="() => HandleClick(button.Id)">
@button.Text
</SfButton>
</Virtualize>
@code {
private List<ButtonModel> buttons = Enumerable.Range(1, 1000)
.Select(i => new ButtonModel { Id = i, Text = $"Button {i}" })
.ToList();
private void HandleClick(int id)
{
Console.WriteLine($"Button {id} clicked");
}
public class ButtonModel
{
public int Id { get; set; }
public string Text { get; set; }
}
}---
Best Practices
1. Use Semantic Button Types
<!-- Good: Semantic meaning -->
<SfButton CssClass="e-primary">Primary Action</SfButton>
<SfButton CssClass="e-danger">Delete</SfButton>
<!-- Avoid: Generic styling -->
<SfButton CssClass="blue-button">Action</SfButton>2. Provide Clear Labels
<!-- Good: Clear purpose -->
<SfButton>Save Changes</SfButton>
<SfButton>Download Report</SfButton>
<!-- Avoid: Vague labels -->
<SfButton>OK</SfButton>
<SfButton>Click Here</SfButton>3. Handle Loading States
<SfButton OnClick="LoadData"
Disabled="@isLoading"
IconCss="@(isLoading ? "e-icons e-spinner" : "")">
@(isLoading ? "Loading..." : "Load Data")
</SfButton>
@code {
private bool isLoading = false;
private async Task LoadData()
{
isLoading = true;
try
{
await FetchDataAsync();
}
finally
{
isLoading = false;
}
}
private async Task FetchDataAsync()
{
await Task.Delay(2000);
}
}4. Confirm Destructive Actions
<SfButton OnClick="ConfirmDelete" CssClass="e-danger">
Delete Account
</SfButton>
@code {
[Inject] private IDialogService DialogService { get; set; }
private async Task ConfirmDelete()
{
var confirmed = await DialogService.ConfirmAsync(
"Are you sure you want to delete your account?",
"Confirm Deletion"
);
if (confirmed)
{
await DeleteAccount();
}
}
private async Task DeleteAccount()
{
// Delete logic
}
}5. Group Related Actions
<div class="button-group">
<SfButton CssClass="e-primary">Save</SfButton>
<SfButton>Cancel</SfButton>
</div>
<style>
.button-group {
display: flex;
gap: 10px;
justify-content: flex-end;
}
</style>---
Testing Strategies
Unit Testing
[Fact]
public void Button_Click_InvokesCallback()
{
// Arrange
var clicked = false;
var component = RenderComponent<SfButton>(parameters => parameters
.Add(p => p.OnClick, () => clicked = true)
);
// Act
component.Find("button").Click();
// Assert
Assert.True(clicked);
}Integration Testing
[Fact]
public async Task Button_DisabledDuringAsyncOperation()
{
// Arrange
var component = RenderComponent<MyComponent>();
var button = component.Find("button");
// Act
button.Click();
// Assert - Button should be disabled
Assert.True(button.HasAttribute("disabled"));
// Wait for operation
await Task.Delay(100);
// Assert - Button should be enabled again
Assert.False(button.HasAttribute("disabled"));
}Accessibility Testing
[Fact]
public void Button_HasProperAriaLabel()
{
// Arrange & Act
var component = RenderComponent<IconButton>();
var button = component.Find("button");
// Assert
Assert.Equal("Close dialog", button.GetAttribute("aria-label"));
}---
Security Considerations
Prevent CSRF in Forms
<EditForm Model="@model" OnValidSubmit="HandleSubmit">
<AntiforgeryToken />
<SfButton HtmlAttributes="@submitAttrs" CssClass="e-primary">
Submit
</SfButton>
</EditForm>
@code {
private Model model = new();
private Dictionary<string, object> submitAttrs = new() { { "type", "submit" } };
private async Task HandleSubmit()
{
// Form submission with CSRF token
}
}Validate User Permissions
@if (hasPermission)
{
<SfButton OnClick="DeleteItem" CssClass="e-danger">
Delete
</SfButton>
}
@code {
[CascadingParameter] private User CurrentUser { get; set; }
private bool hasPermission => CurrentUser?.HasRole("Admin") ?? false;
private async Task DeleteItem()
{
// Verify permission on server-side as well
}
}Rate Limiting
<SfButton OnClick="RateLimitedAction" Disabled="@isRateLimited">
Submit
</SfButton>
@code {
private bool isRateLimited = false;
private int clickCount = 0;
private DateTime windowStart = DateTime.Now;
private const int MaxClicks = 5;
private const int WindowSeconds = 60;
private async Task RateLimitedAction()
{
if ((DateTime.Now - windowStart).TotalSeconds > WindowSeconds)
{
clickCount = 0;
windowStart = DateTime.Now;
}
clickCount++;
if (clickCount > MaxClicks)
{
isRateLimited = true;
return;
}
// Perform action
}
}---
Common Advanced Scenarios
Button with Confirmation Dialog
@inject IJSRuntime JSRuntime
<SfButton OnClick="DeleteWithConfirmation" CssClass="e-danger">
Delete
</SfButton>
@code {
private async Task DeleteWithConfirmation()
{
bool confirmed = await JSRuntime.InvokeAsync<bool>(
"confirm",
"Are you sure you want to delete this item?"
);
if (confirmed)
{
await DeleteItem();
}
}
private async Task DeleteItem()
{
// Delete logic
}
}Button with Progress Tracking
<SfButton OnClick="ProcessWithProgress" Disabled="@isProcessing">
Process (@progress%)
</SfButton>
@code {
private bool isProcessing = false;
private int progress = 0;
private async Task ProcessWithProgress()
{
isProcessing = true;
progress = 0;
for (int i = 0; i <= 100; i += 10)
{
progress = i;
StateHasChanged();
await Task.Delay(200);
}
isProcessing = false;
}
}---
Next Steps
- Review button-features.md for basic functionality
- Explore button-styling.md for customization options
Button Component - Features
Table of Contents
- Getting Started
- Installation
- Basic Button Implementation
- Button Types
- Button Styles
- Icons and Icon Positioning
- Button Sizes
- Repeat Button
- Tooltips
- HTML Attribute Support
- Native Event Handling
- Common Scenarios
- Troubleshooting
---
Getting Started
The Blazor Button component provides a standard clickable button with extensive customization options including types, styles, icons, sizes, and states.
Prerequisites
- .NET SDK 6.0 or later
- Blazor WebAssembly or Blazor Server application
- Visual Studio 2022 or VS Code
---
Installation
Step 1: Install NuGet Packages
dotnet add package Syncfusion.Blazor.Buttons
dotnet add package Syncfusion.Blazor.ThemesOr via Package Manager Console:
Install-Package Syncfusion.Blazor.Buttons
Install-Package Syncfusion.Blazor.ThemesStep 2: Add Namespaces
Open ~/_Imports.razor and add:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.ButtonsStep 3: Register Syncfusion Service
In ~/Program.cs:
using Syncfusion.Blazor;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient {
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
// Register Syncfusion Blazor service
builder.Services.AddSyncfusionBlazor();
await builder.Build().RunAsync();Step 4: Add Theme and Script References
In ~/index.html (WebAssembly) or ~/Pages/_Host.cshtml (Server):
<head>
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"
type="text/javascript"></script>
</head>---
API Reference
SfButton Properties
Content Properties:
Content(string) - Button text contentChildContent(RenderFragment) - Child content for custom markup
Icon Properties:
IconCss(string) - CSS class for button iconIconPosition(IconPosition enum) - Icon position: Left (default), Right, Top, Bottom
Appearance Properties:
CssClass(string) - Custom CSS classes for stylingIsPrimary(bool) - Apply primary button stylingIsToggle(bool) - Enable toggle button behavior
State Properties:
Disabled(bool) - Disable the buttonEnableRtl(bool) - Enable right-to-left rendering
Additional Properties:
HtmlAttributes(Dictionary<string, object>) - Custom HTML attributes
Events:
OnClick(EventCallback<MouseEventArgs>) - Click event handlerCreated(EventCallback<Object>) - Component created event
IconPosition Enum Values
Left- Icon on the left (default)Right- Icon on the rightTop- Icon on topBottom- Icon on bottom
Common CSS Classes
e-primary- Primary button stylee-success- Success/green stylee-info- Info/blue stylee-warning- Warning/orange stylee-danger- Danger/red stylee-link- Link stylee-flat- Flat style (no background)e-outline- Outline stylee-round- Round/circular stylee-small- Small sizee-block- Full width block button
Basic Button Implementation
Simple Button
<SfButton>Click Me</SfButton>Button with Click Event
<SfButton OnClick="HandleClick">Submit</SfButton>
@code {
private void HandleClick()
{
Console.WriteLine("Button clicked!");
}
}Button with Two-Way Binding
<SfButton @onclick="IncrementCount">
Count: @currentCount
</SfButton>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}---
Button Types
The Button component supports multiple visual types through the CssClass property.
Flat Button
Button with no background color:
<SfButton CssClass="e-flat">Flat Button</SfButton>Outline Button
Button with border and transparent background:
<SfButton CssClass="e-outline">Outline Button</SfButton>Round Button
Circular button (usually contains only an icon):
<SfButton CssClass="e-round" IconCss="e-icons e-plus-icon"></SfButton>
<style>
.e-plus-icon::before {
content: '\e823';
}
</style>Primary Button
Button with prominent background (using IsPrimary property):
<SfButton IsPrimary="true">Primary Button</SfButton>Toggle Button
Button that maintains an active/inactive state:
<SfButton CssClass="e-flat"
IsPrimary="true"
IconCss="@IconCss"
Content="@Content"
IsToggle="true"
@onclick="OnToggleClick"
@ref="ToggleBtnObj">
</SfButton>
@code {
SfButton ToggleBtnObj;
public string IconCss = "e-icons e-play";
public string Content = "Play";
public void OnToggleClick()
{
if(ToggleBtnObj.Content == "Play")
{
this.Content = "Pause";
this.IconCss = "e-icons e-pause";
}
else
{
this.Content = "Play";
this.IconCss = "e-icons e-play";
}
}
}
<style>
.e-play::before { content: '\e324'; }
.e-pause::before { content: '\e326'; }
</style>Multiple Types Example
<div class="button-group">
<SfButton>Normal Button</SfButton>
<SfButton IsPrimary="true">Primary</SfButton>
<SfButton CssClass="e-flat">Flat</SfButton>
<SfButton CssClass="e-outline">Outline</SfButton>
<SfButton CssClass="e-round" IconCss="e-icons e-add-icon"></SfButton>
</div>
<style>
.button-group { display: flex; gap: 10px; }
.e-add-icon::before { content: '\e823'; }
</style>---
Button Styles
Predefined styles provide semantic meaning through the CssClass property.
Available Style Classes
| Class | Purpose | Visual |
|---|---|---|
e-primary | Primary action | Blue background |
e-success | Positive action | Green background |
e-info | Informational action | Light blue background |
e-warning | Caution action | Orange background |
e-danger | Negative action | Red background |
e-link | Hyperlink style | No background, colored text |
Style Examples
<SfButton CssClass="e-primary">Primary</SfButton>
<SfButton CssClass="e-success">Success</SfButton>
<SfButton CssClass="e-info">Info</SfButton>
<SfButton CssClass="e-warning">Warning</SfButton>
<SfButton CssClass="e-danger">Danger</SfButton>
<SfButton CssClass="e-link">Link</SfButton>Combining Styles with Types
<!-- Outline Primary Button -->
<SfButton CssClass="e-primary e-outline">Primary Outline</SfButton>
<!-- Flat Success Button -->
<SfButton CssClass="e-success e-flat">Success Flat</SfButton>
<!-- Round Danger Button -->
<SfButton CssClass="e-danger e-round" IconCss="e-icons e-delete-icon"></SfButton>Complete Style Grid Example
<div class="style-grid">
<div class="row">
<SfButton CssClass="e-primary">Primary</SfButton>
<SfButton CssClass="e-primary e-outline">Primary Outline</SfButton>
<SfButton CssClass="e-primary e-flat">Primary Flat</SfButton>
</div>
<div class="row">
<SfButton CssClass="e-success">Success</SfButton>
<SfButton CssClass="e-success e-outline">Success Outline</SfButton>
<SfButton CssClass="e-success e-flat">Success Flat</SfButton>
</div>
<div class="row">
<SfButton CssClass="e-danger">Danger</SfButton>
<SfButton CssClass="e-danger e-outline">Danger Outline</SfButton>
<SfButton CssClass="e-danger e-flat">Danger Flat</SfButton>
</div>
</div>
<style>
.style-grid { display: flex; flex-direction: column; gap: 15px; }
.row { display: flex; gap: 10px; }
</style>Important: Style classes provide only visual indication. Always define meaningful content for assistive technologies.
---
Icons and Icon Positioning
Button with Icon
<SfButton IconCss="e-icons e-save-icon">Save</SfButton>
<style>
.e-save-icon::before {
content: '\e74e'; /* Syncfusion icon unicode */
}
</style>Icon Position
Use the IconPosition property to control icon placement:
<!-- Icon on Left (default) -->
<SfButton IconCss="e-icons e-play-icon">Play</SfButton>
<!-- Icon on Right -->
<SfButton IconCss="e-icons e-play-icon" IconPosition="IconPosition.Right">
Play
</SfButton>
<!-- Icon on Top -->
<SfButton IconCss="e-icons e-upload-icon" IconPosition="IconPosition.Top">
Upload
</SfButton>
<!-- Icon on Bottom -->
<SfButton IconCss="e-icons e-download-icon" IconPosition="IconPosition.Bottom">
Download
</SfButton>
<style>
.e-play-icon::before { content: '\e324'; }
.e-upload-icon::before { content: '\e725'; }
.e-download-icon::before { content: '\e736'; }
</style>Icon-Only Button
<!-- Round icon button -->
<SfButton CssClass="e-round e-primary"
IconCss="e-icons e-add-icon">
</SfButton>
<!-- Small icon button -->
<SfButton CssClass="e-small"
IconCss="e-icons e-edit-icon">
</SfButton>Using External Icon Libraries
Font Awesome
<SfButton IconCss="fas fa-save">Save</SfButton>Material Icons
<SfButton IconCss="material-icons">save</SfButton>Custom Icon Styling
<SfButton IconCss="custom-icon" CssClass="e-primary">
Custom Icon
</SfButton>
<style>
.custom-icon {
background: url('images/custom-icon.png') no-repeat center;
width: 16px;
height: 16px;
display: inline-block;
}
</style>---
Button Sizes
Available Sizes
<!-- Small Button -->
<SfButton CssClass="e-small">Small Button</SfButton>
<!-- Normal Button (default) -->
<SfButton>Normal Button</SfButton>
<!-- Block Button (full width) -->
<SfButton CssClass="e-block">Block Button</SfButton>Size Comparison
<div class="size-demo">
<SfButton CssClass="e-small e-primary">Small</SfButton>
<SfButton CssClass="e-primary">Normal</SfButton>
</div>
<div style="margin-top: 20px;">
<SfButton CssClass="e-block e-primary">Block Button</SfButton>
</div>
<style>
.size-demo { display: flex; gap: 10px; align-items: center; }
</style>Custom Size with CSS
<SfButton CssClass="custom-large">Large Custom Button</SfButton>
<style>
.custom-large.e-btn {
padding: 12px 24px;
font-size: 18px;
min-height: 48px;
}
</style>---
Custom Repeat Button Implementation
Note: SfButton does not have built-in repeat button functionality. Here's how to implement it with custom logic:
Repeat Button with Interval Control
<SfButton @ref="RepeatBtn"
@onmousedown="StartRepeat"
@onmouseup="StopRepeat"
@onmouseleave="StopRepeat">
Count: @repeatCount
</SfButton>
@code {
SfButton RepeatBtn;
private int repeatCount = 0;
private System.Threading.Timer timer;
private void StartRepeat()
{
repeatCount = 0;
if (timer == null)
{
timer = new System.Threading.Timer(_ =>
{
repeatCount++;
InvokeAsync(StateHasChanged);
}, null, 0, 100); // Trigger every 100ms
}
}
private void StopRepeat()
{
timer?.Dispose();
timer = null;
}
public void Dispose()
{
timer?.Dispose();
}
}Implementation Notes:
- Use
@onmousedownto start the timer - Use
@onmouseupand@onmouseleaveto stop the timer - This is a custom implementation, not a built-in feature
- Remember to dispose the timer to prevent memory leaks
---
Tooltips
Using HTML Title Attribute
<SfButton title="Click to submit the form">Submit</SfButton>Using HtmlAttributes Property
<SfButton HtmlAttributes="@buttonAttributes">
Delete
</SfButton>
@code {
private Dictionary<string, object> buttonAttributes = new Dictionary<string, object>
{
{ "title", "Permanently delete this item" }
};
}Integration with Syncfusion Tooltip Component
<div id="tooltip-target">
<SfButton CssClass="e-danger">Delete</SfButton>
</div>
<SfTooltip Target="#tooltip-target" Content="This action cannot be undone">
</SfTooltip>
@code {
@using Syncfusion.Blazor.Popups
}---
HTML Attribute Support
The Button component supports HTML attributes through the HtmlAttributes property.
Adding Custom Attributes
<SfButton HtmlAttributes="@attributes">
Custom Attributes
</SfButton>
@code {
private Dictionary<string, object> attributes = new Dictionary<string, object>
{
{ "data-id", "btn-123" },
{ "aria-label", "Submit form" },
{ "title", "Click to submit" },
{ "data-action", "submit" }
};
}Form-Related Attributes
<EditForm Model="@model" OnValidSubmit="HandleSubmit">
<SfButton HtmlAttributes="@submitAttributes" CssClass="e-primary">
Submit
</SfButton>
</EditForm>
@code {
private FormModel model = new FormModel();
private Dictionary<string, object> submitAttributes = new Dictionary<string, object>
{
{ "type", "submit" },
{ "form", "myForm" },
{ "name", "submitBtn" }
};
private void HandleSubmit()
{
// Handle form submission
}
public class FormModel { }
}Accessibility Attributes
<SfButton HtmlAttributes="@accessibilityAttributes">
Accessible Button
</SfButton>
@code {
private Dictionary<string, object> accessibilityAttributes = new Dictionary<string, object>
{
{ "aria-label", "Submit form" },
{ "aria-describedby", "btn-description" },
{ "role", "button" },
{ "tabindex", "0" }
};
}---
Native Event Handling
Mouse Events
<SfButton @onclick="OnClick"
@onmouseover="OnMouseOver"
@onmouseout="OnMouseOut"
@onmousedown="OnMouseDown"
@onmouseup="OnMouseUp">
Interactive Button
</SfButton>
@code {
private void OnClick() => Console.WriteLine("Clicked");
private void OnMouseOver() => Console.WriteLine("Mouse over");
private void OnMouseOut() => Console.WriteLine("Mouse out");
private void OnMouseDown() => Console.WriteLine("Mouse down");
private void OnMouseUp() => Console.WriteLine("Mouse up");
}Keyboard Events
<SfButton @onkeydown="OnKeyDown"
@onkeyup="OnKeyUp"
@onkeypress="OnKeyPress">
Keyboard Events
</SfButton>
@code {
private void OnKeyDown(KeyboardEventArgs e)
{
Console.WriteLine($"Key down: {e.Key}");
}
private void OnKeyUp(KeyboardEventArgs e)
{
Console.WriteLine($"Key up: {e.Key}");
}
private void OnKeyPress(KeyboardEventArgs e)
{
Console.WriteLine($"Key press: {e.Key}");
}
}Focus Events
<SfButton @onfocus="OnFocus"
@onblur="OnBlur">
Focus Events
</SfButton>
@code {
private void OnFocus() => Console.WriteLine("Button focused");
private void OnBlur() => Console.WriteLine("Button blurred");
}Event Arguments
<SfButton OnClick="HandleClickWithArgs">Click Me</SfButton>
@code {
private void HandleClickWithArgs(MouseEventArgs e)
{
Console.WriteLine($"Button: {e.Button}");
Console.WriteLine($"X: {e.ClientX}, Y: {e.ClientY}");
Console.WriteLine($"Ctrl: {e.CtrlKey}, Shift: {e.ShiftKey}");
}
}---
Common Scenarios
Submit Button in Form
<EditForm Model="@formData" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label>Name:</label>
<InputText @bind-Value="formData.Name" class="form-control" />
</div>
<SfButton CssClass="e-primary" HtmlAttributes="@submitAttrs">
Submit
</SfButton>
</EditForm>
@code {
private FormData formData = new FormData();
private Dictionary<string, object> submitAttrs = new() { { "type", "submit" } };
private void HandleValidSubmit()
{
Console.WriteLine($"Form submitted: {formData.Name}");
}
public class FormData
{
[Required]
public string Name { get; set; }
}
}Async Button Operation
<SfButton OnClick="HandleAsyncOperation" Disabled="@isLoading">
@(isLoading ? "Loading..." : "Load Data")
</SfButton>
@if (data != null)
{
<p>Data: @data</p>
}
@code {
private bool isLoading = false;
private string data = null;
private async Task HandleAsyncOperation()
{
isLoading = true;
try
{
data = await FetchDataAsync();
}
finally
{
isLoading = false;
}
}
private async Task<string> FetchDataAsync()
{
await Task.Delay(2000); // Simulate API call
return "Fetched data";
}
}Conditional Button Rendering
@if (showPrimaryAction)
{
<SfButton CssClass="e-primary" OnClick="HandlePrimaryAction">
Primary Action
</SfButton>
}
else
{
<SfButton CssClass="e-secondary" OnClick="HandleSecondaryAction">
Secondary Action
</SfButton>
}
@code {
private bool showPrimaryAction = true;
private void HandlePrimaryAction() => showPrimaryAction = false;
private void HandleSecondaryAction() => showPrimaryAction = true;
}---
Troubleshooting
Button Not Responding to Clicks
Problem: Button click event not firing.
Solutions: 1. Ensure OnClick is properly bound 2. Check if button is disabled 3. Verify no CSS z-index issues 4. Check JavaScript console for errors
<!-- Correct -->
<SfButton OnClick="HandleClick">Click Me</SfButton>
<!-- Incorrect - missing @ symbol -->
<SfButton OnClick="HandleClick">Click Me</SfButton>Icons Not Displaying
Problem: Icons show as empty boxes or don't appear.
Solutions: 1. Verify icon CSS is defined 2. Check Syncfusion script reference 3. Ensure icon unicode is correct 4. Verify CSS class names
<!-- Correct icon definition -->
<SfButton IconCss="e-icons e-custom-icon">Button</SfButton>
<style>
.e-custom-icon::before {
content: '\e823'; /* Valid unicode */
}
</style>Styling Not Applied
Problem: Custom CSS classes not working.
Solutions: 1. Use ::deep or :global for style penetration 2. Increase CSS specificity 3. Verify CssClass property syntax
/* Correct - using ::deep */
::deep .custom-button.e-btn {
background-color: purple;
}
/* Correct - global scope */
:global(.custom-button.e-btn) {
background-color: purple;
}Button Not Showing in Form
Problem: Button submits form unintentionally.
Solution: Add type="button" attribute:
<SfButton HtmlAttributes="@buttonAttrs" OnClick="HandleClick">
Don't Submit
</SfButton>
@code {
private Dictionary<string, object> buttonAttrs = new()
{
{ "type", "button" }
};
}Performance Issues with Many Buttons
Problem: Page slow with numerous buttons.
Solutions: 1. Use virtualization for lists 2. Implement lazy loading 3. Optimize event handlers 4. Consider ButtonGroup for related actions
---
Best Practices
1. Use semantic styles - Choose button styles that match action intent (e-primary for main actions, e-danger for destructive actions)
2. Provide clear labels - Use descriptive text instead of generic labels like "Click Here"
3. Add icons for clarity - Icons help users quickly identify button purpose
4. Handle loading states - Disable buttons during async operations and show loading indicators
5. Implement keyboard support - Ensure buttons are keyboard accessible (they are by default)
6. Use appropriate sizes - Small buttons for dense UIs, block buttons for mobile
7. Avoid overusing primary style - Only one primary action per section
8. Test accessibility - Verify with screen readers and keyboard navigation
9. Provide visual feedback - Use :hover, :active, and :focus states effectively
10. Consider mobile touch targets - Ensure buttons are at least 44x44px on mobile
---
Next Steps
- Explore button-styling.md for advanced appearance customization
- Learn about button-advanced-features.md for accessibility and optimization
Button Group Component - Advanced Features
Table of Contents
- Accessibility Features
- Keyboard Navigation
- Selection State Management
- Event Handling Patterns
- Performance Optimization
- Best Practices
- Testing Strategies
- Common Patterns
---
Accessibility Features
ARIA Attributes
<SfButtonGroup HtmlAttributes="@groupAttributes" Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => Select(1)"
Selected="@(selected == 1)"
HtmlAttributes="@GetButtonAttributes(1)">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => Select(2)"
Selected="@(selected == 2)"
HtmlAttributes="@GetButtonAttributes(2)">
Option 2
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
private Dictionary<string, object> groupAttributes = new()
{
{ "role", "group" },
{ "aria-label", "Selection options" }
};
private Dictionary<string, object> GetButtonAttributes(int option)
{
return new Dictionary<string, object>
{
{ "aria-pressed", (selected == option).ToString().ToLower() },
{ "aria-label", $"Option {option}" }
};
}
private void Select(int option) => selected = option;
}Screen Reader Support
<SfButtonGroup HtmlAttributes="@groupAttrs">
<ButtonGroupButton IconCss="e-icons e-bold"
HtmlAttributes="@boldAttrs">
<span class="sr-only">Bold</span>
</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-italic"
HtmlAttributes="@italicAttrs">
<span class="sr-only">Italic</span>
</ButtonGroupButton>
</SfButtonGroup>
@code {
private Dictionary<string, object> groupAttrs = new()
{
{ "role", "toolbar" },
{ "aria-label", "Text formatting" }
};
private Dictionary<string, object> boldAttrs = new()
{
{ "aria-label", "Bold text" },
{ "title", "Bold (Ctrl+B)" }
};
private Dictionary<string, object> italicAttrs = new()
{
{ "aria-label", "Italic text" },
{ "title", "Italic (Ctrl+I)" }
};
}
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
.e-bold::before { content: '\e736'; }
.e-italic::before { content: '\e737'; }
</style>Focus Management
<SfButtonGroup CssClass="focus-styled">
<ButtonGroupButton @ref="firstButton">First</ButtonGroupButton>
<ButtonGroupButton>Second</ButtonGroupButton>
<ButtonGroupButton>Third</ButtonGroupButton>
</SfButtonGroup>
<SfButton OnClick="FocusFirst">Focus First Button</SfButton>
@code {
private ButtonGroupButton firstButton;
private async Task FocusFirst()
{
// Focus management logic
await Task.CompletedTask;
}
}
<style>
.focus-styled .e-btn:focus {
outline: 3px solid #4A90E2;
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(74, 144, 226, 0.2);
}
.focus-styled .e-btn:focus:not(:focus-visible) {
outline: none;
box-shadow: none;
}
</style>---
Keyboard Navigation
Arrow Key Navigation
<SfButtonGroup @onkeydown="HandleKeyDown">
<ButtonGroupButton @ref="button1" @onfocus="() => focusedIndex = 0">
Button 1
</ButtonGroupButton>
<ButtonGroupButton @ref="button2" @onfocus="() => focusedIndex = 1">
Button 2
</ButtonGroupButton>
<ButtonGroupButton @ref="button3" @onfocus="() => focusedIndex = 2">
Button 3
</ButtonGroupButton>
</SfButtonGroup>
@code {
private ButtonGroupButton button1, button2, button3;
private int focusedIndex = 0;
private ButtonGroupButton[] buttons => new[] { button1, button2, button3 };
private void HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "ArrowRight")
{
focusedIndex = (focusedIndex + 1) % buttons.Length;
// Focus next button
}
else if (e.Key == "ArrowLeft")
{
focusedIndex = (focusedIndex - 1 + buttons.Length) % buttons.Length;
// Focus previous button
}
}
}Keyboard Shortcuts
<SfButtonGroup @onkeydown="HandleShortcuts">
<ButtonGroupButton>Bold (Ctrl+B)</ButtonGroupButton>
<ButtonGroupButton>Italic (Ctrl+I)</ButtonGroupButton>
<ButtonGroupButton>Underline (Ctrl+U)</ButtonGroupButton>
</SfButtonGroup>
@code {
private void HandleShortcuts(KeyboardEventArgs e)
{
if (e.CtrlKey)
{
switch (e.Key.ToLower())
{
case "b":
ApplyBold();
break;
case "i":
ApplyItalic();
break;
case "u":
ApplyUnderline();
break;
}
}
}
private void ApplyBold() => Console.WriteLine("Bold applied");
private void ApplyItalic() => Console.WriteLine("Italic applied");
private void ApplyUnderline() => Console.WriteLine("Underline applied");
}---
Selection State Management
Single Selection with State Persistence
@inject ProtectedLocalStorage LocalStorage
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => SelectAndSave(1)"
Selected="@(selected == 1)">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => SelectAndSave(2)"
Selected="@(selected == 2)">
Option 2
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => SelectAndSave(3)"
Selected="@(selected == 3)">
Option 3
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
protected override async Task OnInitializedAsync()
{
var result = await LocalStorage.GetAsync<int>("selectedButton");
selected = result.Success ? result.Value : 1;
}
private async Task SelectAndSave(int option)
{
selected = option;
await LocalStorage.SetAsync("selectedButton", selected);
}
}Multiple Selection with Complex State
<SfButtonGroup Mode="SelectionMode.Multiple">
@foreach (var option in options)
{
<ButtonGroupButton @onclick="() => ToggleOption(option.Id)"
Selected="@selectedOptions.Contains(option.Id)">
@option.Name
</ButtonGroupButton>
}
</SfButtonGroup>
<p>Selected: @string.Join(", ", GetSelectedNames())</p>
@code {
private List<Option> options = new()
{
new Option { Id = 1, Name = "Bold" },
new Option { Id = 2, Name = "Italic" },
new Option { Id = 3, Name = "Underline" },
new Option { Id = 4, Name = "Strikethrough" }
};
private HashSet<int> selectedOptions = new();
private void ToggleOption(int id)
{
if (selectedOptions.Contains(id))
selectedOptions.Remove(id);
else
selectedOptions.Add(id);
}
private IEnumerable<string> GetSelectedNames()
{
return options
.Where(o => selectedOptions.Contains(o.Id))
.Select(o => o.Name);
}
private class Option
{
public int Id { get; set; }
public string Name { get; set; }
}
}Conditional Selection Rules
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => TrySelect(1)"
Selected="@(selected == 1)"
Disabled="@(IsDisabled(1))">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => TrySelect(2)"
Selected="@(selected == 2)"
Disabled="@(IsDisabled(2))">
Option 2
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => TrySelect(3)"
Selected="@(selected == 3)"
Disabled="@(IsDisabled(3))">
Option 3
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
private HashSet<int> disabledOptions = new() { 2 };
private bool IsDisabled(int option) => disabledOptions.Contains(option);
private void TrySelect(int option)
{
if (!IsDisabled(option))
{
selected = option;
// Apply business rules
UpdateDisabledOptions();
}
}
private void UpdateDisabledOptions()
{
// Example: Disable option 3 if option 1 is selected
if (selected == 1)
disabledOptions.Add(3);
else
disabledOptions.Remove(3);
}
}---
Event Handling Patterns
Centralized Event Handler
<SfButtonGroup>
<ButtonGroupButton @onclick="() => HandleClick(\"Action1\")">
Action 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => HandleClick(\"Action2\")">
Action 2
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => HandleClick(\"Action3\")">
Action 3
</ButtonGroupButton>
</SfButtonGroup>
@code {
private void HandleClick(string action)
{
Console.WriteLine($"Action: {action}");
switch (action)
{
case "Action1":
PerformAction1();
break;
case "Action2":
PerformAction2();
break;
case "Action3":
PerformAction3();
break;
}
}
private void PerformAction1() { }
private void PerformAction2() { }
private void PerformAction3() { }
}Event Bubbling Pattern
<div @onclick="HandleGroupClick">
<SfButtonGroup>
<ButtonGroupButton data-action="save">Save</ButtonGroupButton>
<ButtonGroupButton data-action="load">Load</ButtonGroupButton>
<ButtonGroupButton data-action="delete">Delete</ButtonGroupButton>
</SfButtonGroup>
</div>
@code {
private void HandleGroupClick(MouseEventArgs e)
{
// Handle click at group level
Console.WriteLine("Group clicked");
}
}Async Event Handling
<SfButtonGroup>
<ButtonGroupButton @onclick="async () => await HandleAsyncAction(1)"
Disabled="@isProcessing">
@(isProcessing && currentAction == 1 ? "Processing..." : "Action 1")
</ButtonGroupButton>
<ButtonGroupButton @onclick="async () => await HandleAsyncAction(2)"
Disabled="@isProcessing">
@(isProcessing && currentAction == 2 ? "Processing..." : "Action 2")
</ButtonGroupButton>
</SfButtonGroup>
@code {
private bool isProcessing = false;
private int currentAction = 0;
private async Task HandleAsyncAction(int action)
{
isProcessing = true;
currentAction = action;
try
{
await ProcessActionAsync(action);
}
finally
{
isProcessing = false;
currentAction = 0;
}
}
private async Task ProcessActionAsync(int action)
{
await Task.Delay(2000);
Console.WriteLine($"Action {action} completed");
}
}---
Performance Optimization
Debounced Selection
<SfButtonGroup Mode="SelectionMode.Single">
@foreach (var option in Enumerable.Range(1, 10))
{
<ButtonGroupButton @onclick="() => DebouncedSelect(option)"
Selected="@(selected == option)">
@option
</ButtonGroupButton>
}
</SfButtonGroup>
@code {
private int selected = 1;
private System.Threading.Timer debounceTimer;
private void DebouncedSelect(int option)
{
debounceTimer?.Dispose();
debounceTimer = new System.Threading.Timer(_ =>
{
InvokeAsync(() =>
{
selected = option;
ProcessSelection(option);
StateHasChanged();
});
}, null, 300, Timeout.Infinite);
}
private void ProcessSelection(int option)
{
// Expensive operation
Console.WriteLine($"Processing option {option}");
}
public void Dispose()
{
debounceTimer?.Dispose();
}
}Virtualization for Large Groups
@using Microsoft.AspNetCore.Components.Web.Virtualization
<div class="button-container">
<Virtualize Items="@buttons" Context="button">
<ButtonGroupButton @onclick="() => HandleClick(button.Id)">
@button.Text
</ButtonGroupButton>
</Virtualize>
</div>
@code {
private List<ButtonModel> buttons = Enumerable.Range(1, 1000)
.Select(i => new ButtonModel { Id = i, Text = $"Button {i}" })
.ToList();
private void HandleClick(int id)
{
Console.WriteLine($"Button {id} clicked");
}
private class ButtonModel
{
public int Id { get; set; }
public string Text { get; set; }
}
}---
Best Practices
1. Provide Clear Visual Feedback
<SfButtonGroup Mode="SelectionMode.Single" CssClass="visual-feedback">
<ButtonGroupButton @onclick="() => selected = 1"
Selected="@(selected == 1)">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => selected = 2"
Selected="@(selected == 2)">
Option 2
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
}
<style>
.visual-feedback .e-btn.e-active {
background-color: #4CAF50;
color: white;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.2);
}
</style>2. Handle Edge Cases
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => SelectWithValidation(1)"
Selected="@(selected == 1)">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => SelectWithValidation(2)"
Selected="@(selected == 2)">
Option 2
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
private void SelectWithValidation(int option)
{
if (CanSelect(option))
{
selected = option;
OnSelectionChanged(option);
}
else
{
ShowValidationMessage();
}
}
private bool CanSelect(int option)
{
// Validation logic
return true;
}
private void OnSelectionChanged(int option)
{
// Handle selection change
}
private void ShowValidationMessage()
{
// Show message to user
}
}3. Implement Proper Cleanup
<SfButtonGroup>
<ButtonGroupButton @onclick="StartTimer">Start</ButtonGroupButton>
<ButtonGroupButton @onclick="StopTimer">Stop</ButtonGroupButton>
</SfButtonGroup>
@code {
private System.Threading.Timer timer;
private void StartTimer()
{
timer = new System.Threading.Timer(_ =>
{
InvokeAsync(StateHasChanged);
}, null, 0, 1000);
}
private void StopTimer()
{
timer?.Dispose();
timer = null;
}
public void Dispose()
{
timer?.Dispose();
}
}---
Testing Strategies
Unit Testing Selection
[Fact]
public void ButtonGroup_SingleSelection_UpdatesState()
{
// Arrange
var component = RenderComponent<ButtonGroupComponent>();
var buttons = component.FindAll("button");
// Act
buttons[1].Click();
// Assert
Assert.True(buttons[1].ClassList.Contains("e-active"));
Assert.False(buttons[0].ClassList.Contains("e-active"));
}Integration Testing
[Fact]
public async Task ButtonGroup_MultipleSelection_AllowsMultiple()
{
// Arrange
var component = RenderComponent<MultiSelectButtonGroup>();
var buttons = component.FindAll("button");
// Act
buttons[0].Click();
buttons[1].Click();
// Assert
Assert.True(buttons[0].ClassList.Contains("e-active"));
Assert.True(buttons[1].ClassList.Contains("e-active"));
}---
Common Patterns
Wizard Navigation
<SfButtonGroup>
<ButtonGroupButton Disabled="@(currentStep == 1)"
@onclick="() => GoToStep(1)">
Step 1
</ButtonGroupButton>
<ButtonGroupButton Disabled="@(currentStep < 2)"
@onclick="() => GoToStep(2)">
Step 2
</ButtonGroupButton>
<ButtonGroupButton Disabled="@(currentStep < 3)"
@onclick="() => GoToStep(3)">
Step 3
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int currentStep = 1;
private void GoToStep(int step)
{
if (step <= currentStep)
{
currentStep = step;
}
}
}Filter Controls
<SfButtonGroup Mode="SelectionMode.Multiple">
<ButtonGroupButton @onclick="() => ToggleFilter(\"active\")"
Selected="@filters.Contains(\"active\")">
Active
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => ToggleFilter(\"pending\")"
Selected="@filters.Contains(\"pending\")">
Pending
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => ToggleFilter(\"completed\")"
Selected="@filters.Contains(\"completed\")">
Completed
</ButtonGroupButton>
</SfButtonGroup>
@code {
private HashSet<string> filters = new();
private void ToggleFilter(string filter)
{
if (filters.Contains(filter))
filters.Remove(filter);
else
filters.Add(filter);
ApplyFilters();
}
private void ApplyFilters()
{
// Filter data based on selected filters
}
}---
Next Steps
- Review button-group-features.md for basic functionality
- Explore button-group-styling.md for customization
Button Group Component - Features
Table of Contents
- Getting Started
- Installation
- Basic Button Group
- Orientation
- Selection Modes
- Nesting Button Groups
- Native Event Handling
- Item Configuration
- Common Scenarios
- Troubleshooting
---
Getting Started
The Blazor ButtonGroup component groups multiple buttons together as a single unit. It supports horizontal/vertical orientation, single/multiple selection modes, and nested groups.
Prerequisites
- .NET SDK 6.0 or later
- Blazor WebAssembly or Blazor Server application
- Visual Studio 2022 or VS Code
---
Installation
Step 1: Install NuGet Packages
dotnet add package Syncfusion.Blazor.SplitButtons
dotnet add package Syncfusion.Blazor.ThemesStep 2: Add Namespaces
In ~/_Imports.razor:
@using Syncfusion.Blazor
@using Syncfusion.Blazor.SplitButtonsStep 3: Register Service
In ~/Program.cs:
using Syncfusion.Blazor;
builder.Services.AddSyncfusionBlazor();
await builder.Build().RunAsync();Step 4: Add Theme and Script
In ~/index.html:
<head>
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>
</head>---
Basic Button Group
Simple Button Group
<SfButtonGroup>
<ButtonGroupButton>Left</ButtonGroupButton>
<ButtonGroupButton>Center</ButtonGroupButton>
<ButtonGroupButton>Right</ButtonGroupButton>
</SfButtonGroup>Button Group with Events
<SfButtonGroup>
<ButtonGroupButton @onclick="OnLeftClick">Left</ButtonGroupButton>
<ButtonGroupButton @onclick="OnCenterClick">Center</ButtonGroupButton>
<ButtonGroupButton @onclick="OnRightClick">Right</ButtonGroupButton>
</SfButtonGroup>
@code {
private void OnLeftClick() => Console.WriteLine("Left clicked");
private void OnCenterClick() => Console.WriteLine("Center clicked");
private void OnRightClick() => Console.WriteLine("Right clicked");
}Button Group with Icons
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-left-icon"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-middle-icon"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-right-icon"></ButtonGroupButton>
</SfButtonGroup>
<style>
.e-left-icon::before { content: '\e729'; }
.e-middle-icon::before { content: '\e72a'; }
.e-right-icon::before { content: '\e72b'; }
</style>---
Orientation
Horizontal Orientation (Default)
<SfButtonGroup>
<ButtonGroupButton>Button 1</ButtonGroupButton>
<ButtonGroupButton>Button 2</ButtonGroupButton>
<ButtonGroupButton>Button 3</ButtonGroupButton>
</SfButtonGroup>Vertical Orientation
<SfButtonGroup Orientation="Orientation.Vertical">
<ButtonGroupButton>Top</ButtonGroupButton>
<ButtonGroupButton>Middle</ButtonGroupButton>
<ButtonGroupButton>Bottom</ButtonGroupButton>
</SfButtonGroup>Dynamic Orientation
<label>
<input type="checkbox" @bind="isVertical" />
Vertical Orientation
</label>
<SfButtonGroup Orientation="@(isVertical ? Orientation.Vertical : Orientation.Horizontal)">
<ButtonGroupButton>Button 1</ButtonGroupButton>
<ButtonGroupButton>Button 2</ButtonGroupButton>
<ButtonGroupButton>Button 3</ButtonGroupButton>
</SfButtonGroup>
@code {
private bool isVertical = false;
}---
Selection Modes
Single Selection
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => SelectOption(1)"
Selected="@(selectedOption == 1)">
Option 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => SelectOption(2)"
Selected="@(selectedOption == 2)">
Option 2
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => SelectOption(3)"
Selected="@(selectedOption == 3)">
Option 3
</ButtonGroupButton>
</SfButtonGroup>
<p>Selected: Option @selectedOption</p>
@code {
private int selectedOption = 1;
private void SelectOption(int option)
{
selectedOption = option;
}
}Multiple Selection
<SfButtonGroup Mode="SelectionMode.Multiple">
<ButtonGroupButton @onclick="() => ToggleSelection(1)"
Selected="@selectedOptions.Contains(1)">
Bold
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => ToggleSelection(2)"
Selected="@selectedOptions.Contains(2)">
Italic
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => ToggleSelection(3)"
Selected="@selectedOptions.Contains(3)">
Underline
</ButtonGroupButton>
</SfButtonGroup>
<p>Selected: @string.Join(", ", selectedOptions)</p>
@code {
private List<int> selectedOptions = new();
private void ToggleSelection(int option)
{
if (selectedOptions.Contains(option))
selectedOptions.Remove(option);
else
selectedOptions.Add(option);
}
}None Selection Mode
<SfButtonGroup Mode="SelectionMode.None">
<ButtonGroupButton @onclick="OnAction1">Action 1</ButtonGroupButton>
<ButtonGroupButton @onclick="OnAction2">Action 2</ButtonGroupButton>
<ButtonGroupButton @onclick="OnAction3">Action 3</ButtonGroupButton>
</SfButtonGroup>
@code {
private void OnAction1() => Console.WriteLine("Action 1");
private void OnAction2() => Console.WriteLine("Action 2");
private void OnAction3() => Console.WriteLine("Action 3");
}---
Nesting Button Groups
Nested Horizontal Groups
<SfButtonGroup>
<ButtonGroupButton>File</ButtonGroupButton>
<ButtonGroupButton>Edit</ButtonGroupButton>
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-bold"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-italic"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-underline"></ButtonGroupButton>
</SfButtonGroup>
</SfButtonGroup>
<style>
.e-bold::before { content: '\e736'; }
.e-italic::before { content: '\e737'; }
.e-underline::before { content: '\e738'; }
</style>Mixed Orientation Nesting
<SfButtonGroup Orientation="Orientation.Vertical">
<ButtonGroupButton>Top Group</ButtonGroupButton>
<SfButtonGroup>
<ButtonGroupButton>H1</ButtonGroupButton>
<ButtonGroupButton>H2</ButtonGroupButton>
<ButtonGroupButton>H3</ButtonGroupButton>
</SfButtonGroup>
<ButtonGroupButton>Bottom Group</ButtonGroupButton>
</SfButtonGroup>---
Native Event Handling
Click Events
<SfButtonGroup>
<ButtonGroupButton @onclick="HandleClick1">
Button 1
</ButtonGroupButton>
<ButtonGroupButton @onclick="HandleClick2">
Button 2
</ButtonGroupButton>
</SfButtonGroup>
@code {
private void HandleClick1()
{
Console.WriteLine("Button 1 clicked");
}
private void HandleClick2()
{
Console.WriteLine("Button 2 clicked");
}
}Mouse Events
<SfButtonGroup>
<ButtonGroupButton @onclick="OnClick"
@onmouseover="OnMouseOver"
@onmouseout="OnMouseOut">
Hover Me
</ButtonGroupButton>
</SfButtonGroup>
<p>@eventMessage</p>
@code {
private string eventMessage = "";
private void OnClick() => eventMessage = "Clicked";
private void OnMouseOver() => eventMessage = "Mouse Over";
private void OnMouseOut() => eventMessage = "Mouse Out";
}Keyboard Events
<SfButtonGroup>
<ButtonGroupButton @onkeydown="OnKeyDown">
Press Key
</ButtonGroupButton>
</SfButtonGroup>
@code {
private void OnKeyDown(KeyboardEventArgs e)
{
Console.WriteLine($"Key pressed: {e.Key}");
}
}---
Item Configuration
Disabled Buttons
<SfButtonGroup>
<ButtonGroupButton>Enabled</ButtonGroupButton>
<ButtonGroupButton Disabled="true">Disabled</ButtonGroupButton>
<ButtonGroupButton>Enabled</ButtonGroupButton>
</SfButtonGroup>Buttons with Content
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-cut">Cut</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-copy">Copy</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-paste">Paste</ButtonGroupButton>
</SfButtonGroup>
<style>
.e-cut::before { content: '\e73f'; }
.e-copy::before { content: '\e721'; }
.e-paste::before { content: '\e739'; }
</style>Custom CSS Classes
<SfButtonGroup>
<ButtonGroupButton CssClass="custom-btn-1">Custom 1</ButtonGroupButton>
<ButtonGroupButton CssClass="custom-btn-2">Custom 2</ButtonGroupButton>
<ButtonGroupButton CssClass="custom-btn-3">Custom 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.custom-btn-1 { background-color: #e3f2fd; }
.custom-btn-2 { background-color: #f3e5f5; }
.custom-btn-3 { background-color: #e8f5e9; }
</style>---
Common Scenarios
Text Alignment Selector
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton IconCss="e-icons e-align-left"
@onclick="() => SetAlignment(\"left\")"
Selected="@(alignment == \"left\")">
</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-align-center"
@onclick="() => SetAlignment(\"center\")"
Selected="@(alignment == \"center\")">
</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-align-right"
@onclick="() => SetAlignment(\"right\")"
Selected="@(alignment == \"right\")">
</ButtonGroupButton>
</SfButtonGroup>
<div style="text-align: @alignment; margin-top: 20px;">
<p>This text is aligned @alignment</p>
</div>
@code {
private string alignment = "left";
private void SetAlignment(string value)
{
alignment = value;
}
}
<style>
.e-align-left::before { content: '\e739'; }
.e-align-center::before { content: '\e73a'; }
.e-align-right::before { content: '\e73b'; }
</style>View Mode Switcher
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton IconCss="e-icons e-grid-view"
@onclick="() => viewMode = \"grid\""
Selected="@(viewMode == \"grid\")">
</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-list-view"
@onclick="() => viewMode = \"list\""
Selected="@(viewMode == \"list\")">
</ButtonGroupButton>
</SfButtonGroup>
<div style="margin-top: 20px;">
@if (viewMode == "grid")
{
<div class="grid-view">Grid View Content</div>
}
else
{
<div class="list-view">List View Content</div>
}
</div>
@code {
private string viewMode = "grid";
}
<style>
.e-grid-view::before { content: '\e7a8'; }
.e-list-view::before { content: '\e7c5'; }
</style>Zoom Controls
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-zoom-out"
@onclick="ZoomOut"
Disabled="@(zoomLevel <= 50)">
</ButtonGroupButton>
<ButtonGroupButton @onclick="ResetZoom">
@zoomLevel%
</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-zoom-in"
@onclick="ZoomIn"
Disabled="@(zoomLevel >= 200)">
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int zoomLevel = 100;
private void ZoomIn() => zoomLevel = Math.Min(200, zoomLevel + 10);
private void ZoomOut() => zoomLevel = Math.Max(50, zoomLevel - 10);
private void ResetZoom() => zoomLevel = 100;
}
<style>
.e-zoom-in::before { content: '\e7a3'; }
.e-zoom-out::before { content: '\e7a4'; }
</style>Toolbar with Button Groups
<div class="toolbar">
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-cut">Cut</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-copy">Copy</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-paste">Paste</ButtonGroupButton>
</SfButtonGroup>
<SfButtonGroup Mode="SelectionMode.Multiple">
<ButtonGroupButton IconCss="e-icons e-bold"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-italic"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-underline"></ButtonGroupButton>
</SfButtonGroup>
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-undo"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-redo"></ButtonGroupButton>
</SfButtonGroup>
</div>
<style>
.toolbar {
display: flex;
gap: 20px;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
}
.e-cut::before { content: '\e73f'; }
.e-copy::before { content: '\e721'; }
.e-paste::before { content: '\e739'; }
.e-bold::before { content: '\e736'; }
.e-italic::before { content: '\e737'; }
.e-underline::before { content: '\e738'; }
.e-undo::before { content: '\e7d7'; }
.e-redo::before { content: '\e7d8'; }
</style>---
Troubleshooting
Selection Not Working
Problem: Button selection state not updating
Solution: Use proper selection mode and state management
<!-- Correct -->
<SfButtonGroup Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => selected = 1"
Selected="@(selected == 1)">
Button 1
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
}Icons Not Displaying
Problem: Icons show as empty boxes
Solution: Ensure icon CSS is properly defined
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-custom-icon">
</ButtonGroupButton>
</SfButtonGroup>
<style>
.e-custom-icon::before {
content: '\e823'; /* Valid unicode */
}
</style>Nested Groups Not Aligned
Problem: Nested button groups misaligned
Solution: Use proper CSS for layout
<SfButtonGroup>
<ButtonGroupButton>Button 1</ButtonGroupButton>
<SfButtonGroup CssClass="nested-group">
<ButtonGroupButton>Nested 1</ButtonGroupButton>
<ButtonGroupButton>Nested 2</ButtonGroupButton>
</SfButtonGroup>
</SfButtonGroup>
<style>
.nested-group {
margin-left: 10px;
}
</style>---
Best Practices
1. Use selection modes appropriately - Single for mutually exclusive options, Multiple for independent toggles 2. Provide clear visual feedback - Ensure selected state is obvious 3. Keep groups focused - Don't mix unrelated actions in one group 4. Use icons consistently - Either all buttons have icons or none 5. Consider mobile - Ensure touch targets are adequate (44px minimum) 6. Test keyboard navigation - Verify tab order and selection 7. Provide tooltips - Especially for icon-only buttons 8. Maintain consistency - Use same patterns across the application
---
Next Steps
- Explore button-group-styling.md for customization
- Learn about button-group-advanced-features.md for accessibility
Button Group Component - Styling
Table of Contents
- Appearance Customization
- Button Types and Styles
- Icons in Button Groups
- Size Variations
- Custom CSS Classes
- Theme Integration
- Spacing and Layout
- Responsive Design
---
Appearance Customization
Custom Colors
<SfButtonGroup CssClass="custom-colors">
<ButtonGroupButton>Button 1</ButtonGroupButton>
<ButtonGroupButton>Button 2</ButtonGroupButton>
<ButtonGroupButton>Button 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.custom-colors .e-btn {
background-color: #667eea;
color: white;
border-color: #5a67d8;
}
.custom-colors .e-btn:hover {
background-color: #5a67d8;
}
.custom-colors .e-btn.e-active {
background-color: #4c51bf;
}
</style>Gradient Backgrounds
<SfButtonGroup CssClass="gradient-group">
<ButtonGroupButton>Gradient 1</ButtonGroupButton>
<ButtonGroupButton>Gradient 2</ButtonGroupButton>
<ButtonGroupButton>Gradient 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.gradient-group .e-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
}
.gradient-group .e-btn:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
</style>Rounded Corners
<SfButtonGroup CssClass="rounded-group">
<ButtonGroupButton>First</ButtonGroupButton>
<ButtonGroupButton>Middle</ButtonGroupButton>
<ButtonGroupButton>Last</ButtonGroupButton>
</SfButtonGroup>
<style>
.rounded-group .e-btn:first-child {
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
}
.rounded-group .e-btn:last-child {
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
}
</style>---
Button Types and Styles
Primary Style Group
<SfButtonGroup>
<ButtonGroupButton CssClass="e-primary">Primary 1</ButtonGroupButton>
<ButtonGroupButton CssClass="e-primary">Primary 2</ButtonGroupButton>
<ButtonGroupButton CssClass="e-primary">Primary 3</ButtonGroupButton>
</SfButtonGroup>Outline Style Group
<SfButtonGroup>
<ButtonGroupButton CssClass="e-outline">Outline 1</ButtonGroupButton>
<ButtonGroupButton CssClass="e-outline">Outline 2</ButtonGroupButton>
<ButtonGroupButton CssClass="e-outline">Outline 3</ButtonGroupButton>
</SfButtonGroup>Flat Style Group
<SfButtonGroup>
<ButtonGroupButton CssClass="e-flat">Flat 1</ButtonGroupButton>
<ButtonGroupButton CssClass="e-flat">Flat 2</ButtonGroupButton>
<ButtonGroupButton CssClass="e-flat">Flat 3</ButtonGroupButton>
</SfButtonGroup>Mixed Styles
<SfButtonGroup>
<ButtonGroupButton CssClass="e-primary">Primary</ButtonGroupButton>
<ButtonGroupButton CssClass="e-success">Success</ButtonGroupButton>
<ButtonGroupButton CssClass="e-danger">Danger</ButtonGroupButton>
</SfButtonGroup>---
Icons in Button Groups
Icon Only Buttons
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-bold"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-italic"></ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-underline"></ButtonGroupButton>
</SfButtonGroup>
<style>
.e-bold::before { content: '\e736'; }
.e-italic::before { content: '\e737'; }
.e-underline::before { content: '\e738'; }
</style>Icon with Text
<SfButtonGroup>
<ButtonGroupButton IconCss="e-icons e-cut">Cut</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-copy">Copy</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-paste">Paste</ButtonGroupButton>
</SfButtonGroup>
<style>
.e-cut::before { content: '\e73f'; }
.e-copy::before { content: '\e721'; }
.e-paste::before { content: '\e739'; }
</style>Custom Icon Styling
<SfButtonGroup CssClass="custom-icons">
<ButtonGroupButton IconCss="e-icons e-icon-1">Action 1</ButtonGroupButton>
<ButtonGroupButton IconCss="e-icons e-icon-2">Action 2</ButtonGroupButton>
</SfButtonGroup>
<style>
.custom-icons .e-icon-1 {
color: #3498db;
font-size: 18px;
}
.custom-icons .e-icon-2 {
color: #e74c3c;
font-size: 18px;
}
.e-icon-1::before { content: '\e823'; }
.e-icon-2::before { content: '\e7fc'; }
</style>---
Size Variations
Small Button Group
<SfButtonGroup CssClass="e-small">
<ButtonGroupButton>Small 1</ButtonGroupButton>
<ButtonGroupButton>Small 2</ButtonGroupButton>
<ButtonGroupButton>Small 3</ButtonGroupButton>
</SfButtonGroup>Normal Button Group (Default)
<SfButtonGroup>
<ButtonGroupButton>Normal 1</ButtonGroupButton>
<ButtonGroupButton>Normal 2</ButtonGroupButton>
<ButtonGroupButton>Normal 3</ButtonGroupButton>
</SfButtonGroup>Large Custom Size
<SfButtonGroup CssClass="large-group">
<ButtonGroupButton>Large 1</ButtonGroupButton>
<ButtonGroupButton>Large 2</ButtonGroupButton>
<ButtonGroupButton>Large 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.large-group .e-btn {
padding: 12px 24px;
font-size: 18px;
min-height: 48px;
}
</style>---
Custom CSS Classes
Themed Button Groups
<SfButtonGroup CssClass="theme-primary">
<ButtonGroupButton>Theme 1</ButtonGroupButton>
<ButtonGroupButton>Theme 2</ButtonGroupButton>
<ButtonGroupButton>Theme 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.theme-primary .e-btn {
background-color: #1e3a8a;
color: white;
border-color: #1e40af;
}
.theme-primary .e-btn:hover {
background-color: #1e40af;
}
.theme-primary .e-btn.e-active {
background-color: #1d4ed8;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.2);
}
</style>Segmented Control Style
<SfButtonGroup CssClass="segmented-control" Mode="SelectionMode.Single">
<ButtonGroupButton @onclick="() => selected = 1"
Selected="@(selected == 1)">
Daily
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => selected = 2"
Selected="@(selected == 2)">
Weekly
</ButtonGroupButton>
<ButtonGroupButton @onclick="() => selected = 3"
Selected="@(selected == 3)">
Monthly
</ButtonGroupButton>
</SfButtonGroup>
@code {
private int selected = 1;
}
<style>
.segmented-control {
background: #e5e7eb;
padding: 4px;
border-radius: 8px;
}
.segmented-control .e-btn {
background: transparent;
border: none;
color: #4b5563;
transition: all 0.2s ease;
}
.segmented-control .e-btn.e-active {
background: white;
color: #1f2937;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
</style>Pill Style
<SfButtonGroup CssClass="pill-group">
<ButtonGroupButton>Pill 1</ButtonGroupButton>
<ButtonGroupButton>Pill 2</ButtonGroupButton>
<ButtonGroupButton>Pill 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.pill-group .e-btn:first-child {
border-radius: 50px 0 0 50px;
}
.pill-group .e-btn:last-child {
border-radius: 0 50px 50px 0;
}
</style>---
Theme Integration
Bootstrap Theme
<SfButtonGroup CssClass="bootstrap-style">
<ButtonGroupButton>Bootstrap 1</ButtonGroupButton>
<ButtonGroupButton>Bootstrap 2</ButtonGroupButton>
<ButtonGroupButton>Bootstrap 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.bootstrap-style .e-btn {
background-color: #0d6efd;
border-color: #0d6efd;
color: white;
}
.bootstrap-style .e-btn:hover {
background-color: #0b5ed7;
border-color: #0a58ca;
}
</style>Material Theme
<SfButtonGroup CssClass="material-style">
<ButtonGroupButton>Material 1</ButtonGroupButton>
<ButtonGroupButton>Material 2</ButtonGroupButton>
<ButtonGroupButton>Material 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.material-style .e-btn {
background-color: #6200ea;
color: white;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.material-style .e-btn:hover {
box-shadow: 0 2px 4px rgba(98, 0, 234, 0.4);
}
</style>---
Spacing and Layout
Gap Between Buttons
<SfButtonGroup CssClass="spaced-group">
<ButtonGroupButton>Button 1</ButtonGroupButton>
<ButtonGroupButton>Button 2</ButtonGroupButton>
<ButtonGroupButton>Button 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.spaced-group .e-btn {
margin-right: 5px;
}
.spaced-group .e-btn:last-child {
margin-right: 0;
}
</style>Vertical Spacing
<SfButtonGroup Orientation="Orientation.Vertical" CssClass="vertical-spaced">
<ButtonGroupButton>Top</ButtonGroupButton>
<ButtonGroupButton>Middle</ButtonGroupButton>
<ButtonGroupButton>Bottom</ButtonGroupButton>
</SfButtonGroup>
<style>
.vertical-spaced .e-btn {
margin-bottom: 5px;
}
.vertical-spaced .e-btn:last-child {
margin-bottom: 0;
}
</style>Full Width
<SfButtonGroup CssClass="full-width">
<ButtonGroupButton>33.3%</ButtonGroupButton>
<ButtonGroupButton>33.3%</ButtonGroupButton>
<ButtonGroupButton>33.3%</ButtonGroupButton>
</SfButtonGroup>
<style>
.full-width {
width: 100%;
display: flex;
}
.full-width .e-btn {
flex: 1;
}
</style>---
Responsive Design
Mobile-First Approach
<SfButtonGroup CssClass="responsive-group">
<ButtonGroupButton>Button 1</ButtonGroupButton>
<ButtonGroupButton>Button 2</ButtonGroupButton>
<ButtonGroupButton>Button 3</ButtonGroupButton>
</SfButtonGroup>
<style>
/* Mobile: Vertical */
.responsive-group {
flex-direction: column;
}
/* Tablet and up: Horizontal */
@media (min-width: 768px) {
.responsive-group {
flex-direction: row;
}
}
</style>Adaptive Button Text
<SfButtonGroup>
<ButtonGroupButton>
<span class="mobile-text">B</span>
<span class="desktop-text">Bold</span>
</ButtonGroupButton>
<ButtonGroupButton>
<span class="mobile-text">I</span>
<span class="desktop-text">Italic</span>
</ButtonGroupButton>
</SfButtonGroup>
<style>
.desktop-text { display: none; }
.mobile-text { display: inline; }
@media (min-width: 768px) {
.desktop-text { display: inline; }
.mobile-text { display: none; }
}
</style>Touch-Friendly Sizes
<SfButtonGroup CssClass="touch-friendly">
<ButtonGroupButton>Touch 1</ButtonGroupButton>
<ButtonGroupButton>Touch 2</ButtonGroupButton>
<ButtonGroupButton>Touch 3</ButtonGroupButton>
</SfButtonGroup>
<style>
.touch-friendly .e-btn {
min-height: 44px;
min-width: 44px;
padding: 12px 16px;
}
@media (hover: hover) and (pointer: fine) {
.touch-friendly .e-btn {
min-height: 36px;
padding: 8px 12px;
}
}
</style>---
Best Practices
1. Maintain visual hierarchy - Use styles to indicate importance 2. Ensure consistent spacing - Keep gaps uniform 3. Test on mobile devices - Verify touch targets are adequate 4. Use semantic colors - Colors should convey meaning 5. Provide clear selected state - Make active buttons obvious 6. Consider color contrast - Ensure readability (WCAG AA) 7. Use animations sparingly - Smooth transitions without distraction 8. Test with themes - Verify appearance across different themes
---
Next Steps
- Review button-group-features.md for functionality
- Explore button-group-advanced-features.md for accessibility
Button Component - Styling
Table of Contents
- CSS Class Customization
- Appearance Customization
- Block Buttons
- Rounded Corners
- RTL Support
- Theme Integration
- Color Schemes
- Responsive Design
- Custom Styling Examples
---
CSS Class Customization
Using CssClass Property
The CssClass property allows you to apply custom CSS classes to buttons.
<SfButton CssClass="custom-button e-primary">Custom Styled Button</SfButton>
<style>
.custom-button.e-btn {
border-radius: 20px;
padding: 10px 30px;
font-weight: bold;
}
</style>Multiple CSS Classes
<SfButton CssClass="custom-primary large-button shadow">
Multi-Class Button
</SfButton>
<style>
.custom-primary.e-btn {
background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
}
.large-button.e-btn {
padding: 15px 40px;
font-size: 18px;
}
.shadow.e-btn {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
</style>Scoped Styles with ::deep
For component-scoped styles that penetrate Syncfusion components:
<SfButton CssClass="scoped-custom">Scoped Button</SfButton>
<style>
::deep .scoped-custom.e-btn {
background-color: #ff6b6b;
color: white;
}
::deep .scoped-custom.e-btn:hover {
background-color: #ff5252;
}
</style>---
Appearance Customization
Custom Background and Text Colors
<SfButton CssClass="color-custom">Custom Colors</SfButton>
<style>
.color-custom.e-btn {
background-color: #8e44ad;
color: #ffffff;
border: 2px solid #9b59b6;
}
.color-custom.e-btn:hover {
background-color: #9b59b6;
border-color: #8e44ad;
}
.color-custom.e-btn:active {
background-color: #7d3c98;
}
</style>Gradient Backgrounds
<SfButton CssClass="gradient-btn">Gradient Button</SfButton>
<style>
.gradient-btn.e-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
}
.gradient-btn.e-btn:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
</style>Box Shadow and Elevation
<SfButton CssClass="elevated-btn e-primary">Elevated Button</SfButton>
<style>
.elevated-btn.e-btn {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.elevated-btn.e-btn:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.elevated-btn.e-btn:active {
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
</style>Border Customization
<SfButton CssClass="custom-border">Custom Border</SfButton>
<style>
.custom-border.e-btn {
border: 3px solid #3498db;
border-style: dashed;
border-radius: 10px;
background: transparent;
color: #3498db;
}
.custom-border.e-btn:hover {
background: #3498db;
color: white;
}
</style>---
Block Buttons
Block buttons span the full width of their container.
Basic Block Button
<SfButton CssClass="e-block e-primary">Full Width Button</SfButton>Custom Block Styling
<div class="button-container">
<SfButton CssClass="e-block e-primary custom-block">
Primary Block
</SfButton>
<SfButton CssClass="e-block e-success custom-block">
Success Block
</SfButton>
</div>
<style>
.button-container {
width: 100%;
display: flex;
flex-direction: column;
gap: 10px;
}
.custom-block.e-btn {
padding: 15px;
font-size: 16px;
font-weight: 600;
}
</style>Responsive Block Buttons
<SfButton CssClass="responsive-block e-primary">
Responsive Button
</SfButton>
<style>
.responsive-block.e-btn {
width: auto;
}
@media (max-width: 768px) {
.responsive-block.e-btn {
width: 100%;
display: block;
}
}
</style>---
Rounded Corners
Default Rounded Corners
<SfButton CssClass="rounded-default e-primary">Rounded Button</SfButton>
<style>
.rounded-default.e-btn {
border-radius: 20px;
}
</style>Fully Rounded (Pill Shape)
<SfButton CssClass="pill-button e-primary">Pill Button</SfButton>
<style>
.pill-button.e-btn {
border-radius: 50px;
padding: 10px 30px;
}
</style>Sharp Corners
<SfButton CssClass="sharp-corners e-primary">Sharp Button</SfButton>
<style>
.sharp-corners.e-btn {
border-radius: 0;
}
</style>Asymmetric Rounding
<SfButton CssClass="asymmetric-round e-primary">Asymmetric</SfButton>
<style>
.asymmetric-round.e-btn {
border-radius: 20px 0 20px 0;
}
</style>---
RTL Support
Enable RTL Mode
<SfButton EnableRtl="true" IconCss="e-icons e-arrow-icon">
RTL Button
</SfButton>
<style>
.e-arrow-icon::before {
content: '\e71a';
}
</style>RTL with Icon Positioning
<div dir="rtl">
<SfButton EnableRtl="true"
IconCss="e-icons e-save-icon"
IconPosition="IconPosition.Right">
حفظ (Save)
</SfButton>
</div>
<style>
.e-save-icon::before {
content: '\e74e';
}
</style>RTL Layout Example
<div dir="rtl" style="display: flex; gap: 10px;">
<SfButton EnableRtl="true" CssClass="e-primary">أساسي</SfButton>
<SfButton EnableRtl="true" CssClass="e-success">نجاح</SfButton>
<SfButton EnableRtl="true" CssClass="e-danger">خطر</SfButton>
</div>---
Theme Integration
Available Themes
Syncfusion Blazor supports multiple themes:
<!-- Bootstrap 5 -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Material -->
<link href="_content/Syncfusion.Blazor.Themes/material.css" rel="stylesheet" />
<!-- Fabric -->
<link href="_content/Syncfusion.Blazor.Themes/fabric.css" rel="stylesheet" />
<!-- Tailwind -->
<link href="_content/Syncfusion.Blazor.Themes/tailwind.css" rel="stylesheet" />
<!-- Fluent -->
<link href="_content/Syncfusion.Blazor.Themes/fluent.css" rel="stylesheet" />Theme Variants
<!-- Dark themes -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5-dark.css" rel="stylesheet" />
<link href="_content/Syncfusion.Blazor.Themes/material-dark.css" rel="stylesheet" />
<link href="_content/Syncfusion.Blazor.Themes/fabric-dark.css" rel="stylesheet" />Custom Theme via CSS Variables
<SfButton CssClass="theme-custom e-primary">Themed Button</SfButton>
<style>
:root {
--button-primary-bg: #667eea;
--button-primary-hover: #5568d3;
--button-primary-text: #ffffff;
}
.theme-custom.e-btn.e-primary {
background-color: var(--button-primary-bg);
color: var(--button-primary-text);
}
.theme-custom.e-btn.e-primary:hover {
background-color: var(--button-primary-hover);
}
</style>Theme Studio Integration
Use Syncfusion Theme Studio to generate custom themes: 1. Visit https://blazor.syncfusion.com/themestudio/ 2. Customize colors, typography, and component styles 3. Download generated CSS 4. Reference in your application
---
Color Schemes
Brand Colors
<div class="brand-buttons">
<SfButton CssClass="brand-primary">Primary Brand</SfButton>
<SfButton CssClass="brand-secondary">Secondary Brand</SfButton>
<SfButton CssClass="brand-accent">Accent</SfButton>
</div>
<style>
.brand-primary.e-btn {
background-color: #1e3a8a;
color: white;
}
.brand-secondary.e-btn {
background-color: #64748b;
color: white;
}
.brand-accent.e-btn {
background-color: #f59e0b;
color: white;
}
</style>Semantic Colors
<div class="semantic-colors">
<SfButton CssClass="color-info">Info</SfButton>
<SfButton CssClass="color-warning">Warning</SfButton>
<SfButton CssClass="color-error">Error</SfButton>
<SfButton CssClass="color-neutral">Neutral</SfButton>
</div>
<style>
.color-info.e-btn { background: #3b82f6; color: white; }
.color-warning.e-btn { background: #f59e0b; color: white; }
.color-error.e-btn { background: #ef4444; color: white; }
.color-neutral.e-btn { background: #6b7280; color: white; }
</style>Monochrome Palette
<div class="mono-palette">
<SfButton CssClass="mono-100">100</SfButton>
<SfButton CssClass="mono-300">300</SfButton>
<SfButton CssClass="mono-500">500</SfButton>
<SfButton CssClass="mono-700">700</SfButton>
<SfButton CssClass="mono-900">900</SfButton>
</div>
<style>
.mono-100.e-btn { background: #f3f4f6; color: #111827; }
.mono-300.e-btn { background: #d1d5db; color: #111827; }
.mono-500.e-btn { background: #6b7280; color: white; }
.mono-700.e-btn { background: #374151; color: white; }
.mono-900.e-btn { background: #111827; color: white; }
</style>---
Responsive Design
Mobile-First Approach
<SfButton CssClass="responsive-btn e-primary">
Responsive Button
</SfButton>
<style>
/* Mobile (default) */
.responsive-btn.e-btn {
padding: 12px 20px;
font-size: 14px;
width: 100%;
}
/* Tablet */
@media (min-width: 768px) {
.responsive-btn.e-btn {
padding: 10px 24px;
font-size: 16px;
width: auto;
}
}
/* Desktop */
@media (min-width: 1024px) {
.responsive-btn.e-btn {
padding: 12px 32px;
font-size: 18px;
}
}
</style>Adaptive Button Sizes
<div class="button-group">
<SfButton CssClass="adaptive-btn e-primary">
<span class="mobile-text">Add</span>
<span class="desktop-text">Add New Item</span>
</SfButton>
</div>
<style>
.desktop-text { display: none; }
.mobile-text { display: inline; }
@media (min-width: 768px) {
.desktop-text { display: inline; }
.mobile-text { display: none; }
}
.adaptive-btn.e-btn {
min-width: 60px;
}
@media (min-width: 768px) {
.adaptive-btn.e-btn {
min-width: 120px;
}
}
</style>Touch-Friendly Buttons
<SfButton CssClass="touch-friendly e-primary">Touch Button</SfButton>
<style>
.touch-friendly.e-btn {
min-height: 44px; /* Minimum touch target */
min-width: 44px;
padding: 12px 20px;
}
@media (hover: hover) and (pointer: fine) {
/* Desktop: reduce size */
.touch-friendly.e-btn {
min-height: 36px;
padding: 8px 16px;
}
}
</style>---
Custom Styling Examples
Glassmorphism Button
<SfButton CssClass="glass-btn">Glassmorphism</SfButton>
<style>
.glass-btn.e-btn {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #333;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
}
.glass-btn.e-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
</style>Neon Glow Button
<SfButton CssClass="neon-btn">Neon Glow</SfButton>
<style>
.neon-btn.e-btn {
background-color: #000;
color: #0ff;
border: 2px solid #0ff;
text-shadow: 0 0 5px #0ff;
box-shadow: 0 0 10px #0ff;
transition: all 0.3s ease;
}
.neon-btn.e-btn:hover {
box-shadow: 0 0 20px #0ff, 0 0 40px #0ff;
text-shadow: 0 0 10px #0ff;
}
</style>Neumorphism Button
<SfButton CssClass="neumorphic-btn">Neumorphism</SfButton>
<style>
.neumorphic-btn.e-btn {
background: #e0e5ec;
border: none;
color: #333;
box-shadow: 9px 9px 16px rgba(163, 177, 198, 0.6),
-9px -9px 16px rgba(255, 255, 255, 0.5);
}
.neumorphic-btn.e-btn:hover {
box-shadow: inset 9px 9px 16px rgba(163, 177, 198, 0.6),
inset -9px -9px 16px rgba(255, 255, 255, 0.5);
}
.neumorphic-btn.e-btn:active {
box-shadow: inset 6px 6px 12px rgba(163, 177, 198, 0.6),
inset -6px -6px 12px rgba(255, 255, 255, 0.5);
}
</style>Animated Hover Effects
<SfButton CssClass="animated-hover e-primary">Hover Me</SfButton>
<style>
.animated-hover.e-btn {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.animated-hover.e-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.animated-hover.e-btn:hover::before {
width: 300px;
height: 300px;
}
</style>Outlined with Hover Fill
<SfButton CssClass="outline-fill">Outline Fill</SfButton>
<style>
.outline-fill.e-btn {
background: transparent;
border: 2px solid #3498db;
color: #3498db;
transition: all 0.3s ease;
position: relative;
z-index: 1;
overflow: hidden;
}
.outline-fill.e-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: #3498db;
transition: left 0.3s ease;
z-index: -1;
}
.outline-fill.e-btn:hover {
color: white;
}
.outline-fill.e-btn:hover::before {
left: 0;
}
</style>Button with Loading Spinner
<SfButton CssClass="loading-btn @(isLoading ? "loading" : "")"
OnClick="SimulateLoading"
Disabled="@isLoading">
@(isLoading ? "Loading..." : "Submit")
</SfButton>
@code {
private bool isLoading = false;
private async Task SimulateLoading()
{
isLoading = true;
await Task.Delay(2000);
isLoading = false;
}
}
<style>
.loading-btn.e-btn.loading::after {
content: '';
display: inline-block;
width: 14px;
height: 14px;
margin-left: 8px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>---
Best Practices
1. Maintain Consistency - Use a consistent color palette and style across your application 2. Consider Accessibility - Ensure sufficient color contrast (WCAG AA: 4.5:1 minimum) 3. Use CSS Variables - Define reusable color and spacing variables 4. Test Responsiveness - Verify button appearance on different screen sizes 5. Optimize Animations - Use transform and opacity for performant animations 6. Provide Hover States - Give visual feedback for interactive elements 7. Use Semantic Classes - Name classes based on purpose, not appearance 8. Consider Dark Mode - Design buttons that work in both light and dark themes 9. Avoid Over-Styling - Keep designs clean and functional 10. Test Cross-Browser - Verify styles work in all target browsers
---
Common Styling Issues
Issue: Custom Colors Not Applying
Problem: Button keeps default theme colors
Solution: Increase CSS specificity or use !important
/* Increase specificity */
::deep .custom-btn.e-btn.e-primary {
background-color: #custom-color;
}
/* Or use !important (use sparingly) */
.custom-btn.e-btn {
background-color: #custom-color !important;
}Issue: Hover Styles Not Working
Problem: Hover effects don't apply
Solution: Ensure proper selector hierarchy
/* Correct */
::deep .custom-btn.e-btn:hover {
background-color: #hover-color;
}
/* Also works */
:global(.custom-btn.e-btn:hover) {
background-color: #hover-color;
}Issue: Border Radius Not Applying
Problem: Rounded corners not showing
Solution: Override Syncfusion's default styles
.rounded-btn.e-btn {
border-radius: 20px !important;
}---
Next Steps
- Explore button-features.md for component functionality
- Learn about button-advanced-features.md for accessibility
DropdownMenu Component - Advanced Features
Accessibility
ARIA Support
The DropdownMenu automatically includes:
aria-haspopup="true"aria-expandedstatesaria-labelfor screen readers
<SfDropDownButton Content="Accessible Menu" CssClass="accessible-dropdown">
<DropDownMenuItems>
<DropDownMenuItem Text="Option 1"></DropDownMenuItem>
<DropDownMenuItem Text="Option 2"></DropDownMenuItem>
</DropDownMenuItems>
</SfDropDownButton>Keyboard Navigation
- Space/Enter: Open dropdown
- Arrow Up/Down: Navigate items
- Enter: Select item
- Esc: Close dropdown
- Tab: Move focus
State Management
<SfDropDownButton Content="@selectedText" @ref="dropdownRef">
<DropDownButtonEvents ItemSelected="OnSelect"></DropDownButtonEvents>
<DropDownMenuItems>
@foreach (var item in items)
{
<DropDownMenuItem Text="@item" Id="@item"></DropDownMenuItem>
}
</DropDownMenuItems>
</SfDropDownButton>
<button @onclick="ResetSelection">Reset</button>
@code {
private SfDropDownButton dropdownRef;
private string selectedText = "Select Option";
private List<string> items = new() { "Option 1", "Option 2", "Option 3" };
private void OnSelect(MenuEventArgs args)
{
selectedText = args.Item.Text;
}
private void ResetSelection()
{
selectedText = "Select Option";
StateHasChanged();
}
}Programmatic Control
<SfDropDownButton @ref="dropdown" Content="Controlled">
<DropDownMenuItems>
<DropDownMenuItem Text="Item 1"></DropDownMenuItem>
</DropDownMenuItems>
</SfDropDownButton>
<button @onclick="OpenMenu">Open</button>
<button @onclick="CloseMenu">Close</button>
@code {
private SfDropDownButton dropdown;
private async Task OpenMenu()
{
// Open programmatically if needed
}
private async Task CloseMenu()
{
// Close programmatically if needed
}
}Event Handling
<SfDropDownButton Content="Events">
<DropDownButtonEvents
Created="OnCreated"
OnOpen="OnBeforeOpen"
Opened="OnOpened"
OnClose="OnBeforeClose"
Closed="OnClosed"
ItemSelected="OnItemSelect">
</DropDownButtonEvents>
<DropDownMenuItems>
<DropDownMenuItem Text="Item 1"></DropDownMenuItem>
</DropDownMenuItems>
</SfDropDownButton>
@code {
private void OnCreated() => Console.WriteLine("Created");
private void OnBeforeOpen(BeforeOpenCloseMenuEventArgs args) => Console.WriteLine("Before Open");
private void OnOpened(OpenCloseMenuEventArgs args) => Console.WriteLine("Opened");
private void OnBeforeClose(BeforeOpenCloseMenuEventArgs args) => Console.WriteLine("Before Close");
private void OnClosed(OpenCloseMenuEventArgs args) => Console.WriteLine("Closed");
private void OnItemSelect(MenuEventArgs args) => Console.WriteLine($"Selected: {args.Item.Text}");
}Performance
Lazy Loading
<SfDropDownButton Content="Lazy Load">
<DropDownButtonEvents OnOpen="LoadItems"></DropDownButtonEvents>
<DropDownMenuItems>
@if (itemsLoaded)
{
@foreach (var item in items)
{
<DropDownMenuItem Text="@item"></DropDownMenuItem>
}
}
</DropDownMenuItems>
</SfDropDownButton>
@code {
private List<string> items = new();
private bool itemsLoaded = false;
private void LoadItems(BeforeOpenCloseMenuEventArgs args)
{
if (!itemsLoaded)
{
items = GetItemsFromDatabase();
itemsLoaded = true;
}
}
private List<string> GetItemsFromDatabase()
{
// Simulated data loading
return new List<string> { "Item 1", "Item 2", "Item 3" };
}
}Testing
[Fact]
public void DropdownMenu_Opens_OnClick()
{
var cut = RenderComponent<SfDropDownButton>();
var button = cut.Find("button");
button.Click();
var popup = cut.Find(".e-dropdown-popup");
Assert.NotNull(popup);
}---
See also: dropdown-menu-features.md | dropdown-menu-styling.md
Floating Action Button (FAB) - Advanced Features
Accessibility
ARIA Attributes
FAB automatically includes:
role="button"aria-labelfor screen readers- Focus indicators
<SfFab IconCss="e-icons e-edit" CssClass="accessible-fab"></SfFab>
<style>
.accessible-fab:focus-visible {
outline: 2px solid #0078d4;
outline-offset: 2px;
}
</style>Keyboard Navigation
- Space/Enter: Trigger FAB click
- Tab: Move focus to/from FAB
Event Handling
<SfFab IconCss="e-icons e-plus">
<FabEvents
Created="OnCreated"
OnClick="OnClick">
</FabEvents>
</SfFab>
@code {
private void OnCreated()
{
Console.WriteLine("FAB created");
}
private void OnClick(FabClickEventArgs args)
{
Console.WriteLine("FAB clicked");
// Perform action
}
}State Management
<SfFab IconCss="@fabIcon" Visible="@showFab">
<FabEvents OnClick="PerformAction"></FabEvents>
</SfFab>
@code {
private bool showFab = true;
private string fabIcon = "e-icons e-plus";
private async Task PerformAction(FabClickEventArgs args)
{
showFab = false;
await Task.Delay(2000);
showFab = true;
}
}Programmatic Control
<SfFab @ref="fabRef" IconCss="e-icons e-edit"></SfFab>
<button @onclick="TriggerFab">Trigger FAB</button>
@code {
private SfFab fabRef;
private async Task TriggerFab()
{
await fabRef.FocusAsync();
}
}Performance
Best practices: 1. Use single FAB per view 2. Minimize DOM updates 3. Lazy load action handlers 4. Use efficient event handlers
---
See also: fab-features.md | fab-styling.md