dioxus-modal

Crates.iodioxus-modal
lib.rsdioxus-modal
version0.3.1
created_at2025-04-18 15:42:07.210428+00
updated_at2026-01-13 12:08:28.887268+00
descriptionModal composable for Dioxus
homepage
repository
max_upload_size
id1639574
size76,257
Dmndz (d14mndz)

documentation

README

We're dsplce.co, check out our work on our website: dsplce.co πŸ–€

dioxus-modal

Modal composable for Dioxus β€” A minimal and type-safe framework for modals in Dioxus applications.

βΈ»

πŸ–€ Features

βœ… Type-safe modal system with generics
βœ… Pass additional context to your modals
βœ… Close your modals from anywhere with a global fn
βœ… ARIA-compliant
βœ… Esc key handling
βœ… Portal-style rendering with proper z-index
βœ… Proc macro for modal component creation
βœ… Zero external CSS dependencies

βΈ»

πŸ“¦ Installation

Add to your Cargo.toml:

[dependencies]
dioxus-modal = "0.3"

This crate requires Rust 2024 edition and Dioxus version 0.7. Check the compatibility table for other supported Dioxus versions.

Fullstack applications

You need to enable the crate's SSR feature on the server for fullstack apps. In your Cargo.toml:

[features]
server = ["dioxus/server", "dioxus-modal/ssr"]

This will tell dioxus-modal and its dependencies not to perform DOM-related operations at the stage of server side rendering.

βΈ»

πŸ§ͺ Usage

1. Set up modal collector

Add the ModalCollector component to your app to enable rendering modals.

use dioxus::prelude::*;
use dioxus_modal::prelude::*;

#[component]
fn App() -> Element {
    rsx! {
        Router::<Route> {}
        ModalCollector {}
    }
}

2. Create modal component

Imagine in your application there is a user list view, and you want to add the functionality to delete a user to it. You decide a confirmation dialog would come in handy.

In dioxus-modal, your modal component needs to adhere to the following signature:

#[modal]
pub fn ConfirmationModal(input: Input, ctx: Context, close: fn()) -> Element;

Where:

  • Input is dynamic data typically not known until the modal's opening is triggered (in our example it would be the user to delete). Should satisfy 'static
  • Context is something constant, passed to the modal on registration and thus not changeable (eg. a function responsible for user deletion). Should satisfy 'static + Clone

Let's implement the confirmation dialog:

use dioxus_modal::prelude::*;

#[modal]
pub fn ConfirmationModal(user: User, delete_callback: fn(String), close: fn()) -> Element {
    rsx! {
        div {
            class: "confirmation-modal",
            h2 { "Confirm Action" }
            p { "Are you sure you want to delete {user.name}?" }

            div {
                class: "confirmation-modal__actions",
                button {
                    onclick: move |_| close(),
                    "Cancel"
                }
                button {
                    onclick: move |_| {
                        delete_callback(user.id.clone());
                        close();
                    },
                    "Confirm"
                }
            }
        }
    }
}

3. Use the modal

Now that you've defined the confirmation modal, let's call it using the use_modal! macro:

use dioxus::{logger::tracing, prelude::*};
use dioxus_modal::prelude::*;

#[derive(Clone, PartialEq)]
struct User {
    id: String,
    name: String,
}

#[component]
fn UsersView(users: Vec<User>) -> Element {
    let delete_user = move |id: String| {
        tracing::info!("Deleting user with id {id}");
    };

    // Registers the modal
    let modal = use_modal!(ConfirmationModal, delete_user);

    let on_delete = EventHandler::new(move |user: User| {
        modal.open(user.clone());
    });

    rsx! {
        // ❗ Notice the `ConfirmationModal` is not mounted directly
        // anywhere β€” it is the `ModalCollector`'s job to render modals
        ul {
            for user in users {
                li {
                    key: "{user.id}",
                    "{user.name}"
                    button {
                        onclick: move |_| on_delete.call(user.clone()),
                        "Delete"
                    }
                }
            }
        }
    }
}

βΈ»

πŸ“ API Reference

ModalCollector

Singleton component that manages modal state and rendering.

#[modal]

Proc macro that helps the ModalCollector render your modals.

use_modal!

Creates a typed modal controller:

// Without context
let modal = use_modal!(ModalComponent);

// With context
let modal = use_modal!(ModalComponent, context);

Returns a modal struct with the methods:

  • open(args) - Opens the modal with provided arguments
  • close() - Closes the modal

close

Close the modal from anywhere in your application:

dioxus_modal::close();

Defining a modal

With both context and input:

#[modal]
pub fn ModalComponent(input: Input, ctx: Context, close: fn()) -> Element {
    rsx! {
        // ...
    }
}

let modal = use_modal!(ModalComponent, context);
modal.open(input)

Skipping context:

#[modal]
pub fn ModalComponent(input: Input, ctx: (), close: fn()) -> Element {
    rsx! {
        // ...
    }
}

let modal = use_modal!(ModalComponent);
modal.open(input)

Skipping input:

#[modal]
pub fn ModalComponent(input: (), ctx: Context, close: fn()) -> Element {
    rsx! {
        // ...
    }
}

let modal = use_modal!(ModalComponent, context);
modal.open(())

Modal Features

  • Accessibility: Proper ARIA attributes (role="dialog", aria-modal)
  • Keyboard Navigation: Esc key closes modal out of the box
  • Global State: Modals have a single place to render, and there is always one modal visible at a time (why would you want to show more than one modal at a time? 🀨)
  • Overlay: Semi-transparent backdrop with proper positioning
  • Responsive: Full viewport coverage with centered content
  • Transitions: Built-in smooth enter/leave modal transitions

βΈ»

Compatibility with Dioxus versions

Dioxus version dioxus-modal version
0.7 0.3
0.6 0.2

βΈ»

πŸ“ Repo & Contributions

πŸ“¦ Crate: crates.io/crates/dioxus-modal
πŸ› οΈ Repo: github.com/dsplce-co/dioxus-modal

Contributions, issues, ideas? Hit us up πŸ–€

βΈ»

πŸ”’ License

MIT or Apache-2.0, at your option.

βΈ»

Commit count: 0

cargo fmt