Are you still using JavaScript modals in your Blazor app? It’s time to level up. Building custom modal components in Blazor not only keeps your app more C#-centric but also makes it easier to manage UI state and enhance reusability.
Understanding the Role of Modals in Web Apps
What is a Modal?
A modal is a UI overlay that captures user attention by presenting information or requiring input without leaving the current page. They’re commonly used for:
- Alert dialogs
- Confirmations
- Forms (e.g., login, signup)
In terms of UX, modals help streamline workflows by keeping users in context.
Why Use Modals in Blazor?
Blazor’s component model is perfect for crafting modals:
- Encapsulation: Modal behavior and styles can be neatly contained.
- Reusability: Easily reuse modals across multiple pages.
- C# integration: You can manage modal state and interactions purely with .NET.
Setting Up Your Blazor Project for Modal Components
Prerequisites and Tools
To follow this guide, ensure you have:
- .NET 8 SDK
- Visual Studio 2022+ or VS Code
- A Blazor WebAssembly or Server App
Structuring Your Project for Reusability
A good starting point:
/Components/Modal
— All modal-related files/wwwroot/css/modal.css
— Styles for modal animation and layout
Keep markup, logic, and styles modular and tidy.
Building the Basic Modal Component
Creating the Modal Markup and Styles
Create Modal.razor
:
<div class="modal-overlay" @onclick="Close">
<div class="modal-container" @onclick:stopPropagation>
@ChildContent
</div>
</div>
Add styles in modal.css
:
.modal-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-container {
background: white;
padding: 1.5rem;
border-radius: 10px;
min-width: 300px;
}
Handling Visibility and State
Update Modal.razor.cs
:
[Parameter] public bool IsVisible { get; set; }
[Parameter] public EventCallback OnClose { get; set; }
private async Task Close()
{
if (OnClose.HasDelegate)
await OnClose.InvokeAsync();
}
Conditionally render in Modal.razor
:
@if (IsVisible)
{
<div class="modal-overlay" @onclick="Close">
<div class="modal-container" @onclick:stopPropagation>
@ChildContent
</div>
</div>
}
Adding Slot-like Content Support
Add:
[Parameter] public RenderFragment? ChildContent { get; set; }
This allows flexible insertion of any content into the modal.
Enhancing the Modal with Animations
Using CSS Transitions for Smooth Effects
Update modal.css
:
.modal-container {
opacity: 0;
transform: scale(0.9);
transition: all 0.3s ease;
}
.modal-overlay.show .modal-container {
opacity: 1;
transform: scale(1);
}
Use a @ref
to toggle the show
class after render.
Integrating with Blazor Lifecycle Events
In OnAfterRenderAsync
, apply CSS class:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && IsVisible)
{
await JS.InvokeVoidAsync("setTimeout", 0); // trigger class change
}
}
Making the Modal Truly Reusable
Parameterizing Modal Behavior
Add parameters:
[Parameter] public string Title { get; set; } = "";
[Parameter] public string Theme { get; set; } = "light";
Render them dynamically in the markup.
Creating an EventCallback API
Expose user interactions:
[Parameter] public EventCallback OnConfirm { get; set; }
[Parameter] public EventCallback OnCancel { get; set; }
Common Modal Use Cases in Blazor
Form Inside a Modal
Embed a EditForm
for login/signup. Example:
<EditForm Model="loginModel" OnValidSubmit="HandleLogin">
<InputText @bind-Value="loginModel.Username" />
<InputText @bind-Value="loginModel.Password" type="password" />
<button type="submit">Login</button>
</EditForm>
Alert and Confirmation Dialogs
Add ModalConfirm.razor
:
<Modal IsVisible="@IsOpen" OnClose="Hide">
<h3>Are you sure?</h3>
<button @onclick="Confirm">Yes</button>
<button @onclick="Hide">Cancel</button>
</Modal>
Advanced Tips and Best Practices
Accessibility and Keyboard Support
To make your modals accessible:
- Add
role="dialog"
andaria-modal="true"
to the modal container. - Use
tabindex="-1"
to allow focus on the modal. - Set initial focus to the modal when it opens. You can do this in
OnAfterRenderAsync
using JavaScript interop:
// JS function
function focusModal() {
document.querySelector('.modal-container')?.focus();
}
await JS.InvokeVoidAsync("focusModal");
- Handle
Escape
key to close the modal:
@onkeydown="HandleKeyDown"
private void HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Escape") Close();
}
Lazy Loading and Performance Optimization
Heavy content inside modals (like charts or long forms) can slow down page loads. Optimize by rendering conditionally:
@if (IsVisible)
{
<MyHeavyComponent />
}
This ensures the modal content isn’t loaded until actually visible, improving startup performance and responsiveness.
FAQ: Your Questions About Blazor Modals
Technically yes, but UX-wise it’s not recommended. Manage modal stack manually if needed.
Add a KeyboardListener
JS interop or use onkeydown
and handle key event in Blazor.
Absolutely. Works in both Blazor Server and WASM.
Conclusion: Create Better UX with Reusable Blazor Modals
If you’re tired of clunky modal logic, this guide gives you a solid base to build flexible, animated, and fully .NET-integrated modal components. Implement it once and reuse it across your app with zero JavaScript.
Let me know how you use modals in your app — do you use custom components or 3rd-party libraries?