
Syncfusion Blazor Notifications
- 226 installs
- 4 repo stars
- Updated July 28, 2026
- syncfusion/blazor-ui-components-skills
Use syncfusion-blazor-notifications for development tasks
About
syncfusion-blazor-notifications: A skill for development. This provides functionality for development workflows.
- syncfusion-blazor-notifications
Syncfusion Blazor Notifications by the numbers
- 226 all-time installs (skills.sh)
- +14 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,757 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-notificationsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 226 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/blazor-ui-components-skills ↗ |
What it does
Use syncfusion-blazor-notifications for development tasks
Files
Implementing Syncfusion Blazor Notifications
A comprehensive skill for implementing Toast, Message, and Skeleton components from Syncfusion Blazor Notifications. These components provide essential user feedback mechanisms including temporary notifications, inline messages, and content loading states.
Component Overview
SfToast
Lightweight, non-blocking notification popups that appear temporarily to provide feedback. Supports positioning, animations, action buttons, and templates.
Key Features:
- Configurable positions (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right)
- Show/hide animations with customizable timing
- Auto-dismiss with timeout and progress bar
- Action buttons for user interaction
- Template support for custom content
- Event callbacks (OnOpen, OnClose, OnClick)
- Multiple toast stacking (newest on top/bottom)
SfMessage
Inline alert component for displaying contextual feedback messages with different severity levels.
Key Features:
- Severity types (Normal, Info, Success, Warning, Error) with color-coded styling
- Variants (Text, Outlined, Filled) for different visual styles
- Built-in icons matching severity levels
- Optional close button for dismissible messages
- Content alignment (left, center, right)
- Visibility control and close event handling
- Custom CSS styling support
SfSkeleton
Loading placeholder component that displays animated shapes to indicate content is loading.
Key Features:
- Shapes (Circle, Square, Rectangle, Text) for different content types
- Shimmer effects (Pulse, Wave, Fade, None) for visual feedback
- Configurable dimensions (width, height)
- Accessibility labels for screen readers
- Visibility toggle for showing/hiding
- CSS customization for styling
Quick Start
Toast - Basic Example
@page "/toast-demo"
@using Syncfusion.Blazor.Notifications
@using Syncfusion.Blazor.Buttons
<SfButton @onclick="ShowToast">Show Toast</SfButton>
<SfToast @ref="ToastObj" Title="Notification" Content="Your changes have been saved successfully!" />
@code {
SfToast ToastObj;
private async Task ShowToast()
{
await ToastObj.ShowAsync();
}
}Message - Basic Example
@page "/message-demo"
@using Syncfusion.Blazor.Notifications
<SfMessage Severity="MessageSeverity.Success" ShowIcon="true" ShowCloseIcon="true">
Your profile has been updated successfully!
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning" ShowIcon="true">
Your session will expire in 5 minutes.
</SfMessage>
<SfMessage Severity="MessageSeverity.Error" ShowIcon="true" ShowCloseIcon="true">
Failed to save changes. Please try again.
</SfMessage>Skeleton - Basic Example
@page "/skeleton-demo"
@using Syncfusion.Blazor.Notifications
<div class="skeleton-container">
<SfSkeleton Shape="SkeletonType.Circle" Width="60px" Height="60px" />
<div class="skeleton-text">
<SfSkeleton Shape="SkeletonType.Text" Width="80%" />
<SfSkeleton Shape="SkeletonType.Text" Width="60%" />
</div>
</div>
<style>
.skeleton-container {
display: flex;
gap: 16px;
padding: 16px;
}
.skeleton-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
</style>Documentation and Navigation Guide
Toast Component
Getting Started with Toast
📄 Read: references/toast-getting-started.md
- Installation and NuGet package setup
- Service registration in Program.cs
- Basic toast implementation
- Showing and hiding toasts programmatically
- Position configuration basics
- Simple success/error notifications
Advanced Toast Features
📄 Read: references/toast-features.md
- Position and alignment options (9 predefined positions)
- Animation settings (show/hide with effects and duration)
- Auto-dismiss with timeout and extended timeout
- Progress bar display and direction
- Action buttons with event handlers
- Title and content templates
- Multiple toast management and stacking order
- Event callbacks (OnOpen, OnClose, OnClick, BeforeOpen, BeforeClose)
- RTL (right-to-left) support
- Custom width, height, and CSS styling
- Target container specification
Message Component
Message Implementation
📄 Read: references/message-implementation.md
- Getting started with Message component
- Severity types (Normal, Info, Success, Warning, Error)
- Variant styles (Text, Outlined, Filled)
- Icon configuration (built-in and custom)
- Close icon and dismissal handling
- Content alignment options
- Visibility control and two-way binding
- Close event handling
- Custom content with ChildContent
- CSS class customization
- Accessibility features
- Integration with forms and validation
Skeleton Component
Skeleton Implementation
📄 Read: references/skeleton-implementation.md
- Getting started with Skeleton component
- Skeleton shapes (Circle, Square, Rectangle, Text)
- Shimmer effects (Pulse, Wave, Fade, None)
- Width and height configuration
- Visibility toggle for loading states
- Accessibility labels for screen readers
- Multiple skeleton layouts (lists, cards, tables)
- Common loading patterns (profile, article, grid)
- CSS customization and theming
- Performance considerations
Styling and Themes
Styling All Notification Components
📄 Read: references/styling-and-themes.md
- Theme integration (Bootstrap, Material, Fluent, Tailwind)
- Custom CSS classes for Toast
- Custom CSS classes for Message
- Custom CSS classes for Skeleton
- Dark mode support for all components
- Responsive design patterns
- Color customization
- Animation customization
- Component-specific styling techniques
Use Cases and Examples
Real-World Implementation Examples
📄 Read: references/use-cases-and-examples.md
- Form submission feedback (Toast + Message)
- E-commerce cart notifications
- Dashboard data updates
- File upload progress and feedback
- User authentication feedback
- Data loading with Skeleton placeholders
- Error handling and recovery
- Multi-step process feedback
- Notification center/history
- Combining all three components effectively
Common Patterns
Pattern 1: Success/Error Feedback with Toast
@code {
SfToast ToastObj;
private async Task SaveData()
{
try
{
await DataService.SaveAsync();
ToastObj.Title = "Success";
ToastObj.Content = "Data saved successfully!";
await ToastObj.ShowAsync();
}
catch (Exception ex)
{
ToastObj.Title = "Error";
ToastObj.Content = $"Failed to save: {ex.Message}";
await ToastObj.ShowAsync();
}
}
}Pattern 2: Form Validation with Message
<EditForm Model="@model" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
@if (!string.IsNullOrEmpty(errorMessage))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => errorMessage = null)">
@errorMessage
</SfMessage>
}
<!-- Form fields -->
</EditForm>Pattern 3: Loading State with Skeleton
@if (isLoading)
{
<div class="product-card-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="200px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80%" />
<SfSkeleton Shape="SkeletonType.Text" Width="60%" />
</div>
}
else
{
<div class="product-card">
<img src="@product.ImageUrl" alt="@product.Name" />
<h3>@product.Name</h3>
<p>@product.Description</p>
</div>
}Pattern 4: Multiple Toast Types
<SfToast @ref="SuccessToast"
Title="Success"
Icon="e-success"
CssClass="e-toast-success"
<ToastPosition X="Right" Y="Top"></ToastPositio
<SfToast @ref="ErrorToast"
Title="Error"
Icon="e-error"
CssClass="e-toast-danger"
<ToastPosition X="Right" Y="Top"></ToastPosition>
<SfToast @ref="WarningToast"
Title="Warning"
Icon="e-warning"
CssClass="e-toast-warning"
<ToastPosition X="Right" Y="Top"></ToastPosition>
@code {
SfToast SuccessToast, ErrorToast, WarningToast;
private async Task ShowSuccess(string message)
{
SuccessToast.Content = message;
await SuccessToast.ShowAsync();
}
private async Task ShowError(string message)
{
ErrorToast.Content = message;
await ErrorToast.ShowAsync();
}
private async Task ShowWarning(string message)
{
WarningToast.Content = message;
await WarningToast.ShowAsync();
}
}Installation
NuGet Package
dotnet add package Syncfusion.Blazor.Notifications
dotnet add package Syncfusion.Blazor.ThemesService Registration
Program.cs:
using Syncfusion.Blazor;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSyncfusionBlazor();Namespace Import
_Imports.razor:
@using Syncfusion.Blazor.NotificationsTheme Reference
App.razor or layout file:
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>Key Properties Reference
Toast Properties
| Property | Type | Description |
|---|---|---|
| Content | string | Toast message content |
| Title | string | Toast title text |
| Icon | string | Icon CSS class |
| Position | ToastPosition | X and Y position (Top, Bottom, Left, Right, Center) |
| Timeout | int | Auto-hide duration in milliseconds (default: 5000) |
| ShowCloseButton | bool | Display close button (default: false) |
| ShowProgressBar | bool | Display progress bar (default: false) |
| NewestOnTop | bool | Stack newest toast on top (default: true) |
| CssClass | string | Custom CSS classes |
Message Properties
| Property | Type | Description |
|---|---|---|
| Severity | MessageSeverity | Normal, Info, Success, Warning, Error |
| Variant | MessageVariant | Text, Outlined, Filled |
| ShowIcon | bool | Display severity icon (default: true) |
| ShowCloseIcon | bool | Display close button (default: false) |
| Visible | bool | Control visibility |
| ContentAlignment | HorizontalAlign | Left, Center, Right alignment |
| CssClass | string | Custom CSS classes |
Skeleton Properties
| Property | Type | Description |
|---|---|---|
| Shape | SkeletonType | Circle, Square, Rectangle, Text |
| Effect | ShimmerEffect | Pulse, Wave, Fade, None |
| Width | string | Width in px, %, or other CSS units |
| Height | string | Height in px, %, or other CSS units |
| Visible | bool | Control visibility |
| Label | string | Accessibility label |
| CssClass | string | Custom CSS classes |
Common Use Cases
1. Form Submission Feedback - Show success/error toast after form submission 2. Validation Messages - Display inline messages for validation errors 3. Loading States - Use skeleton during data fetching 4. Cart Operations - Toast notifications for add/remove from cart 5. File Upload - Progress feedback and completion notification 6. API Errors - Display error messages with retry options 7. Session Warnings - Show warning message before session expires 8. Data Refresh - Skeleton placeholders during refresh operations 9. Multi-Step Forms - Progress feedback at each step 10. Real-time Updates - Toast notifications for live data changes
Troubleshooting
Toast Not Appearing
- Ensure
ShowAsync()is called to display the toast - Verify toast component has content or template
- Check if toast timeout is too short
- Confirm ToastPosition is within viewport bounds
Message Not Visible
- Check
Visibleproperty is set totrue - Verify message has content in ChildContent or Content property
- Ensure component is rendered in DOM (not hidden by parent)
- Check CSS z-index conflicts
Skeleton Not Animating
- Verify
Effectproperty is set (Pulse, Wave, or Fade) - Check if shimmer effect CSS is loaded with theme
- Ensure skeleton has defined width and height
- Confirm
Visibleis set totrue
Theme Not Applied
- Verify theme CSS is referenced in App.razor or layout
- Check Syncfusion.Blazor.Themes package is installed
- Ensure CSS link is before component usage
- Try clearing browser cache
Service Not Registered
- Add
builder.Services.AddSyncfusionBlazor()in Program.cs - Verify Syncfusion.Blazor.Core package is installed
- Ensure using statement is present
- Rebuild project after adding service
Best Practices
1. Use appropriate notification type - Toast for temporary, Message for persistent 2. Limit toast duration - 3-7 seconds for most messages, longer for errors 3. Provide clear actions - Include retry/dismiss buttons where appropriate 4. Skeleton resemblance - Make skeleton shapes match actual content structure 5. Accessibility - Always provide labels for skeletons and icons 6. Position consistency - Use same toast position throughout app 7. Message severity - Use correct severity level for message context 8. Avoid overuse - Don't overwhelm users with too many notifications 9. Progressive enhancement - Use skeleton for perceived performance improvement 10. Error handling - Always handle toast/message display errors gracefully
Next Steps
- Explore Toast Advanced Features for animations and templates
- Learn Message Implementation for form validation
- Review Skeleton Patterns for loading states
- Check Styling Guide for customization
- See Real-World Examples for complete scenarios
Message - Complete Implementation Guide
Table of Contents
- Overview
- Getting Started
- Severity Types
- Variant Styles
- Icons
- Dismissible Messages
- Content Alignment
- Visibility Control
- Event Handling
- Form Integration
- Accessibility
---
Overview
The SfMessage component displays inline alert messages with contextual feedback. Unlike Toast (which is temporary and positioned), Message is persistent, inline, and remains visible until dismissed or hidden programmatically.
When to use Message:
- Form validation feedback that should stay visible
- Persistent status information (warnings, errors, success)
- Inline contextual help or informational banners
- Page-level alerts that don't auto-dismiss
- Multi-line messages with detailed information
When to use Toast instead:
- Temporary notifications that auto-dismiss
- Feedback that appears in a specific position (corner, center)
- Non-critical updates that shouldn't interrupt workflow
- Multiple stacked notifications
---
Getting Started
Installation
Same as Toast - requires Syncfusion.Blazor.Notifications package:
dotnet add package Syncfusion.Blazor.Notifications
dotnet add package Syncfusion.Blazor.ThemesBasic Message
@page "/message-basic"
@using Syncfusion.Blazor.Notifications
<SfMessage>
This is a message using ChildContent.
</SfMessage>Message with Severity
<SfMessage Severity="MessageSeverity.Info">
Information message
</SfMessage>
<SfMessage Severity="MessageSeverity.Success">
Success message
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning">
Warning message
</SfMessage>
<SfMessage Severity="MessageSeverity.Error">
Error message
</SfMessage>---
Severity Types
Messages come in five severity levels, each with distinct styling and semantic meaning.
Normal (Default)
<SfMessage Severity="MessageSeverity.Normal">
This is a normal message with neutral styling.
</SfMessage>Use for:
- General information
- Neutral updates
- Non-critical notifications
Info
<SfMessage Severity="MessageSeverity.Info" ShowIcon="true">
Your profile is set to private. Only connections can view your information.
</SfMessage>Use for:
- Informational content
- Tips and hints
- Feature explanations
- Status information
Success
<SfMessage Severity="MessageSeverity.Success" ShowIcon="true">
Your changes have been saved successfully!
</SfMessage>Use for:
- Successful operations
- Confirmation messages
- Completion notifications
- Positive feedback
Warning
<SfMessage Severity="MessageSeverity.Warning" ShowIcon="true">
Your subscription expires in 7 days. Renew now to avoid service interruption.
</SfMessage>Use for:
- Cautionary information
- Potential issues
- Expiring items
- Recommendations to act
Error
<SfMessage Severity="MessageSeverity.Error" ShowIcon="true">
Failed to save changes. Please check your input and try again.
</SfMessage>Use for:
- Error messages
- Failed operations
- Validation errors
- Critical issues
All Severity Types Example
@page "/message-severity"
<div class="message-demo">
<h3>Message Severity Types</h3>
<SfMessage Severity="MessageSeverity.Normal" ShowIcon="true">
<strong>Normal:</strong> This is a general message with neutral information.
</SfMessage>
<SfMessage Severity="MessageSeverity.Info" ShowIcon="true">
<strong>Info:</strong> Did you know? You can customize your dashboard layout.
</SfMessage>
<SfMessage Severity="MessageSeverity.Success" ShowIcon="true">
<strong>Success:</strong> Your profile has been updated successfully.
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning" ShowIcon="true">
<strong>Warning:</strong> Your password will expire in 3 days.
</SfMessage>
<SfMessage Severity="MessageSeverity.Error" ShowIcon="true">
<strong>Error:</strong> Unable to connect to the server. Please try again later.
</SfMessage>
</div>
<style>
.message-demo > * {
margin-bottom: 15px;
}
</style>---
Variant Styles
Messages support three visual variants: Text, Outlined, and Filled.
Text Variant (Default)
Subtle styling with colored text and border.
<SfMessage Severity="MessageSeverity.Info"
Variant="MessageVariant.Text"
ShowIcon="true">
Text variant with subtle background
</SfMessage>Outlined Variant
Emphasized border with transparent background.
<SfMessage Severity="MessageSeverity.Success"
Variant="MessageVariant.Outlined"
ShowIcon="true">
Outlined variant with prominent border
</SfMessage>Filled Variant
Solid background with white text.
<SfMessage Severity="MessageSeverity.Warning"
Variant="MessageVariant.Filled"
ShowIcon="true">
Filled variant with solid background
</SfMessage>All Variants Comparison
@page "/message-variants"
<h4>Info Messages</h4>
<SfMessage Severity="MessageSeverity.Info" Variant="MessageVariant.Text" ShowIcon="true">
Info - Text Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Info" Variant="MessageVariant.Outlined" ShowIcon="true">
Info - Outlined Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Info" Variant="MessageVariant.Filled" ShowIcon="true">
Info - Filled Variant
</SfMessage>
<h4>Success Messages</h4>
<SfMessage Severity="MessageSeverity.Success" Variant="MessageVariant.Text" ShowIcon="true">
Success - Text Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Success" Variant="MessageVariant.Outlined" ShowIcon="true">
Success - Outlined Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Success" Variant="MessageVariant.Filled" ShowIcon="true">
Success - Filled Variant
</SfMessage>
<h4>Warning Messages</h4>
<SfMessage Severity="MessageSeverity.Warning" Variant="MessageVariant.Text" ShowIcon="true">
Warning - Text Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning" Variant="MessageVariant.Outlined" ShowIcon="true">
Warning - Outlined Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning" Variant="MessageVariant.Filled" ShowIcon="true">
Warning - Filled Variant
</SfMessage>
<h4>Error Messages</h4>
<SfMessage Severity="MessageSeverity.Error" Variant="MessageVariant.Text" ShowIcon="true">
Error - Text Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Error" Variant="MessageVariant.Outlined" ShowIcon="true">
Error - Outlined Variant
</SfMessage>
<SfMessage Severity="MessageSeverity.Error" Variant="MessageVariant.Filled" ShowIcon="true">
Error - Filled Variant
</SfMessage>
<style>
h4 {
margin-top: 20px;
margin-bottom: 10px;
}
.e-message {
margin-bottom: 10px;
}
</style>---
Icons
Control icon display with built-in or custom icons.
Built-in Icons
Each severity has a default icon that appears when ShowIcon is enabled.
<SfMessage Severity="MessageSeverity.Info" ShowIcon="true">
Info with built-in icon (ℹ️)
</SfMessage>
<SfMessage Severity="MessageSeverity.Success" ShowIcon="true">
Success with built-in icon (✓)
</SfMessage>
<SfMessage Severity="MessageSeverity.Warning" ShowIcon="true">
Warning with built-in icon (⚠️)
</SfMessage>
<SfMessage Severity="MessageSeverity.Error" ShowIcon="true">
Error with built-in icon (✕)
</SfMessage>Hide Icons
<SfMessage Severity="MessageSeverity.Info" ShowIcon="false">
Message without icon
</SfMessage>Custom Icons with CSS
<SfMessage Severity="MessageSeverity.Info" ShowIcon="true" CssClass="custom-icon-message">
Message with custom icon
</SfMessage>
<style>
.custom-icon-message .e-msg-icon::before {
content: "🎉";
font-size: 20px;
}
</style>---
Dismissible Messages
Allow users to close messages with a close button.
Basic Dismissible Message
<SfMessage Severity="MessageSeverity.Info"
ShowIcon="true"
ShowCloseIcon="true">
You can close this message by clicking the X button.
</SfMessage>Close Event Handling
@page "/message-dismissible"
@if (showMessage)
{
<SfMessage Severity="MessageSeverity.Success"
ShowIcon="true"
ShowCloseIcon="true"
Closed="OnMessageClosed">
Your settings have been saved. This message can be dismissed.
</SfMessage>
}
<SfButton @onclick="ShowMessageAgain" Disabled="@showMessage">
Show Message Again
</SfButton>
@code {
private bool showMessage = true;
private void OnMessageClosed(MessageCloseEventArgs args)
{
showMessage = false;
Console.WriteLine("Message was closed by user");
// Can perform actions like logging, analytics, etc.
}
private void ShowMessageAgain()
{
showMessage = true;
}
}Multiple Dismissible Messages
@page "/message-multiple"
@if (showInfoMessage)
{
<SfMessage Severity="MessageSeverity.Info"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showInfoMessage = false)">
<strong>Tip:</strong> You can customize your dashboard layout.
</SfMessage>
}
@if (showWarningMessage)
{
<SfMessage Severity="MessageSeverity.Warning"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showWarningMessage = false)">
<strong>Reminder:</strong> Complete your profile for better matches.
</SfMessage>
}
@if (showErrorMessage)
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showErrorMessage = false)">
<strong>Error:</strong> Failed to load user preferences.
</SfMessage>
}
<SfButton @onclick="ShowAllMessages">Show All Messages</SfButton>
@code {
private bool showInfoMessage = true;
private bool showWarningMessage = true;
private bool showErrorMessage = true;
private void ShowAllMessages()
{
showInfoMessage = true;
showWarningMessage = true;
showErrorMessage = true;
}
}---
Content Alignment
Control horizontal alignment of message content.
<SfMessage ContentAlignment="HorizontalAlign.Left"
ShowIcon="true">
Left aligned (default)
</SfMessage>
<SfMessage ContentAlignment="HorizontalAlign.Center"
ShowIcon="true">
Center aligned
</SfMessage>
<SfMessage ContentAlignment="HorizontalAlign.Right"
ShowIcon="true">
Right aligned
</SfMessage>---
Visibility Control
Dynamically show/hide messages with the Visible property.
Programmatic Visibility
@page "/message-visibility"
<SfButton @onclick="ToggleMessage">
@(messageVisible ? "Hide" : "Show") Message
</SfButton>
<SfMessage Severity="MessageSeverity.Info"
ShowIcon="true"
Visible="@messageVisible">
This message visibility is controlled programmatically.
</SfMessage>
@code {
private bool messageVisible = true;
private void ToggleMessage()
{
messageVisible = !messageVisible;
}
}Two-Way Binding
<SfMessage Severity="MessageSeverity.Warning"
ShowIcon="true"
ShowCloseIcon="true"
@bind-Visible="messageVisible">
Two-way binding allows tracking visibility changes.
</SfMessage>
@code {
private bool messageVisible = true;
protected override void OnParametersSet()
{
if (!messageVisible)
{
SaveUserPreference("message_dismissed", true);
}
}
private void SaveUserPreference(string key, bool value)
{
Console.WriteLine($"{key} saved with value: {value}");
}
}Conditional Display
@page "/message-conditional"
<EditForm Model="@user" OnValidSubmit="SaveUser">
<DataAnnotationsValidator />
@if (!string.IsNullOrEmpty(errorMessage))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => errorMessage = null)">
@errorMessage
</SfMessage>
}
@if (!string.IsNullOrEmpty(successMessage))
{
<SfMessage Severity="MessageSeverity.Success"
ShowIcon="true">
@successMessage
</SfMessage>
}
<!-- Form fields -->
<SfButton Type="ButtonType.Submit">Save</SfButton>
</EditForm>
@code {
private User user = new();
private string errorMessage;
private string successMessage;
private async Task SaveUser()
{
try
{
await UserService.SaveAsync(user);
successMessage = "User saved successfully!";
errorMessage = null;
}
catch (Exception ex)
{
errorMessage = $"Failed to save user: {ex.Message}";
successMessage = null;
}
}
}---
Event Handling
Closed Event
Triggered when user closes the message by clicking the close icon.
<SfMessage Severity="MessageSeverity.Info"
ShowCloseIcon="true"
Closed="OnClosed">
Close this message
</SfMessage>
@code {
private void OnClosed(MessageCloseEventArgs args)
{
// Event triggered when close icon is clicked
Console.WriteLine("Message closed");
// Can access event data
// args contains information about the close event
}
}---
Form Integration
Messages work excellently for form validation feedback.
Form Validation Example
@page "/form-validation"
@using System.ComponentModel.DataAnnotations
<EditForm Model="@model" OnValidSubmit="HandleValidSubmit" OnInvalidSubmit="HandleInvalidSubmit">
<DataAnnotationsValidator />
@if (showSuccessMessage)
{
<SfMessage Severity="MessageSeverity.Success"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showSuccessMessage = false)">
<strong>Success!</strong> Your registration has been submitted.
</SfMessage>
}
@if (showErrorMessage)
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showErrorMessage = false)">
<strong>Error!</strong> Please correct the errors below and try again.
</SfMessage>
}
<div class="form-group">
<label>Email:</label>
<InputText @bind-Value="model.Email" class="form-control" />
<ValidationMessage For="@(() => model.Email)" />
</div>
<div class="form-group">
<label>Password:</label>
<InputText @bind-Value="model.Password" type="password" class="form-control" />
<ValidationMessage For="@(() => model.Password)" />
</div>
<SfButton Type="ButtonType.Submit">Submit</SfButton>
</EditForm>
@code {
private RegistrationModel model = new();
private bool showSuccessMessage = false;
private bool showErrorMessage = false;
private async Task HandleValidSubmit()
{
try
{
await SubmitRegistration();
showSuccessMessage = true;
showErrorMessage = false;
}
catch
{
showSuccessMessage = false;
showErrorMessage = true;
}
}
private void HandleInvalidSubmit()
{
showErrorMessage = true;
showSuccessMessage = false;
}
public class RegistrationModel
{
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Invalid email format")]
public string Email { get; set; }
[Required(ErrorMessage = "Password is required")]
[MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
public string Password { get; set; }
}
}API Error Display
@code {
private string apiErrorMessage;
private async Task LoadData()
{
try
{
var data = await ApiService.GetDataAsync();
apiErrorMessage = null; // Clear any previous errors
}
catch (HttpRequestException ex)
{
apiErrorMessage = $"Network error: {ex.Message}";
}
catch (Exception ex)
{
apiErrorMessage = $"An error occurred: {ex.Message}";
}
}
}
@if (!string.IsNullOrEmpty(apiErrorMessage))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => apiErrorMessage = null)">
@apiErrorMessage
</SfMessage>
}---
Accessibility
Messages are designed with accessibility in mind.
ARIA Attributes
Messages automatically include appropriate ARIA roles and attributes:
<!-- Rendered HTML includes: -->
<!-- role="alert" for error/warning -->
<!-- aria-live="polite" for info/success -->Screen Reader Support
<SfMessage Severity="MessageSeverity.Error" ShowIcon="true">
<span class="sr-only">Error:</span>
Your session has expired. Please log in again.
</SfMessage>
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
</style>Keyboard Navigation
Close icons are keyboard accessible:
- Tab: Focus the close button
- Enter/Space: Close the message
---
Best Practices
1. Choose appropriate severity - Match severity to message importance 2. Use icons - Enable icons for better visual recognition 3. Be concise - Keep messages brief and actionable 4. Provide actions - Include buttons or links for user response when appropriate 5. Don't overuse - Too many messages can overwhelm users 6. Consider persistence - Use Message for important info that shouldn't auto-dismiss 7. Test accessibility - Verify screen reader compatibility 8. Position strategically - Place messages near relevant content 9. Clear errors - Remove error messages when issues are resolved 10. Consistent styling - Use same variant throughout your app
---
Next Steps
- Explore Toast Component for temporary notifications
- Learn Skeleton Component for loading states
- Review Styling Guide for customization
- See Use Cases for complete examples
Skeleton - Complete Implementation Guide
This guide covers the Syncfusion Blazor Skeleton component for creating loading placeholders with shimmer effects.
Overview
The SfSkeleton component displays animated placeholder shapes while content is loading, providing visual feedback and improving perceived performance. It's a modern alternative to traditional loading spinners.
When to use Skeleton:
- Content is loading asynchronously (API calls, images, data)
- Need to show layout structure before content arrives
- Want to improve perceived performance
- Display loading state for cards, lists, profiles, articles
- Replace spinning loaders with content-aware placeholders
Benefits:
- Better UX - Users see layout structure immediately
- Reduced perceived wait time
- Content-aware loading (shapes match actual content)
- Professional, modern appearance
- Accessibility-friendly loading states
---
Getting Started
Installation
Requires Syncfusion.Blazor.Notifications package:
dotnet add package Syncfusion.Blazor.Notifications
dotnet add package Syncfusion.Blazor.ThemesBasic Skeleton
@page "/skeleton-basic"
@using Syncfusion.Blazor.Notifications
<SfSkeleton />This creates a default rectangular skeleton with pulse animation.
Skeleton with Dimensions
<SfSkeleton Width="100%" Height="100px" />
<SfSkeleton Width="200px" Height="200px" />
<SfSkeleton Width="80%" Height="50px" />---
Skeleton Shapes
The Skeleton supports four predefined shapes for different content types.
Rectangle (Default)
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="150px" />Use for:
- Images
- Video thumbnails
- Cards
- Banners
- Content blocks
Circle
<SfSkeleton Shape="SkeletonType.Circle" Width="60px" Height="60px" />Use for:
- Avatar placeholders
- Profile pictures
- Icon placeholders
- Status indicators
- Circular images
Square
<SfSkeleton Shape="SkeletonType.Square" Width="100px" Height="100px" />Use for:
- Square thumbnails
- App icons
- Product images
- Grid items
- Tile layouts
Text
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="15px" />Use for:
- Text lines
- Headings
- Paragraphs
- Labels
- Descriptions
All Shapes Example
@page "/skeleton-shapes"
<div class="skeleton-demo">
<h3>Skeleton Shapes</h3>
<div class="shape-example">
<h4>Circle</h4>
<SfSkeleton Shape="SkeletonType.Circle" Width="80px" Height="80px" />
</div>
<div class="shape-example">
<h4>Square</h4>
<SfSkeleton Shape="SkeletonType.Square" Width="100px" Height="100px" />
</div>
<div class="shape-example">
<h4>Rectangle</h4>
<SfSkeleton Shape="SkeletonType.Rectangle" Width="200px" Height="120px" />
</div>
<div class="shape-example">
<h4>Text</h4>
<SfSkeleton Shape="SkeletonType.Text" Width="250px" Height="15px" />
<SfSkeleton Shape="SkeletonType.Text" Width="200px" Height="30px" />
<SfSkeleton Shape="SkeletonType.Text" Width="180px" Height="45px" />
</div>
</div>
<style>
.skeleton-demo {
padding: 20px;
}
.shape-example {
margin-bottom: 30px;
}
.shape-example h4 {
margin-bottom: 10px;
color: #666;
}
</style>---
Shimmer Effects
Control the animation style with different shimmer effects.
Pulse (Default)
Fading in and out animation.
<SfSkeleton Effect="ShimmerEffect.Pulse" Width="100%" Height="100px" />Best for:
- General loading states
- Subtle animations
- Conservative designs
Wave
Shimmer wave moving across the skeleton.
<SfSkeleton Effect="ShimmerEffect.Wave" Width="100%" Height="100px" />Best for:
- Modern, dynamic appearance
- Grabbing attention
- Loading larger content blocks
Fade
Smooth fade in/out effect.
<SfSkeleton Effect="ShimmerEffect.Fade" Width="100%" Height="100px" />Best for:
- Gentle, subtle loading
- Minimalist designs
- Background loading
None
No animation.
<SfSkeleton Effect="ShimmerEffect.None" Width="100%" Height="100px" />Best for:
- Static placeholders
- Performance-critical scenarios
- High-density layouts with many skeletons
Effects Comparison
@page "/skeleton-effects"
<div class="effects-demo">
<h3>Shimmer Effects</h3>
<div class="effect-row">
<div class="effect-item">
<h4>Pulse</h4>
<SfSkeleton Effect="ShimmerEffect.Pulse" Width="100%" Height="80px" />
</div>
<div class="effect-item">
<h4>Wave</h4>
<SfSkeleton Effect="ShimmerEffect.Wave" Width="100%" Height="80px" />
</div>
<div class="effect-item">
<h4>Fade</h4>
<SfSkeleton Effect="ShimmerEffect.Fade" Width="100%" Height="80px" />
</div>
<div class="effect-item">
<h4>None</h4>
<SfSkeleton Effect="ShimmerEffect.None" Width="100%" Height="80px" />
</div>
</div>
</div>
<style>
.effects-demo {
padding: 20px;
}
.effect-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.effect-item h4 {
margin-bottom: 10px;
color: #666;
}
</style>---
Visibility Control
Toggle skeleton visibility based on loading state.
Basic Toggle
@page "/skeleton-visibility"
<SfButton @onclick="ToggleLoading">
@(isLoading ? "Stop" : "Start") Loading
</SfButton>
<div class="content-area">
<SfSkeleton Visible="@isLoading" Width="100%" Height="100px" />
@if (!isLoading)
{
<div class="actual-content">
<h3>Content Loaded</h3>
<p>This is the actual content that replaced the skeleton.</p>
</div>
}
</div>
@code {
private bool isLoading = true;
private void ToggleLoading()
{
isLoading = !isLoading;
}
}Async Data Loading
@page "/skeleton-data"
@if (isLoading)
{
<div class="product-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="200px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80%" />
<SfSkeleton Shape="SkeletonType.Text" Width="60%" />
</div>
}
else if (product != null)
{
<div class="product-card">
<img src="@product.ImageUrl" alt="@product.Name" />
<h3>@product.Name</h3>
<p>@product.Description</p>
<p class="price">$@product.Price</p>
</div>
}
@code {
private bool isLoading = true;
private Product product;
protected override async Task OnInitializedAsync()
{
await LoadProduct();
}
private async Task LoadProduct()
{
isLoading = true;
await Task.Delay(2000); // Simulate API call
product = new Product
{
Name = "Wireless Headphones",
Description = "Premium sound quality with noise cancellation",
Price = 199.99m,
ImageUrl = "/images/product.jpg"
};
isLoading = false;
}
private class Product
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
}
<style>
.product-skeleton, .product-card {
max-width: 300px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
}
.product-skeleton > * {
margin-bottom: 10px;
}
.product-card img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 10px;
}
.price {
font-size: 24px;
font-weight: bold;
color: #2196F3;
}
</style>---
Common Loading Patterns
Profile Card Skeleton
<div class="profile-skeleton">
<SfSkeleton Shape="SkeletonType.Circle" Width="80px" Height="80px" />
<div class="profile-text">
<SfSkeleton Shape="SkeletonType.Text" Width="60%" Height="20px" />
<SfSkeleton Shape="SkeletonType.Text" Width="40%" Height="16px" />
</div>
</div>
<style>
.profile-skeleton {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.profile-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
</style>Article Skeleton
<div class="article-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="250px" />
<SfSkeleton Shape="SkeletonType.Text" Width="90%" Height="28px" />
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="75%" Height="16px" />
</div>
<style>
.article-skeleton {
max-width: 600px;
display: flex;
flex-direction: column;
gap: 12px;
}
</style>List Skeleton
<div class="list-skeleton">
@for (int i = 0; i < 5; i++)
{
<div class="list-item-skeleton">
<SfSkeleton Shape="SkeletonType.Circle" Width="40px" Height="40px" />
<div class="list-item-text">
<SfSkeleton Shape="SkeletonType.Text" Width="70%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="50%" Height="14px" />
</div>
</div>
}
</div>
<style>
.list-skeleton {
max-width: 400px;
}
.list-item-skeleton {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
border-bottom: 1px solid #f0f0f0;
}
.list-item-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
</style>Grid Skeleton
<div class="grid-skeleton">
@for (int i = 0; i < 6; i++)
{
<div class="grid-item-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="180px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80%" Height="18px" />
<SfSkeleton Shape="SkeletonType.Text" Width="60%" Height="14px" />
</div>
}
</div>
<style>
.grid-skeleton {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.grid-item-skeleton {
display: flex;
flex-direction: column;
gap: 8px;
}
</style>Table Skeleton
<div class="table-skeleton">
<div class="table-header-skeleton">
<SfSkeleton Shape="SkeletonType.Text" Width="100px" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="120px" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80px" Height="16px" />
</div>
@for (int i = 0; i < 4; i++)
{
<div class="table-row-skeleton">
<SfSkeleton Shape="SkeletonType.Text" Width="100px" Height="14px" />
<SfSkeleton Shape="SkeletonType.Text" Width="120px" Height="14px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80px" Height="14px" />
</div>
}
</div>
<style>
.table-skeleton {
max-width: 600px;
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.table-header-skeleton {
display: flex;
gap: 20px;
padding: 16px;
background: #f5f5f5;
border-bottom: 1px solid #e0e0e0;
}
.table-row-skeleton {
display: flex;
gap: 20px;
padding: 16px;
border-bottom: 1px solid #f0f0f0;
}
.table-row-skeleton:last-child {
border-bottom: none;
}
</style>---
Accessibility
Accessibility Labels
Provide descriptive labels for screen readers.
<SfSkeleton Shape="SkeletonType.Circle"
Width="60px"
Height="60px"
Label="Loading user profile picture" />
<SfSkeleton Shape="SkeletonType.Text"
Width="100%"
Label="Loading article content" />ARIA Attributes
Skeletons automatically include aria-busy="true" and aria-label attributes for accessibility.
Complete Accessible Pattern
<div role="region" aria-busy="@isLoading" aria-label="Product information">
@if (isLoading)
{
<div class="product-skeleton" aria-label="Loading product details">
<SfSkeleton Shape="SkeletonType.Rectangle"
Width="100%"
Height="200px"
Label="Loading product image" />
<SfSkeleton Shape="SkeletonType.Text"
Width="80%"
Label="Loading product name" />
<SfSkeleton Shape="SkeletonType.Text"
Width="60%"
Label="Loading product description" />
</div>
}
else
{
<!-- Actual content -->
}
</div>---
Custom Styling
Custom Colors
<SfSkeleton Width="100%"
Height="100px"
CssClass="custom-skeleton-color" />
<style>
.custom-skeleton-color {
background: linear-gradient(90deg, #e3f2fd 0%, #bbdefb 50%, #e3f2fd 100%);
}
</style>Custom Animation Duration
<SfSkeleton Width="100%"
Height="100px"
CssClass="slow-animation" />
<style>
.slow-animation {
animation-duration: 3s !important;
}
</style>Rounded Corners
<SfSkeleton Width="100%"
Height="100px"
CssClass="rounded-skeleton" />
<style>
.rounded-skeleton {
border-radius: 12px;
}
</style>---
Complete Working Examples
E-commerce Product Grid
@page "/skeleton-ecommerce"
<div class="product-grid">
@if (isLoading)
{
@for (int i = 0; i < 8; i++)
{
<div class="product-card-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="200px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80%" Height="20px" />
<SfSkeleton Shape="SkeletonType.Text" Width="60%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="40%" Height="24px" />
</div>
}
}
else
{
@foreach (var product in products)
{
<div class="product-card">
<img src="@product.ImageUrl" alt="@product.Name" />
<h3>@product.Name</h3>
<p>@product.Description</p>
<p class="price">$@product.Price</p>
</div>
}
}
</div>
@code {
private bool isLoading = true;
private List<Product> products = new();
protected override async Task OnInitializedAsync()
{
await LoadProducts();
}
private async Task LoadProducts()
{
await Task.Delay(2000); // Simulate API call
// Load products...
isLoading = false;
}
}
<style>
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
padding: 20px;
}
.product-card-skeleton, .product-card {
padding: 15px;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.product-card-skeleton > * {
margin-bottom: 10px;
}
</style>Social Media Feed
@page "/skeleton-social"
<div class="feed-container">
@if (isLoading)
{
@for (int i = 0; i < 3; i++)
{
<div class="post-skeleton">
<div class="post-header-skeleton">
<SfSkeleton Shape="SkeletonType.Circle" Width="50px" Height="50px" />
<div class="header-text">
<SfSkeleton Shape="SkeletonType.Text" Width="150px" Height="18px" />
<SfSkeleton Shape="SkeletonType.Text" Width="100px" Height="14px" />
</div>
</div>
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Text" Width="90%" Height="16px" />
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="300px" />
</div>
}
}
else
{
<!-- Actual posts -->
}
</div>
<style>
.feed-container {
max-width: 600px;
margin: 0 auto;
}
.post-skeleton {
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: white;
}
.post-header-skeleton {
display: flex;
gap: 12px;
margin-bottom: 15px;
}
.header-text {
display: flex;
flex-direction: column;
gap: 6px;
}
.post-skeleton > .e-skeleton {
margin-bottom: 10px;
}
</style>---
Performance Considerations
1. Use `Effect="ShimmerEffect.None"` for high-density layouts with many skeletons 2. Limit skeleton count - Don't render hundreds of skeletons at once 3. Match skeleton to content - Skeleton should closely resemble actual content 4. Remove from DOM - Hide skeletons when content loads (Visible="false" or conditional rendering) 5. Optimize dimensions - Use percentage widths where possible
---
Best Practices
1. Match content structure - Skeleton shapes should mirror actual content layout 2. Consistent timing - Show skeleton for at least 300-500ms for smooth transition 3. Progressive loading - Show content as it loads, not all at once 4. Appropriate effects - Use Wave for attention, Pulse for subtlety 5. Accessibility first - Always provide descriptive labels 6. Mobile responsive - Ensure skeletons work on all screen sizes 7. Avoid overuse - Don't skeleton everything; use for main content only 8. Test loading states - Throttle network to test skeleton appearance 9. Gradual reveal - Fade in content when replacing skeleton 10. Combine with suspense - Use with error boundaries for robust loading states
---
Next Steps
- Learn about Toast Notifications for feedback messages
- Explore Message Component for inline alerts
- Review Styling Guide for customization
- See Use Cases for combined examples
Styling and Themes
Table of Contents
- Theme Integration
- Toast Styling
- Message Styling
- Skeleton Styling
- Dark Mode Support
- Responsive Design
- Custom Themes
---
Theme Integration
Syncfusion Blazor components support multiple built-in themes that provide consistent styling across all notification components.
Available Themes
<!-- Bootstrap 5 (Default) -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
<!-- Material Design -->
<link href="_content/Syncfusion.Blazor.Themes/material.css" rel="stylesheet" />
<!-- Fluent UI (Microsoft) -->
<link href="_content/Syncfusion.Blazor.Themes/fluent.css" rel="stylesheet" />
<!-- Tailwind CSS -->
<link href="_content/Syncfusion.Blazor.Themes/tailwind.css" rel="stylesheet" />
<!-- Office Fabric -->
<link href="_content/Syncfusion.Blazor.Themes/fabric.css" rel="stylesheet" />
<!-- High Contrast (Accessibility) -->
<link href="_content/Syncfusion.Blazor.Themes/highcontrast.css" rel="stylesheet" />Dark Mode Variants
<!-- Bootstrap 5 Dark -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5-dark.css" rel="stylesheet" />
<!-- Material Dark -->
<link href="_content/Syncfusion.Blazor.Themes/material-dark.css" rel="stylesheet" />
<!-- Fluent Dark -->
<link href="_content/Syncfusion.Blazor.Themes/fluent-dark.css" rel="stylesheet" />
<!-- Tailwind Dark -->
<link href="_content/Syncfusion.Blazor.Themes/tailwind-dark.css" rel="stylesheet" />
<!-- Fabric Dark -->
<link href="_content/Syncfusion.Blazor.Themes/fabric-dark.css" rel="stylesheet" />Theme Switcher Implementation
@page "/theme-switcher"
<div class="theme-selector">
<label>Select Theme:</label>
<select @onchange="OnThemeChange">
<option value="bootstrap5">Bootstrap 5</option>
<option value="material">Material</option>
<option value="fluent">Fluent</option>
<option value="tailwind">Tailwind</option>
<option value="fabric">Fabric</option>
</select>
<label>
<input type="checkbox" @onchange="ToggleDarkMode" />
Dark Mode
</label>
</div>
@code {
private string currentTheme = "bootstrap5";
private bool isDarkMode = false;
private void OnThemeChange(ChangeEventArgs e)
{
currentTheme = e.Value.ToString();
UpdateTheme();
}
private void ToggleDarkMode(ChangeEventArgs e)
{
isDarkMode = (bool)e.Value;
UpdateTheme();
}
private void UpdateTheme()
{
var themeName = isDarkMode ? $"{currentTheme}-dark" : currentTheme;
// Update theme reference in App.razor or use JavaScript interop
JSRuntime.InvokeVoidAsync("changeTheme", themeName);
}
}---
Toast Styling
Built-in CSS Classes
Syncfusion Toast provides predefined CSS classes for different message types:
<!-- Success Toast -->
<SfToast CssClass="e-toast-success" Title="Success" Content="Operation completed" />
<!-- Info Toast -->
<SfToast CssClass="e-toast-info" Title="Info" Content="New update available" />
<!-- Warning Toast -->
<SfToast CssClass="e-toast-warning" Title="Warning" Content="Session expiring soon" />
<!-- Danger/Error Toast -->
<SfToast CssClass="e-toast-danger" Title="Error" Content="Failed to save" />Custom Toast Colors
<SfToast CssClass="custom-toast-primary" Title="Custom" Content="Custom styled toast" />
<style>
.custom-toast-primary {
background-color: #6200EA;
color: white;
}
.custom-toast-primary .e-toast-title {
color: white;
font-weight: 600;
}
.custom-toast-primary .e-toast-message {
color: rgba(255, 255, 255, 0.9);
}
.custom-toast-primary .e-toast-icon {
color: white;
}
</style>Toast Shadow and Border
<SfToast CssClass="elevated-toast" Title="Elevated" Content="Toast with custom shadow" />
<style>
.elevated-toast {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
border: 2px solid #2196F3;
border-radius: 12px;
}
</style>Toast Typography
<SfToast CssClass="custom-typography" Title="Custom Font" Content="Toast with custom typography" />
<style>
.custom-typography .e-toast-title {
font-family: 'Arial', sans-serif;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.5px;
}
.custom-typography .e-toast-message {
font-family: 'Georgia', serif;
font-size: 15px;
line-height: 1.6;
}
</style>Gradient Toast
<SfToast CssClass="gradient-toast" Title="Gradient" Content="Beautiful gradient background" />
<style>
.gradient-toast {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.gradient-toast .e-toast-title,
.gradient-toast .e-toast-message {
color: white;
}
</style>Toast Icon Styling
<SfToast Icon="e-custom-icon" CssClass="custom-icon-toast" Title="Custom Icon" Content="With styled icon" />
<style>
.custom-icon-toast .e-toast-icon {
font-size: 28px;
color: #FF5722;
}
.custom-icon-toast .e-toast-icon::before {
content: "🎉";
}
</style>Positioning Offset
<SfToast CssClass="offset-toast"
Position="new ToastPosition { X = "Right", Y = "Top" }"
Title="Offset"
Content="Custom positioning" />
<style>
.offset-toast {
margin: 20px;
}
</style>---
Message Styling
Custom Message Colors
<SfMessage CssClass="custom-message-blue" ShowIcon="true">
Custom blue message
</SfMessage>
<style>
.custom-message-blue {
background-color: #E3F2FD;
border-left: 4px solid #2196F3;
color: #1565C0;
}
.custom-message-blue .e-msg-icon {
color: #2196F3;
}
</style>Message with Custom Icon
<SfMessage CssClass="custom-icon-message" ShowIcon="true">
Message with custom icon
</SfMessage>
<style>
.custom-icon-message .e-msg-icon::before {
content: "⭐";
font-size: 24px;
}
</style>Message Border Styles
<SfMessage CssClass="border-message" ShowIcon="true">
Message with custom border
</SfMessage>
<style>
.border-message {
border: 2px dashed #FF9800;
border-radius: 8px;
padding: 16px;
}
</style>Message Spacing
<SfMessage CssClass="spaced-message" ShowIcon="true">
Message with custom spacing
</SfMessage>
<style>
.spaced-message {
padding: 20px;
margin: 20px 0;
line-height: 1.8;
}
.spaced-message .e-msg-icon {
margin-right: 16px;
}
</style>Severity-Specific Styling
<style>
/* Custom Error Message */
.e-message.e-error {
background-color: #FFEBEE;
border-left: 5px solid #F44336;
}
.e-message.e-error .e-msg-icon {
color: #F44336;
font-size: 24px;
}
/* Custom Success Message */
.e-message.e-success {
background-color: #E8F5E9;
border-left: 5px solid #4CAF50;
}
.e-message.e-success .e-msg-icon {
color: #4CAF50;
}
/* Custom Warning Message */
.e-message.e-warning {
background-color: #FFF3E0;
border-left: 5px solid #FF9800;
}
.e-message.e-warning .e-msg-icon {
color: #FF9800;
}
/* Custom Info Message */
.e-message.e-info {
background-color: #E1F5FE;
border-left: 5px solid #03A9F4;
}
.e-message.e-info .e-msg-icon {
color: #03A9F4;
}
</style>---
Skeleton Styling
Custom Skeleton Colors
<SfSkeleton CssClass="custom-skeleton-blue" Width="100%" Height="100px" />
<style>
.custom-skeleton-blue {
background: linear-gradient(90deg, #E3F2FD 0%, #BBDEFB 50%, #E3F2FD 100%);
background-size: 200% 100%;
}
</style>Skeleton Animation Speed
<SfSkeleton CssClass="slow-skeleton" Width="100%" Height="100px" />
<SfSkeleton CssClass="fast-skeleton" Width="100%" Height="100px" />
<style>
.slow-skeleton {
animation-duration: 3s !important;
}
.fast-skeleton {
animation-duration: 0.8s !important;
}
</style>Skeleton Border Radius
<SfSkeleton CssClass="rounded-skeleton" Width="100%" Height="100px" />
<style>
.rounded-skeleton {
border-radius: 16px;
}
</style>Custom Wave Effect
<SfSkeleton Effect="ShimmerEffect.Wave" CssClass="custom-wave" Width="100%" Height="100px" />
<style>
.custom-wave {
background: linear-gradient(
90deg,
#f0f0f0 0%,
#e0e0e0 20%,
#f0f0f0 40%,
#f0f0f0 100%
);
background-size: 200% 100%;
animation-duration: 1.5s;
}
</style>Skeleton Opacity
<SfSkeleton CssClass="subtle-skeleton" Width="100%" Height="100px" />
<style>
.subtle-skeleton {
opacity: 0.3;
}
</style>---
Dark Mode Support
Auto Dark Mode Detection
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var isDarkMode = await JSRuntime.InvokeAsync<bool>("matchMedia", "(prefers-color-scheme: dark)").matches;
if (isDarkMode)
{
await ApplyDarkTheme();
}
}
}
}Dark Mode Toast Styling
<style>
@media (prefers-color-scheme: dark) {
.e-toast {
background-color: #2C2C2C;
color: #E0E0E0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.e-toast.e-toast-success {
background-color: #1B5E20;
border-left: 4px solid #4CAF50;
}
.e-toast.e-toast-error {
background-color: #B71C1C;
border-left: 4px solid #F44336;
}
.e-toast.e-toast-warning {
background-color: #E65100;
border-left: 4px solid #FF9800;
}
.e-toast.e-toast-info {
background-color: #01579B;
border-left: 4px solid #03A9F4;
}
}
</style>Dark Mode Message Styling
<style>
@media (prefers-color-scheme: dark) {
.e-message {
background-color: #2C2C2C;
color: #E0E0E0;
}
.e-message.e-success {
background-color: #1B5E20;
border-color: #4CAF50;
}
.e-message.e-error {
background-color: #B71C1C;
border-color: #F44336;
}
.e-message.e-warning {
background-color: #E65100;
border-color: #FF9800;
}
.e-message.e-info {
background-color: #01579B;
border-color: #03A9F4;
}
}
</style>Dark Mode Skeleton Styling
<style>
@media (prefers-color-scheme: dark) {
.e-skeleton {
background: linear-gradient(90deg, #2C2C2C 0%, #3C3C3C 50%, #2C2C2C 100%);
background-size: 200% 100%;
}
.e-skeleton-wave {
background: linear-gradient(90deg, #2C2C2C 0%, #4C4C4C 50%, #2C2C2C 100%);
background-size: 200% 100%;
}
}
</style>---
Responsive Design
Mobile-Optimized Toast
<SfToast CssClass="responsive-toast"
Position="new ToastPosition { X = "Center", Y = "Bottom" }"
Title="Mobile Optimized"
Content="Adjusts to screen size" />
<style>
.responsive-toast {
width: 90%;
max-width: 500px;
}
@media (max-width: 768px) {
.responsive-toast {
width: 95%;
font-size: 14px;
}
.responsive-toast .e-toast-title {
font-size: 16px;
}
}
@media (max-width: 480px) {
.responsive-toast {
width: 100%;
border-radius: 0;
margin: 0;
}
}
</style>Responsive Message
<SfMessage CssClass="responsive-message" ShowIcon="true">
Responsive message adapts to screen size
</SfMessage>
<style>
.responsive-message {
padding: 16px;
}
@media (max-width: 768px) {
.responsive-message {
padding: 12px;
font-size: 14px;
}
.responsive-message .e-msg-icon {
font-size: 18px;
}
}
@media (max-width: 480px) {
.responsive-message {
padding: 10px;
font-size: 13px;
}
}
</style>Responsive Skeleton Grid
<div class="skeleton-grid">
@for (int i = 0; i < 6; i++)
{
<div class="skeleton-item">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="200px" />
<SfSkeleton Shape="SkeletonType.Text" Width="80%" />
</div>
}
</div>
<style>
.skeleton-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
@media (max-width: 768px) {
.skeleton-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
}
@media (max-width: 480px) {
.skeleton-grid {
grid-template-columns: 1fr;
gap: 10px;
}
}
</style>---
Custom Themes
Creating Custom Theme Variables
:root {
/* Toast Colors */
--toast-bg: #FFFFFF;
--toast-text: #333333;
--toast-success: #4CAF50;
--toast-error: #F44336;
--toast-warning: #FF9800;
--toast-info: #2196F3;
/* Message Colors */
--message-bg: #F5F5F5;
--message-border: #E0E0E0;
/* Skeleton Colors */
--skeleton-bg: #E0E0E0;
--skeleton-highlight: #F5F5F5;
}
[data-theme="dark"] {
--toast-bg: #2C2C2C;
--toast-text: #E0E0E0;
--toast-success: #66BB6A;
--toast-error: #EF5350;
--toast-warning: #FFA726;
--toast-info: #42A5F5;
--message-bg: #2C2C2C;
--message-border: #3C3C3C;
--skeleton-bg: #2C2C2C;
--skeleton-highlight: #3C3C3C;
}Apply Custom Theme
.e-toast {
background-color: var(--toast-bg);
color: var(--toast-text);
}
.e-toast.e-toast-success {
background-color: var(--toast-success);
}
.e-toast.e-toast-error {
background-color: var(--toast-error);
}
.e-message {
background-color: var(--message-bg);
border-color: var(--message-border);
}
.e-skeleton {
background: linear-gradient(
90deg,
var(--skeleton-bg) 0%,
var(--skeleton-highlight) 50%,
var(--skeleton-bg) 100%
);
}---
Animation Customization
Custom Toast Entrance Animation
@keyframes slideInFromRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.custom-animation-toast.e-toast-show {
animation: slideInFromRight 0.5s ease-out;
}Custom Message Reveal
@keyframes messageReveal {
from {
max-height: 0;
opacity: 0;
padding: 0;
}
to {
max-height: 200px;
opacity: 1;
padding: 16px;
}
}
.e-message {
animation: messageReveal 0.3s ease-out;
}---
Best Practices
1. Consistent theming - Use the same theme across all notification components 2. Contrast ratios - Ensure text meets WCAG AA standards (4.5:1 minimum) 3. Mobile-first - Design for small screens, enhance for larger 4. Performance - Minimize CSS complexity, avoid heavy animations 5. Brand alignment - Customize colors to match brand identity 6. Accessibility - Test with screen readers and keyboard navigation 7. Dark mode - Provide dark mode alternatives for all styles 8. Responsive typography - Scale font sizes appropriately 9. z-index management - Ensure toasts appear above other content 10. Test across themes - Verify custom styles work with all Syncfusion themes
---
Next Steps
- See Use Cases and Examples for complete implementation scenarios
- Review Toast Features for advanced toast customization
- Explore Message Implementation for message styling
- Check Skeleton Implementation for skeleton patterns
Toast - Advanced Features
Table of Contents
- Animation Settings
- Progress Bar
- Action Buttons
- Templates
- Event Callbacks
- Multiple Toast Management
- Target Container
- RTL Support
- Custom Dimensions
---
Animation Settings
Control how toasts appear and disappear with customizable animations.
Show and Hide Animations
<SfToast @ref="ToastObj" Title="Animated Toast" Content="Watch me slide in and fade out!">
<ToastAnimationSettings>
<ToastShowAnimationSettings Effect="ToastEffect.SlideLeftIn" Duration="600" Easing="ToastEasing.Ease" />
<ToastHideAnimationSettings Effect="ToastEffect.FadeOut" Duration="400" Easing="ToastEasing.Linear" />
</ToastAnimationSettings>
</SfToast>
@code {
SfToast ToastObj;
}Available Animation Effects
Show Effects:
SlideLeftIn- Slide from leftSlideRightIn- Slide from rightSlideTopIn- Slide from topSlideBottomIn- Slide from bottomFadeIn- Fade inFadeZoomIn- Zoom and fade inFlipLeftDownIn- Flip animation
Hide Effects:
SlideLeftOut- Slide to leftSlideRightOut- Slide to rightSlideTopOut- Slide to topSlideBottomOut- Slide to bottomFadeOut- Fade outFadeZoomOut- Zoom and fade outFlipLeftDownOut- Flip animation
Easing Options
Linear- Constant speedEase- Slow start and end, faster in middle (default)EaseIn- Slow startEaseOut- Slow endEaseInOut- Slow start and end
Position-Specific Animations
@page "/toast-animations"
<SfButton @onclick="ShowTopToast">Top (Slide Down)</SfButton>
<SfButton @onclick="ShowBottomToast">Bottom (Slide Up)</SfButton>
<SfButton @onclick="ShowLeftToast">Left (Slide Right)</SfButton>
<SfButton @onclick="ShowRightToast">Right (Slide Left)</SfButton>
<SfToast @ref="TopToast" Title="Top Toast" Content="Sliding from top">
<ToastPosition X="Center" Y="Top" />
<ToastAnimationSettings>
<ToastShowAnimationSettings Effect="ToastEffect.SlideTopIn" Duration="400" />
<ToastHideAnimationSettings Effect="ToastEffect.SlideTopOut" Duration="400" />
</ToastAnimationSettings>
</SfToast>
<SfToast @ref="BottomToast" Title="Bottom Toast" Content="Sliding from bottom">
<ToastPosition X="Center" Y="Bottom" />
<ToastAnimationSettings>
<ToastShowAnimationSettings Effect="ToastEffect.SlideBottomIn" Duration="400" />
<ToastHideAnimationSettings Effect="ToastEffect.SlideBottomOut" Duration="400" />
</ToastAnimationSettings>
</SfToast>
<SfToast @ref="LeftToast" Title="Left Toast" Content="Sliding from left">
<ToastPosition X="Left" Y="Center" />
<ToastAnimationSettings>
<ToastShowAnimationSettings Effect="ToastEffect.SlideLeftIn" Duration="400" />
<ToastHideAnimationSettings Effect="ToastEffect.SlideLeftOut" Duration="400" />
</ToastAnimationSettings>
</SfToast>
<SfToast @ref="RightToast" Title="Right Toast" Content="Sliding from right">
<ToastPosition X="Right" Y="Center" />
<ToastAnimationSettings>
<ToastShowAnimationSettings Effect="ToastEffect.SlideRightIn" Duration="400" />
<ToastHideAnimationSettings Effect="ToastEffect.SlideRightOut" Duration="400" />
</ToastAnimationSettings>
</SfToast>
@code {
SfToast TopToast, BottomToast, LeftToast, RightToast;
private async Task ShowTopToast() => await TopToast.ShowAsync();
private async Task ShowBottomToast() => await BottomToast.ShowAsync();
private async Task ShowLeftToast() => await LeftToast.ShowAsync();
private async Task ShowRightToast() => await RightToast.ShowAsync();
}---
Progress Bar
Display a visual indicator of remaining time before auto-dismiss.
Basic Progress Bar
<SfToast @ref="ToastObj"
Title="Progress Demo"
Content="Watch the progress bar count down"
ShowProgressBar="true"
Timeout="5000" />Progress Bar Direction
<SfToast @ref="ToastObj"
Title="Progress Demo"
Content="Progress bar moving right to left"
ShowProgressBar="true"
ProgressDirection="ProgressDirection.RTL"
Timeout="5000" />Direction options:
ProgressDirection.LTR- Left to right (default)ProgressDirection.RTL- Right to left
Custom Progress Bar Styling
<SfToast @ref="ToastObj"
Title="Styled Progress"
Content="Custom progress bar colors"
ShowProgressBar="true"
CssClass="custom-progress-toast"
Timeout="5000" />
<style>
.custom-progress-toast .e-toast-progress {
background-color: #4CAF50;
height: 4px;
}
</style>---
Action Buttons
Add interactive buttons to toasts for user actions.
Single Action Button
<SfToast @ref="ToastObj" Title="Action Required" Content="Do you want to save changes?">
<ToastButtons>
<ToastButton Content="Save" OnClick="@SaveChanges" />
</ToastButtons>
</SfToast>
@code {
SfToast ToastObj;
private async Task SaveChanges()
{
// Hide toast after action
await ToastObj.HideAsync();
}
}Multiple Action Buttons
<SfToast @ref="ToastObj"
Title="Confirmation"
Content="Unsaved changes detected. What would you like to do?"
Timeout="0">
<ToastButtons>
<ToastButton Content="Save" CssClass="e-success" OnClick="@SaveAndContinue" />
<ToastButton Content="Discard" CssClass="e-danger" OnClick="@DiscardChanges" />
<ToastButton Content="Cancel" OnClick="@CancelAction" />
</ToastButtons>
</SfToast>
@code {
SfToast ToastObj;
private async Task SaveAndContinue()
{
await ToastObj.HideAsync();
}
private async Task DiscardChanges()
{
await ToastObj.HideAsync();
}
private async Task CancelAction()
{
await ToastObj.HideAsync();
}
}Styled Action Buttons
<SfToast @ref="ToastObj" Title="Update Available" Content="A new version is available. Install now?">
<ToastButtons>
<ToastButton Content="Install" CssClass="e-primary e-small" OnClick="@InstallUpdate" />
<ToastButton Content="Later" CssClass="e-flat e-small" OnClick="@RemindLater" />
</ToastButtons>
</SfToast>
<style>
.e-toast .e-toast-actions button {
margin: 0 5px;
padding: 6px 12px;
font-size: 13px;
}
</style>---
Templates
Customize toast appearance with custom templates.
<SfToast @ref="ToastObj" Content="Template demo with custom title">
<ToastTemplates>
<Template>
<div class="custom-title">
<span class="e-icons e-star"></span>
<strong>Special Notification</strong>
</div>
</Template>
</ToastTemplates>
</SfToast>
<style>
.custom-title {
display: flex;
align-items: center;
gap: 8px;
color: #ff9800;
}
</style><SfToast @ref="ToastObj" Title="New Message">
<ToastTemplates>
<Template>
<div class="message-content">
<img src="images/avatar.png" alt="User" class="avatar" />
<div class="message-text">
<strong>John Doe</strong>
<p>Hey! Are you available for a meeting?</p>
<small>2 minutes ago</small>
</div>
</div>
</Template>
</ToastTemplates>
</SfToast>
<style>
.message-content {
display: flex;
gap: 12px;
align-items: start;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
.message-text strong {
display: block;
margin-bottom: 4px;
}
.message-text p {
margin: 0 0 4px 0;
font-size: 14px;
}
.message-text small {
color: #666;
font-size: 12px;
}
</style>Complete Custom Template
<SfToast @ref="ToastObj">
<ToastTemplates>
<Template>
<div class="custom-toast-wrapper">
<div class="toast-header">
<span class="e-icons e-check-circle"></span>
<h4>Upload Complete</h4>
<button @onclick="@(() => ToastObj.HideAsync())" class="close-btn">×</button>
</div>
<div class="toast-body">
<p><strong>document.pdf</strong> (2.3 MB)</p>
<div class="progress-complete">100%</div>
<small>Uploaded to My Documents</small>
</div>
<div class="toast-actions">
<button @onclick="ViewFile" class="action-btn">View</button>
<button @onclick="ShareFile" class="action-btn">Share</button>
</div>
</div>
</Template>
</ToastTemplates>
</SfToast>
@code {
private async Task ViewFile()
{
// Open file
await ToastObj.HideAsync();
}
private async Task ShareFile()
{
// Share dialog
await ToastObj.HideAsync();
}
}
<style>
.custom-toast-wrapper {
padding: 12px;
}
.toast-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.toast-header h4 {
flex: 1;
margin: 0;
font-size: 16px;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
.toast-body {
margin-bottom: 10px;
}
.progress-complete {
background: #4CAF50;
color: white;
padding: 4px 8px;
border-radius: 4px;
display: inline-block;
font-size: 12px;
margin: 5px 0;
}
.toast-actions {
display: flex;
gap: 8px;
}
.action-btn {
padding: 6px 16px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
}
</style>---
Event Callbacks
Handle toast lifecycle events.
Available Events
@using Syncfusion.Blazor.Notifications
<SfToast @ref="ToastObj"
Title="Event Demo"
Content="Toast with event handlers">
<ToastEvents
Created="OnCreated"
Destroyed="OnDestroyed"
OnOpen="OnOpen"
Opened="Opened"
Closed="Closed"
OnClick="OnClick">
</ToastEvents>
</SfToast>
<button class="e-btn" @onclick="ShowToast">Show Toast</button>
@code {
private SfToast ToastObj;
private async Task ShowToast()
{
await ToastObj.ShowAsync();
}
// Fires after Toast component is created
public void OnCreated(object args)
{
Console.WriteLine("Toast component created");
}
// Fires after Toast component is destroyed
public void OnDestroyed(object args)
{
Console.WriteLine("Toast component destroyed");
}
// Fires before toast opens (can cancel)
public void OnOpen(ToastBeforeOpenArgs args)
{
Console.WriteLine("Before toast opens");
// args.Cancel = true;
}
// Fires after toast is shown
public void Opened(ToastOpenArgs args)
{
Console.WriteLine("Toast opened");
}
// Fires when toast is clicked
public void OnClick(ToastClickEventArgs args)
{
Console.WriteLine("Toast clicked");
}
// Fires after toast closes
public void Closed(ToastCloseArgs args)
{
Console.WriteLine("Toast closed");
}
}
Practical Event Usage
Track notification history:
@code {
private List<string> notificationHistory = new();
private void OnOpen(ToastOpenArgs args)
{
var message = $"{DateTime.Now:HH:mm:ss} - {ToastObj.Content}";
notificationHistory.Add(message);
}
}Analytics tracking:
@code {
private void OnClick(ToastClickEventArgs args)
{
// Track user interaction
await AnalyticsService.TrackEvent("Toast_Clicked", new
{
Title = ToastObj.Title,
Timestamp = DateTime.Now
});
}
}---
Multiple Toast Management
Control how multiple toasts are displayed and organized.
Stack Order
<SfToast @ref="ToastObj"
NewestOnTop="true">
<ToastPosition X="Right" Y="Bottom"></ToastPosition>
</SfToast>
@code {
// NewestOnTop = true: Latest toast on top (default)
// NewestOnTop = false: Latest toast at bottom
}Sequential Toast Display
@page "/sequential-toasts"
<SfButton @onclick="ShowSequentialMessages">Show Progress Updates</SfButton>
<SfToast @ref="ToastObj">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
SfToast ToastObj;
private async Task ShowSequentialMessages()
{
ToastObj.Content = "Step 1: Validating data...";
ToastObj.Timeout = 2000;
await ToastObj.ShowAsync();
await Task.Delay(2000);
ToastObj.Content = "Step 2: Processing records...";
await ToastObj.ShowAsync();
await Task.Delay(2000);
ToastObj.Content = "Step 3: Generating report...";
await ToastObj.ShowAsync();
await Task.Delay(2000);
ToastObj.Content = "Complete! Report generated successfully.";
ToastObj.CssClass = "e-toast-success";
ToastObj.Timeout = 5000;
await ToastObj.ShowAsync();
}
}Hide All Toasts
@code {
// Hide all visible toasts
private async Task HideAllToasts()
{
await ToastObj.HideAsync("All");
}
}---
Target Container
Specify where toasts should be rendered within the page.
Default (Body)
<!-- Renders in document body (default) -->
<SfToast @ref="ToastObj" Title="Default" Content="Appears in body" />Specific Container
<div id="toast-container" style="position: relative; height: 400px; border: 1px solid #ddd;">
<p>Toasts will appear within this container</p>
<SfButton @onclick="@(() => ToastObj.ShowAsync())">Show Toast</SfButton>
<SfToast @ref="ToastObj"
Title="Contained"
Content="Appears in specific container"
Target="#toast-container">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
</div>
@code {
SfToast ToastObj;
}Use cases for Target:
- Modal dialogs with their own notifications
- Specific page sections (dashboard panels)
- Iframe content
- Split-screen layouts
---
RTL Support
Enable right-to-left support for Arabic, Hebrew, and other RTL languages.
<SfToast @ref="ToastObj"
Title="إشعار"
Content="تم حفظ التغييرات بنجاح"
EnableRtl="true">
<ToastPosition X="Left" Y="Top"></ToastPosition>
</SfToast>
@code {
SfToast ToastObj;
}---
Custom Dimensions
Control toast width and height.
<SfToast @ref="ToastObj"
Title="Custom Size"
Content="This toast has custom dimensions"
Width="400px"
Height="150px" />Width options:
- Pixels:
"400px" - Percentage:
"50%" - Auto:
"auto"(fits content)
Height options:
- Pixels:
"150px" - Auto:
"auto"(default, fits content)
Responsive Width
<SfToast @ref="ToastObj"
Title="Responsive"
Content="Adapts to screen size"
Width="90%"
CssClass="responsive-toast" />
<style>
@media (min-width: 768px) {
.responsive-toast {
width: 400px !important;
}
}
</style>---
Advanced Patterns
Notification Queue
@code {
private Queue<NotificationMessage> notificationQueue = new();
private bool isShowingToast = false;
private async Task EnqueueNotification(string title, string content)
{
notificationQueue.Enqueue(new NotificationMessage { Title = title, Content = content });
if (!isShowingToast)
{
await ProcessNotificationQueue();
}
}
private async Task ProcessNotificationQueue()
{
isShowingToast = true;
while (notificationQueue.Count > 0)
{
var notification = notificationQueue.Dequeue();
ToastObj.Title = notification.Title;
ToastObj.Content = notification.Content;
await ToastObj.ShowAsync();
await Task.Delay(ToastObj.Timeout + 500); // Wait for toast to auto-hide + buffer
}
isShowingToast = false;
}
private class NotificationMessage
{
public string Title { get; set; }
public string Content { get; set; }
}
}Extended Timeout on Hover
<SfToast @ref="ToastObj"
Title="Hover Me"
Content="Hover to extend display time"
Timeout="3000"
ExtendedTimeout="10000" />
@code {
// ExtendedTimeout: Duration to show when mouse is over toast
// Timeout: Normal duration
}---
Next Steps
- Learn about Message Component for inline alerts
- Explore Skeleton Component for loading states
- Review Styling Guide for advanced customization
- See Use Cases for complete scenarios
Toast - Getting Started
This guide covers the basics of implementing the Syncfusion Blazor Toast component for displaying temporary notification messages.
Overview
The SfToast component is a lightweight, non-blocking notification popup that appears temporarily to provide user feedback. It's ideal for success messages, errors, warnings, and informational notifications that don't require persistent display.
When to use Toast:
- Show temporary feedback after user actions (save, delete, update)
- Display non-critical notifications that auto-dismiss
- Provide status updates without interrupting workflow
- Show multiple notifications in a stack
- Need positioned notifications (corners, center)
Installation
Step 1: Install NuGet Package
dotnet add package Syncfusion.Blazor.Notifications
dotnet add package Syncfusion.Blazor.ThemesStep 2: Register Syncfusion Service
Add the service registration in Program.cs:
using Syncfusion.Blazor;
var builder = WebApplication.CreateBuilder(args);
// Add Syncfusion Blazor service
builder.Services.AddSyncfusionBlazor();
var app = builder.Build();Step 3: Add Theme and Script References
In App.razor (or your main layout file):
<head>
<!-- Syncfusion Blazor theme -->
<link href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" rel="stylesheet" />
</head>
<body>
<!-- App content -->
<!-- Syncfusion Blazor scripts -->
<script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js"></script>
</body>Available themes:
bootstrap5.css- Bootstrap 5material.css- Material Designfluent.css- Fluent UItailwind.css- Tailwind CSSfabric.css- Office Fabric
Step 4: Import Namespace
Add to _Imports.razor:
@using Syncfusion.Blazor.Notifications
@using Syncfusion.Blazor.ButtonsBasic Toast Implementation
Simple Toast with Button
@page "/toast-basic"
<div class="col-lg-12 control-section toast-default-section">
<SfButton @onclick="ShowToast">Show Toast</SfButton>
</div>
<SfToast @ref="ToastObj" Title="Notification" Content="Your message has been sent successfully!" />
@code {
SfToast ToastObj;
private async Task ShowToast()
{
await ToastObj.ShowAsync();
}
}How it works: 1. @ref="ToastObj" - Creates a reference to the toast instance 2. ShowAsync() - Displays the toast notification 3. Toast auto-hides after default timeout (5 seconds)
Toast with Custom Content
@page "/toast-content"
<SfButton @onclick="@(() => ShowToastWithContent("Data saved successfully!"))">Save</SfButton>
<SfButton @onclick="@(() => ShowToastWithContent("Changes discarded"))">Cancel</SfButton>
<SfToast @ref="ToastObj" Title="Status Update" />
@code {
SfToast ToastObj;
private async Task ShowToastWithContent(string message)
{
ToastObj.Content = message;
await ToastObj.ShowAsync();
}
}Toast with Icon
@page "/toast-icon"
<SfButton @onclick="ShowSuccessToast">Success</SfButton>
<SfButton @onclick="ShowErrorToast">Error</SfButton>
<SfToast @ref="ToastObj" />
@code {
SfToast ToastObj;
private async Task ShowSuccessToast()
{
ToastObj.Title = "Success";
ToastObj.Content = "Your profile has been updated";
ToastObj.Icon = "e-success toast-icons";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
private async Task ShowErrorToast()
{
ToastObj.Title = "Error";
ToastObj.Content = "Failed to connect to server";
ToastObj.Icon = "e-error toast-icons";
ToastObj.CssClass = "e-toast-danger";
await ToastObj.ShowAsync();
}
}
<style>
.toast-icons {
font-size: 20px;
}
</style>Toast Position
The toast can be positioned in 9 predefined locations using the Position property.
Position Examples
@page "/toast-position"
<div class="button-grid">
<SfButton @onclick="@(() => ShowToastAt("Left", "Top"))">Top Left</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Center", "Top"))">Top Center</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Right", "Top"))">Top Right</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Left", "Center"))">Center Left</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Center", "Center"))">Center</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Right", "Center"))">Center Right</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Left", "Bottom"))">Bottom Left</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Center", "Bottom"))">Bottom Center</SfButton>
<SfButton @onclick="@(() => ShowToastAt("Right", "Bottom"))">Bottom Right</SfButton>
</div>
<SfToast @ref="ToastObj"
Title="Position Demo"
Content="Toast notification">
<ToastPosition X="@PositionX" Y="@PositionY"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private string PositionX = "Center";
private string PositionY = "Top";
private async Task ShowToastAt(string x, string y)
{
PositionX = x;
PositionY = y;
await ToastObj.HideAsync();
await ToastObj.ShowAsync();
}
}
<style>
.button-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
max-width: 600px;
}
</style>Position values:
- X-axis:
"Left","Center","Right", or custom pixel/percentage values - Y-axis:
"Top","Center","Bottom", or custom pixel/percentage values
Custom Position with Pixels
<SfToast @ref="ToastObj"
Title="Custom Position"
Content="Positioned at 100px from left, 50px from top">
<ToastPosition X="100" Y="50"></ToastPosition>
</SfToast>Showing and Hiding Toasts
Show Method
@code {
// Show toast immediately
await ToastObj.ShowAsync();
}Hide Method
@code {
// Hide toast manually
await ToastObj.HideAsync();
// Hide all toasts
await ToastObj.HideAsync("All");
}Hide Specific Toast
@code {
// Each toast has a unique element
await ToastObj.HideAsync(toastElement);
}Auto-Dismiss Timeout
Control how long the toast remains visible before auto-hiding.
<SfToast @ref="ToastObj"
Title="Timeout Demo"
Content="This message will disappear in 3 seconds"
Timeout="3000" />
@code {
// Timeout in milliseconds (default: 5000)
// Set to 0 to disable auto-hide
}Common timeout values:
- Quick messages: 2000-3000ms (2-3 seconds)
- Standard messages: 5000ms (5 seconds, default)
- Important messages: 7000-10000ms (7-10 seconds)
- Manual dismiss only: 0 (no auto-hide)
Close Button
Enable users to manually dismiss the toast.
<SfToast @ref="ToastObj"
Title="Dismissible Toast"
Content="Click the X to close"
ShowCloseButton="true" />Multiple Toasts
Display multiple toasts simultaneously.
@page "/multiple-toasts"
<SfButton @onclick="ShowMultipleToasts">Show 3 Toasts</SfButton>
<SfToast @ref="ToastObj"
NewestOnTop="true">
<ToastPosition X="Right" Y="Bottom"></ToastPosition>
</SfToast>
@code {
SfToast ToastObj;
private async Task ShowMultipleToasts()
{
ToastObj.Content = "First notification";
await ToastObj.ShowAsync();
await Task.Delay(500);
ToastObj.Content = "Second notification";
await ToastObj.ShowAsync();
await Task.Delay(500);
ToastObj.Content = "Third notification";
await ToastObj.ShowAsync();
}
}NewestOnTop property:
true- Latest toast appears on top of stack (default)false- Latest toast appears at bottom of stack
Complete Working Example
@page "/toast-complete"
@using Syncfusion.Blazor.Notifications
@using Syncfusion.Blazor.Buttons
<div class="toast-demo">
<h3>Toast Notification Demo</h3>
<div class="button-group">
<SfButton CssClass="e-success" @onclick="ShowSuccess">Success</SfButton>
<SfButton CssClass="e-info" @onclick="ShowInfo">Info</SfButton>
<SfButton CssClass="e-warning" @onclick="ShowWarning">Warning</SfButton>
<SfButton CssClass="e-danger" @onclick="ShowError">Error</SfButton>
</div>
</div>
<SfToast @ref="ToastObj"
ShowCloseButton="true"
Timeout="5000">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
SfToast ToastObj;
private async Task ShowSuccess()
{
ToastObj.Title = "Success";
ToastObj.Content = "Your changes have been saved successfully!";
ToastObj.Icon = "e-success toast-icon";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
private async Task ShowInfo()
{
ToastObj.Title = "Information";
ToastObj.Content = "You have 3 new messages in your inbox.";
ToastObj.Icon = "e-info toast-icon";
ToastObj.CssClass = "e-toast-info";
await ToastObj.ShowAsync();
}
private async Task ShowWarning()
{
ToastObj.Title = "Warning";
ToastObj.Content = "Your session will expire in 5 minutes.";
ToastObj.Icon = "e-warning toast-icon";
ToastObj.CssClass = "e-toast-warning";
await ToastObj.ShowAsync();
}
private async Task ShowError()
{
ToastObj.Title = "Error";
ToastObj.Content = "Failed to connect to the server. Please try again.";
ToastObj.Icon = "e-error toast-icon";
ToastObj.CssClass = "e-toast-danger";
await ToastObj.ShowAsync();
}
}
<style>
.toast-demo {
padding: 20px;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 15px;
}
.toast-icon {
font-size: 18px;
}
</style>Common Patterns
Form Submission Feedback
<EditForm Model="@model" OnValidSubmit="HandleValidSubmit">
<!-- Form fields -->
<SfButton Type="ButtonType.Submit">Submit</SfButton>
</EditForm>
<SfToast @ref="ToastObj">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
SfToast ToastObj;
private FormModel model = new();
private async Task HandleValidSubmit()
{
try
{
await SaveData();
ToastObj.Title = "Success";
ToastObj.Content = "Form submitted successfully!";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
catch (Exception ex)
{
ToastObj.Title = "Error";
ToastObj.Content = $"Submission failed: {ex.Message}";
ToastObj.CssClass = "e-toast-danger";
await ToastObj.ShowAsync();
}
}
}API Call Feedback
@code {
private async Task LoadData()
{
try
{
var data = await Http.GetFromJsonAsync<Data[]>("api/data");
ToastObj.Title = "Data Loaded";
ToastObj.Content = $"Successfully loaded {data.Length} items";
ToastObj.CssClass = "e-toast-info";
ToastObj.Timeout = 3000;
await ToastObj.ShowAsync();
}
catch
{
ToastObj.Title = "Load Failed";
ToastObj.Content = "Unable to load data. Please refresh the page.";
ToastObj.CssClass = "e-toast-danger";
ToastObj.ShowCloseButton = true;
ToastObj.Timeout = 0; // Manual dismiss only
await ToastObj.ShowAsync();
}
}
}Troubleshooting
Toast Not Appearing
Problem: Called ShowAsync() but toast doesn't appear.
Solutions:
- Ensure
@ref="ToastObj"is set on the component - Check that the component is initialized (not null)
- Verify Title or Content has a value
- Check if toast is positioned outside viewport
- Ensure theme CSS is loaded
Toast Appears Behind Other Elements
Problem: Toast is hidden behind modal dialogs or other components.
Solution: Increase z-index with custom CSS:
.custom-toast {
z-index: 10000 !important;
}<SfToast @ref="ToastObj" CssClass="custom-toast" />Multiple Toasts Not Stacking
Problem: New toasts replace old ones instead of stacking.
Solution: Ensure each toast call has different content or use slight delays:
await Task.Delay(100); // Small delay between toast callsNext Steps
- Learn about Advanced Toast Features including animations, templates, and action buttons
- Explore Styling and Themes for customization options
- See Use Cases and Examples for real-world scenarios
Use Cases and Real-World Examples
Table of Contents
- Form Submission Feedback
- E-commerce Cart Notifications
- File Upload Progress
- Dashboard Data Loading
- User Authentication
- API Error Handling
- Real-time Updates
- Multi-Step Forms
- Notification Center
- Complete Application Examples
---
Form Submission Feedback
Combine Message for inline validation errors and Toast for submission success/failure.
@page "/form-example"
@using System.ComponentModel.DataAnnotations
<div class="form-container">
<h2>User Registration</h2>
@if (!string.IsNullOrEmpty(errorMessage))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => errorMessage = null)">
<strong>Error:</strong> @errorMessage
</SfMessage>
}
<EditForm Model="@model" OnValidSubmit="HandleSubmit" OnInvalidSubmit="HandleInvalidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<label>Full Name:</label>
<InputText @bind-Value="model.FullName" class="form-control" />
<ValidationMessage For="@(() => model.FullName)" />
</div>
<div class="form-group">
<label>Email:</label>
<InputText @bind-Value="model.Email" class="form-control" />
<ValidationMessage For="@(() => model.Email)" />
</div>
<div class="form-group">
<label>Password:</label>
<InputText @bind-Value="model.Password" type="password" class="form-control" />
<ValidationMessage For="@(() => model.Password)" />
</div>
<div class="form-group">
<label>Confirm Password:</label>
<InputText @bind-Value="model.ConfirmPassword" type="password" class="form-control" />
<ValidationMessage For="@(() => model.ConfirmPassword)" />
</div>
<SfButton Type="ButtonType.Submit" IsPrimary="true" Disabled="@isSubmitting">
@(isSubmitting ? "Submitting..." : "Register")
</SfButton>
</EditForm>
</div>
<SfToast @ref="ToastObj"
ShowCloseButton="true" >
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
private RegistrationModel model = new();
private SfToast ToastObj;
private string errorMessage;
private bool isSubmitting = false;
private async Task HandleSubmit()
{
if (model.Password != model.ConfirmPassword)
{
errorMessage = "Passwords do not match";
return;
}
isSubmitting = true;
errorMessage = null;
try
{
await Task.Delay(1500); // Simulate API call
// await UserService.RegisterAsync(model);
ToastObj.Title = "Success!";
ToastObj.Content = "Your account has been created successfully.";
ToastObj.Icon = "e-success";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
// Reset form
model = new();
}
catch (Exception ex)
{
errorMessage = $"Registration failed: {ex.Message}";
}
finally
{
isSubmitting = false;
}
}
private void HandleInvalidSubmit()
{
errorMessage = "Please correct the errors below.";
}
public class RegistrationModel
{
[Required(ErrorMessage = "Full name is required")]
public string FullName { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Invalid email format")]
public string Email { get; set; }
[Required(ErrorMessage = "Password is required")]
[MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
public string Password { get; set; }
[Required(ErrorMessage = "Please confirm your password")]
public string ConfirmPassword { get; set; }
}
}
<style>
.form-container {
max-width: 500px;
margin: 40px auto;
padding: 30px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: white;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
}
.validation-message {
color: #f44336;
font-size: 13px;
margin-top: 4px;
}
</style>---
E-commerce Cart Notifications
Show toast notifications for cart operations with action buttons.
@page "/cart-demo"
<div class="product-grid">
@foreach (var product in products)
{
<div class="product-card">
<img src="@product.ImageUrl" alt="@product.Name" />
<h3>@product.Name</h3>
<p class="price">$@product.Price</p>
<SfButton @onclick="@(() => AddToCart(product))">Add to Cart</SfButton>
</div>
}
</div>
<SfToast @ref="ToastObj"
Timeout="4000">
<ToastButtons>
<ToastButton Content="View Cart" OnClick="@ViewCart" CssClass="e-primary" />
<ToastButton Content="Undo" OnClick="@UndoAddToCart" />
</ToastButtons>
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private List<Product> products = new();
private Product lastAddedProduct;
protected override void OnInitialized()
{
products = new List<Product>
{
new Product { Id = 1, Name = "Wireless Mouse", Price = 29.99m, ImageUrl = "/images/mouse.jpg" },
new Product { Id = 2, Name = "Keyboard", Price = 79.99m, ImageUrl = "/images/keyboard.jpg" },
new Product { Id = 3, Name = "Monitor", Price = 299.99m, ImageUrl = "/images/monitor.jpg" }
};
}
private async Task AddToCart(Product product)
{
lastAddedProduct = product;
// Add to cart logic
await CartService.AddAsync(product);
ToastObj.Title = "Added to Cart";
ToastObj.Content = $"{product.Name} has been added to your cart.";
ToastObj.Icon = "e-success";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
private async Task ViewCart()
{
await ToastObj.HideAsync();
NavigationManager.NavigateTo("/cart");
}
private async Task UndoAddToCart()
{
if (lastAddedProduct != null)
{
await CartService.RemoveAsync(lastAddedProduct.Id);
ToastObj.Title = "Removed";
ToastObj.Content = $"{lastAddedProduct.Name} has been removed from cart.";
ToastObj.Icon = "e-info";
ToastObj.CssClass = "e-toast-info";
await ToastObj.ShowAsync();
}
}
private class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
}---
File Upload Progress
Combine Skeleton for loading state, Message for status, and Toast for completion.
@page "/file-upload"
<div class="upload-container">
<h2>File Upload</h2>
@if (uploadStatus == UploadStatus.Error)
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => uploadStatus = UploadStatus.None)">
@errorMessage
</SfMessage>
}
<InputFile OnChange="HandleFileUpload" multiple accept="image/*" />
@if (uploadStatus == UploadStatus.Uploading)
{
<div class="upload-progress">
<SfMessage Severity="MessageSeverity.Info" ShowIcon="true">
Uploading @fileName... @uploadProgress%
</SfMessage>
<div class="file-preview-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="200px" />
<SfSkeleton Shape="SkeletonType.Text" Width="70%" />
</div>
</div>
}
@if (uploadStatus == UploadStatus.Completed && uploadedFiles.Any())
{
<div class="uploaded-files">
<h3>Uploaded Files</h3>
@foreach (var file in uploadedFiles)
{
<div class="file-item">
<img src="@file.Url" alt="@file.Name" />
<p>@file.Name</p>
</div>
}
</div>
}
</div>
<SfToast @ref="ToastObj">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private UploadStatus uploadStatus = UploadStatus.None;
private string fileName;
private string errorMessage;
private int uploadProgress;
private List<UploadedFile> uploadedFiles = new();
private async Task HandleFileUpload(InputFileChangeEventArgs e)
{
uploadStatus = UploadStatus.Uploading;
errorMessage = null;
foreach (var file in e.GetMultipleFiles(10))
{
fileName = file.Name;
uploadProgress = 0;
try
{
// Simulate upload with progress
for (int i = 0; i <= 100; i += 10)
{
uploadProgress = i;
StateHasChanged();
await Task.Delay(200);
}
// Upload file
using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
var url = await FileService.UploadAsync(stream, file.Name);
uploadedFiles.Add(new UploadedFile
{
Name = file.Name,
Url = url
});
uploadStatus = UploadStatus.Completed;
ToastObj.Title = "Upload Complete";
ToastObj.Content = $"{file.Name} uploaded successfully!";
ToastObj.Icon = "e-success";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
catch (Exception ex)
{
uploadStatus = UploadStatus.Error;
errorMessage = $"Failed to upload {file.Name}: {ex.Message}";
}
}
}
private enum UploadStatus
{
None,
Uploading,
Completed,
Error
}
private class UploadedFile
{
public string Name { get; set; }
public string Url { get; set; }
}
}
<style>
.upload-container {
max-width: 800px;
margin: 40px auto;
padding: 30px;
}
.file-preview-skeleton {
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.uploaded-files {
margin-top: 30px;
}
.file-item {
display: inline-block;
margin: 10px;
text-align: center;
}
.file-item img {
width: 150px;
height: 150px;
object-fit: cover;
border-radius: 8px;
}
</style>---
Dashboard Data Loading
Use Skeleton while loading dashboard data, then show content with Toast for updates.
@page "/dashboard"
<div class="dashboard">
<h1>Analytics Dashboard</h1>
@if (isLoading)
{
<!-- Dashboard Skeleton -->
<div class="dashboard-grid">
@for (int i = 0; i < 4; i++)
{
<div class="stat-card-skeleton">
<SfSkeleton Shape="SkeletonType.Text" Width="60%" Height="20px" />
<SfSkeleton Shape="SkeletonType.Text" Width="40%" Height="32px" />
</div>
}
<div class="chart-skeleton">
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="300px" />
</div>
</div>
}
else
{
<!-- Actual Dashboard Content -->
<div class="dashboard-grid">
@foreach (var stat in stats)
{
<div class="stat-card">
<h3>@stat.Label</h3>
<p class="value">@stat.Value</p>
<span class="change @(stat.ChangePositive ? "positive" : "negative")">
@stat.Change%
</span>
</div>
}
<div class="chart-container">
<!-- Chart component here -->
<h3>Revenue Trend</h3>
</div>
</div>
}
@if (showUpdateNotification)
{
<SfMessage Severity="MessageSeverity.Info"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => showUpdateNotification = false)">
Dashboard data updated @lastUpdateTime.ToString("HH:mm:ss")
</SfMessage>
}
</div>
<SfToast @ref="ToastObj">
<ToastPosition X="Right" Y="Bottom"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private bool isLoading = true;
private bool showUpdateNotification = false;
private DateTime lastUpdateTime;
private List<DashboardStat> stats = new();
protected override async Task OnInitializedAsync()
{
await LoadDashboardData();
// Auto-refresh every 30 seconds
_ = AutoRefreshData();
}
private async Task LoadDashboardData()
{
isLoading = true;
await Task.Delay(2000); // Simulate API call
stats = new List<DashboardStat>
{
new DashboardStat { Label = "Total Revenue", Value = "$45,231", Change = 12.5, ChangePositive = true },
new DashboardStat { Label = "New Users", Value = "1,893", Change = 8.3, ChangePositive = true },
new DashboardStat { Label = "Orders", Value = "432", Change = -3.2, ChangePositive = false },
new DashboardStat { Label = "Conversion Rate", Value = "3.24%", Change = 1.8, ChangePositive = true }
};
lastUpdateTime = DateTime.Now;
isLoading = false;
}
private async Task AutoRefreshData()
{
while (true)
{
await Task.Delay(30000); // 30 seconds
await LoadDashboardData();
showUpdateNotification = true;
StateHasChanged();
ToastObj.Title = "Data Refreshed";
ToastObj.Content = "Dashboard data has been updated.";
ToastObj.Icon = "e-info";
ToastObj.CssClass = "e-toast-info";
ToastObj.Timeout = 3000;
await ToastObj.ShowAsync();
}
}
private class DashboardStat
{
public string Label { get; set; }
public string Value { get; set; }
public double Change { get; set; }
public bool ChangePositive { get; set; }
}
}
<style>
.dashboard {
padding: 30px;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.stat-card, .stat-card-skeleton {
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: white;
}
.stat-card-skeleton {
display: flex;
flex-direction: column;
gap: 12px;
}
.chart-skeleton, .chart-container {
grid-column: 1 / -1;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: white;
}
.value {
font-size: 32px;
font-weight: bold;
margin: 10px 0;
}
.change {
font-size: 14px;
font-weight: 600;
}
.change.positive {
color: #4CAF50;
}
.change.negative {
color: #F44336;
}
</style>---
User Authentication
Show loading skeleton during login, then success/error notifications.
@page "/login"
<div class="login-container">
<h2>Login</h2>
@if (!string.IsNullOrEmpty(errorMessage))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => errorMessage = null)">
@errorMessage
</SfMessage>
}
@if (isLoading)
{
<div class="loading-skeleton">
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="40px" />
<SfSkeleton Shape="SkeletonType.Text" Width="100%" Height="40px" />
<SfSkeleton Shape="SkeletonType.Rectangle" Width="100%" Height="40px" />
</div>
}
else
{
<EditForm Model="@model" OnValidSubmit="HandleLogin">
<DataAnnotationsValidator />
<div class="form-group">
<label>Email:</label>
<InputText @bind-Value="model.Email" class="form-control" />
<ValidationMessage For="@(() => model.Email)" />
</div>
<div class="form-group">
<label>Password:</label>
<InputText @bind-Value="model.Password" type="password" class="form-control" />
<ValidationMessage For="@(() => model.Password)" />
</div>
<SfButton Type="ButtonType.Submit" IsPrimary="true" Disabled="@isAuthenticating">
@(isAuthenticating ? "Signing in..." : "Sign In")
</SfButton>
</EditForm>
}
</div>
<SfToast @ref="ToastObj">
<ToastPosition X="Center" Y="Top"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private LoginModel model = new();
private bool isLoading = false;
private bool isAuthenticating = false;
private string errorMessage;
private async Task HandleLogin()
{
isAuthenticating = true;
errorMessage = null;
try
{
await Task.Delay(1500); // Simulate auth API call
// await AuthService.LoginAsync(model.Email, model.Password);
ToastObj.Title = "Welcome Back!";
ToastObj.Content = "You have been successfully logged in.";
ToastObj.Icon = "e-success";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
await Task.Delay(1000);
NavigationManager.NavigateTo("/dashboard");
}
catch (Exception ex)
{
errorMessage = "Invalid email or password. Please try again.";
}
finally
{
isAuthenticating = false;
}
}
public class LoginModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
}---
API Error Handling
Comprehensive error handling with retry options.
@page "/api-demo"
<div class="api-demo">
<h2>Data Management</h2>
@if (!string.IsNullOrEmpty(apiError))
{
<SfMessage Severity="MessageSeverity.Error"
ShowIcon="true"
ShowCloseIcon="true"
Closed="@(() => apiError = null)">
<strong>API Error:</strong> @apiError
<SfButton @onclick="RetryLastOperation" CssClass="retry-btn">Retry</SfButton>
</SfMessage>
}
<SfButton @onclick="LoadData">Load Data</SfButton>
<SfButton @onclick="SaveData">Save Data</SfButton>
</div>
<SfToast @ref="ToastObj">
<ToastButtons>
<ToastButton Content="Retry" OnClick="@RetryFromToast" />
<ToastButton Content="Dismiss" OnClick="@DismissError" />
</ToastButtons>
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private string apiError;
private Action lastFailedOperation;
private async Task LoadData()
{
try
{
await ApiService.LoadDataAsync();
ToastObj.Title = "Success";
ToastObj.Content = "Data loaded successfully.";
ToastObj.CssClass = "e-toast-success";
await ToastObj.ShowAsync();
}
catch (HttpRequestException ex)
{
lastFailedOperation = LoadData;
apiError = $"Network error: Unable to reach the server.";
ToastObj.Title = "Connection Error";
ToastObj.Content = "Unable to connect to the server. Please check your internet connection.";
ToastObj.CssClass = "e-toast-danger";
ToastObj.Timeout = 0; // Don't auto-dismiss
await ToastObj.ShowAsync();
}
catch (Exception ex)
{
lastFailedOperation = LoadData;
apiError = ex.Message;
}
}
private async Task SaveData()
{
// Similar error handling
}
private async Task RetryLastOperation()
{
apiError = null;
if (lastFailedOperation != null)
{
await lastFailedOperation.Invoke();
}
}
private async Task RetryFromToast()
{
await ToastObj.HideAsync();
await RetryLastOperation();
}
private async Task DismissError()
{
await ToastObj.HideAsync();
}
}---
Real-time Updates
Show toast notifications for real-time events (SignalR, WebSockets).
@page "/realtime"
@implements IAsyncDisposable
<div class="realtime-container">
<h2>Real-time Notifications</h2>
<div class="notification-history">
@foreach (var notification in notifications)
{
<div class="notification-item">
<span class="timestamp">@notification.Time.ToString("HH:mm:ss")</span>
<span class="message">@notification.Message</span>
</div>
}
</div>
</div>
<SfToast @ref="ToastObj"
NewestOnTop="true">
<ToastPosition X="Right" Y="Bottom"></ToastPosition>
</SfToast>
@code {
private SfToast ToastObj;
private List<Notification> notifications = new();
private HubConnection hubConnection;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/notificationHub"))
.Build();
hubConnection.On<string, string>("ReceiveNotification", async (title, message) =>
{
notifications.Add(new Notification
{
Time = DateTime.Now,
Message = message
});
ToastObj.Title = title;
ToastObj.Content = message;
ToastObj.Icon = "e-info";
ToastObj.CssClass = "e-toast-info";
await ToastObj.ShowAsync();
StateHasChanged();
});
await hubConnection.StartAsync();
}
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
private class Notification
{
public DateTime Time { get; set; }
public string Message { get; set; }
}
}---
Best Practices Summary
1. Use appropriate component - Toast for temporary, Message for persistent, Skeleton for loading 2. Combine components - Use together for comprehensive UX 3. Handle all states - Loading, success, error, empty 4. Provide actions - Give users options to respond (retry, undo, view) 5. Clear messaging - Be specific about what happened and why 6. Accessibility - Always include labels and ARIA attributes 7. Performance - Remove components from DOM when not needed 8. Responsive - Test on all screen sizes 9. Consistent positioning - Use same toast position throughout app 10. Error recovery - Always provide way to retry or dismiss errors
---
Next Steps
- Review Toast Features for advanced customization
- Explore Message Implementation for inline alerts
- Check Skeleton Patterns for loading states
- Study Styling Guide for theme customization