Lumeo

Overlay Service

Open dialogs, sheets, drawers, and alert dialogs programmatically from C# code and await their results.

Quick Start

The OverlayService lets you open overlays imperatively instead of declaring them in markup. Every method returns a Task<OverlayResult> so you can await the user's response and read back data they entered.

Step 1 — Add the provider to your layout

Place <OverlayProvider /> once in your root layout. It renders all active overlays.

<!-- MainLayout.razor --> <div class="min-h-screen"> @Body <OverlayProvider /> </div>

Step 2 — Create a content component

Your content component receives an OverlayReference via [CascadingParameter]. Call Overlay.Close(data) to return a result, or Overlay.Cancel() to dismiss without returning data.

<!-- EditProfileForm.razor --> <Stack Gap="4"> <Stack Gap="2"> <Label>Name</Label> <Input @bind-Value="_name" Placeholder="Enter your name..." /> </Stack> <Stack Gap="2"> <Label>Email</Label> <Input @bind-Value="_email" Placeholder="Enter your email..." /> </Stack> <Flex Justify="end" Gap="2" Class="pt-2"> <Button Variant="Button.ButtonVariant.Outline" OnClick="() => Overlay.Cancel()">Cancel</Button> <Button OnClick="Submit">Save</Button> </Flex> </Stack> @code { [CascadingParameter] public OverlayReference Overlay { get; set; } = default!; private string _name = ""; private string _email = ""; private void Submit() { Overlay.Close(new { Name = _name, Email = _email }); } }

Step 3 — Open from code

Inject OverlayService and call one of its Show*Async methods. The call awaits until the user closes or cancels the overlay.

@inject OverlayService OverlayService @using Lumeo.Services @code { private async Task EditProfile() { var result = await OverlayService.ShowDialogAsync<EditProfileForm>("Edit Profile"); if (!result.Cancelled) { var data = result.GetData<dynamic>(); Console.WriteLine($"Name: {data?.Name}, Email: {data?.Email}"); } } }

Dialog

Opens a centered modal dialog with your component as the body content. The dialog includes a title bar and close button by default.

Live Demo

// Basic dialog var result = await OverlayService.ShowDialogAsync<OverlayDemoContent>("Edit Profile"); if (!result.Cancelled) { // The user submitted — result.Data contains the object passed to Close() var data = result.GetData<dynamic>(); } // Dialog with custom width var result = await OverlayService.ShowDialogAsync<OverlayDemoContent>( title: "Edit Profile", options: new OverlayOptions { Class = "max-w-2xl" } );

Sheet

Opens a panel that slides in from any edge of the screen. Use sheets for secondary navigation, settings panels, detail views, or any content that should overlay from a specific direction.

Slide from any side

// Sheet from the right (default side) var result = await OverlayService.ShowSheetAsync<OverlayDemoContent>( title: "Settings", side: Lumeo.Side.Right, size: SheetSize.Default ); // Sheet from the left, large size var result = await OverlayService.ShowSheetAsync<OverlayDemoContent>( title: "Navigation", side: Lumeo.Side.Left, size: SheetSize.Lg ); // Full-width sheet from the bottom var result = await OverlayService.ShowSheetAsync<OverlayDemoContent>( title: "Details", side: Lumeo.Side.Bottom, size: SheetSize.Full );

Sheet sizes

The SheetSize enum controls the width (for left/right) or height (for top/bottom) of the sheet panel.

Size Description
Sm Small — narrow panel for simple content
Default Standard width — good for forms and settings
Lg Large — for detail views and complex content
Xl Extra large — for data tables or wide layouts
Full Full width/height — covers the entire edge

Drawer

Opens a bottom drawer, ideal for mobile-friendly interactions. Drawers slide up from the bottom of the viewport and are commonly used for action menus, date pickers, or compact forms.

Live Demo

var result = await OverlayService.ShowDrawerAsync<OverlayDemoContent>("Edit Profile"); if (!result.Cancelled) { var data = result.GetData<dynamic>(); Console.WriteLine($"Name: {data?.Name}"); }

Alert Dialog

A simple confirm/cancel dialog that requires no custom component. Pass an AlertDialogOptions object to configure the title, description, button text, and whether the action is destructive.

Confirm action (non-destructive)

Standard confirmation before saving or performing a significant action.

var result = await OverlayService.ShowAlertDialogAsync(new AlertDialogOptions { Title = "Save changes?", Description = "This will overwrite your existing data.", ConfirmText = "Save", CancelText = "Cancel" }); if (!result.Cancelled) { await SaveAsync(); }

Destructive action (red confirm button)

Use IsDestructive = true to render the confirm button with destructive styling, signaling an irreversible action.

var result = await OverlayService.ShowAlertDialogAsync(new AlertDialogOptions { Title = "Delete your account?", Description = "This action cannot be undone. All of your data will be permanently removed.", ConfirmText = "Delete Account", CancelText = "Keep Account", IsDestructive = true }); if (!result.Cancelled) { await DeleteAccountAsync(); }

Passing Parameters

Use OverlayParameters to pass data into your content component. The fluent .Add() API chains cleanly and parameters are fed to Blazor's DynamicComponent, so they map directly to [Parameter] properties on your component.

Building parameters (caller side)

var parameters = new OverlayParameters() .Add("UserId", 42) .Add("Mode", "edit") .Add("ReadOnly", false); var result = await OverlayService.ShowDialogAsync<EditUserForm>( "Edit User", parameters: parameters );

Receiving parameters (content component)

Each key in OverlayParameters maps to a [Parameter] property with the same name. You can also access raw parameters through the OverlayReference.Parameters property.

@code { // Option A: Blazor [Parameter] binding (recommended) [Parameter] public int UserId { get; set; } [Parameter] public string Mode { get; set; } = ""; [Parameter] public bool ReadOnly { get; set; } // Always available via cascading parameter [CascadingParameter] public OverlayReference Overlay { get; set; } = default!; // Option B: Read from OverlayReference.Parameters directly protected override void OnInitialized() { var userId = Overlay.Parameters?.Get<int>("UserId"); } }

Getting Results

Every Show*Async method returns a Task<OverlayResult>. The task completes when the user closes or cancels the overlay. Check result.Cancelled to determine which action was taken.

Returning data from Close()

Inside your content component, call Overlay.Close(data) to pass any object back to the caller. The data is available on result.Data (as object?) or via the typed helper result.GetData<T>().

// Inside content component — close with data Overlay.Close(new { Name = _name, Email = _email }); // Close without data (just confirms) Overlay.Close(); // Cancel (result.Cancelled will be true) Overlay.Cancel();

Reading the result (caller side)

var result = await OverlayService.ShowDialogAsync<EditProfileForm>("Edit Profile"); // Check if cancelled if (result.Cancelled) { // User clicked Cancel or dismissed the overlay return; } // Read raw data object? raw = result.Data; // Read typed data — returns default if cast fails var profile = result.GetData<ProfileModel>(); if (profile is not null) { await SaveProfileAsync(profile); }

Full round-trip example

This shows the complete flow: open an overlay, let the user fill a form, close it, and read the result.

// Page.razor @inject OverlayService OverlayService @using Lumeo.Services <Button OnClick="OpenEditor">Edit User</Button> @if (_lastSaved is not null) { <Alert>Saved: @_lastSaved</Alert> } @code { private string? _lastSaved; private async Task OpenEditor() { var result = await OverlayService.ShowDialogAsync<UserEditorForm>( "Edit User", parameters: new OverlayParameters() .Add("UserId", 42) .Add("CurrentName", "Jane Doe") ); if (result.Cancelled) return; var data = result.GetData<dynamic>(); _lastSaved = $"Name={data?.Name}, Email={data?.Email}"; } } // UserEditorForm.razor <Stack Gap="4"> <Input @bind-Value="_name" Placeholder="Name" /> <Input @bind-Value="_email" Placeholder="Email" /> <Flex Justify="end" Gap="2"> <Button Variant="Button.ButtonVariant.Outline" OnClick="() => Overlay.Cancel()">Cancel</Button> <Button OnClick="Save">Save</Button> </Flex> </Stack> @code { [CascadingParameter] public OverlayReference Overlay { get; set; } = default!; [Parameter] public int UserId { get; set; } [Parameter] public string CurrentName { get; set; } = ""; private string _name = ""; private string _email = ""; protected override void OnInitialized() { _name = CurrentName; } private void Save() => Overlay.Close(new { Name = _name, Email = _email }); }

Options & Customization

All overlay methods accept an optional OverlayOptions record to control behavior and appearance.

PreventClose

Set PreventClose = true to prevent the overlay from being dismissed by clicking outside or pressing Escape. The user must explicitly click a button inside your content component to close the overlay.

var result = await OverlayService.ShowDialogAsync<ImportantForm>( "Required Information", options: new OverlayOptions { PreventClose = true } );

Live Demo — PreventClose

Try clicking outside the dialog or pressing Escape. It won't close until you use a button.

Custom CSS Class

Use the Class property to add Tailwind utility classes to the overlay container. This is especially useful for controlling width.

// Make the dialog wider options: new OverlayOptions { Class = "max-w-2xl" } // Extra wide for data-heavy content options: new OverlayOptions { Class = "max-w-4xl" } // Combine options options: new OverlayOptions { Class = "max-w-3xl", PreventClose = true }

SheetSide and SheetSize

For sheets, SheetSide and SheetSize are passed as direct parameters to ShowSheetAsync. They are also available on OverlayOptions for advanced scenarios.

// Side and size are method parameters (recommended) await OverlayService.ShowSheetAsync<MyComponent>( title: "Settings", side: Lumeo.Side.Left, size: SheetSize.Lg ); // Can also be set via options (they will be overridden by method parameters) await OverlayService.ShowSheetAsync<MyComponent>( title: "Settings", side: Lumeo.Side.Right, size: SheetSize.Xl, options: new OverlayOptions { PreventClose = true } );

Content Component Pattern

Every component rendered inside an overlay receives an OverlayReference as a [CascadingParameter]. This is the component's handle to control the overlay it lives in.

Complete content component example

<!-- CreateUserForm.razor --> @namespace MyApp.Components @using Lumeo.Services <Stack Gap="4" Class="py-2"> <Stack Gap="2"> <Label>Full Name</Label> <Input @bind-Value="_name" Placeholder="Enter name..." /> </Stack> <Stack Gap="2"> <Label>Email</Label> <Input @bind-Value="_email" Placeholder="[email protected]" /> </Stack> <Stack Gap="2"> <Label>Role</Label> <Select @bind-Value="_role"> <SelectTrigger> <SelectValue Placeholder="Select a role" /> </SelectTrigger> <SelectContent> <SelectItem Value="@("admin")">Admin</SelectItem> <SelectItem Value="@("editor")">Editor</SelectItem> <SelectItem Value="@("viewer")">Viewer</SelectItem> </SelectContent> </Select> </Stack> <Flex Justify="end" Gap="2" Class="pt-4"> <Button Variant="Button.ButtonVariant.Outline" OnClick="HandleCancel">Cancel</Button> <Button OnClick="HandleSubmit" disabled="@(!IsValid)">Create User</Button> </Flex> </Stack> @code { // Cascading parameter — always available in overlay content [CascadingParameter] public OverlayReference Overlay { get; set; } = default!; // Parameters passed via OverlayParameters.Add() [Parameter] public string DefaultRole { get; set; } = "viewer"; [Parameter] public int TeamId { get; set; } private string _name = ""; private string _email = ""; private string _role = ""; private bool IsValid => !string.IsNullOrWhiteSpace(_name) && !string.IsNullOrWhiteSpace(_email); protected override void OnInitialized() { _role = DefaultRole; } private void HandleSubmit() { // Close with data — caller receives this in OverlayResult.Data Overlay.Close(new { Name = _name, Email = _email, Role = _role, TeamId }); } private void HandleCancel() { // Cancel — caller sees OverlayResult.Cancelled = true Overlay.Cancel(); } }

API Reference

OverlayService Methods
6 methods

Method Returns Description
ShowDialogAsync<T>(title?, parameters?, options?) Task<OverlayResult> Opens a centered modal dialog with component T as the body.
ShowSheetAsync<T>(title?, side, size, parameters?, options?) Task<OverlayResult> Opens a slide-in panel from the specified edge with component T.
ShowDrawerAsync<T>(title?, parameters?, options?) Task<OverlayResult> Opens a bottom drawer with component T.
ShowAlertDialogAsync(AlertDialogOptions) Task<OverlayResult> Shows a built-in confirm/cancel alert dialog. No custom component needed.
Close(overlayId, result?) void Closes a specific overlay by ID and returns data. Typically called internally via OverlayReference.Close().
Cancel(overlayId) void Cancels a specific overlay by ID. Typically called internally via OverlayReference.Cancel().

OverlayResult

Returned by all Show*Async methods when the overlay closes.

Member Type Description
Cancelled bool true when the user dismissed the overlay without confirming (clicked Cancel, backdrop, or pressed Escape).
Data object? The raw value passed to Overlay.Close(data). Null when cancelled or closed without data.
GetData<T>() T? Attempts to cast Data to type T. Returns default if the cast fails.

Static factory methods: OverlayResult.Ok(data?) and OverlayResult.CancelResult().

OverlayReference

Cascaded into every content component. This is how your component communicates with the overlay it lives in.

Member Type Description
Id string Unique identifier for this overlay instance.
Parameters OverlayParameters? The parameters passed when the overlay was opened. Use .Get<T>(name) to read values.
Close(result?) void Close the overlay and return data to the caller. Pass any object as the result.
Cancel() void Dismiss the overlay without returning data. Sets Cancelled = true on the result.

OverlayParameters

Fluent builder for passing named values to content components.

Method Returns Description
Add(string name, object value) OverlayParameters Adds a named parameter. Returns this for chaining.
Get<T>(string name) T? Retrieves a parameter by name, cast to T. Returns default if not found or wrong type.
ToDictionary() Dictionary<string, object> Converts all parameters to a dictionary. Used internally by the overlay provider.

OverlayOptions

Record type that controls overlay appearance and behavior.

Property Type Default Description
Class string? null Additional CSS classes applied to the overlay container (e.g., max-w-2xl).
PreventClose bool false When true, prevents dismissal via backdrop click or Escape key.
SheetSide SheetSide Right Which edge the sheet slides in from. Set automatically by ShowSheetAsync.
SheetSize SheetSize Default Width (left/right) or height (top/bottom) of the sheet. Set automatically by ShowSheetAsync.

AlertDialogOptions

Record type that configures the built-in alert dialog.

Property Type Default Description
Title string "Are you sure?" The heading text displayed at the top of the alert dialog.
Description string? null Optional body text explaining the action or its consequences.
ConfirmText string "Continue" Label for the confirm/action button.
CancelText string "Cancel" Label for the cancel/dismiss button.
IsDestructive bool false When true, the confirm button renders with destructive styling (red).

SheetSide Enum

Value Description
Top Slides down from the top of the viewport.
Right Slides in from the right edge. Default value.
Bottom Slides up from the bottom of the viewport.
Left Slides in from the left edge.

SheetSize Enum

Value Description
Sm Small — compact panel for simple content.
Default Standard size — suitable for most forms and settings panels.
Lg Large — for detail views and complex content.
Xl Extra large — for data tables or wide layouts.
Full Full width or height — covers the entire edge of the viewport.