Skip to content

Modal Component

Modals are popup dialogs for detailed views, forms, and confirmations.

Overview

Modals are used when you need to:

  • Show detailed information that doesn't fit in a tile
  • Collect user input through forms
  • Confirm destructive actions
  • Display lists or tables of data

Basic Usage

from ContaraNAS.core.ui import Modal, Stack, Text, Button

modal = Modal(
    id="my_modal",
    title="Modal Title",
    children=[
        Text(content="This is the modal content."),
        Text(content="You can put any components here.", variant="secondary"),
    ],
    footer=[
        Button(label="Close", variant="secondary"),
    ],
)

Props

Prop Type Default Description
id str Required Unique identifier for the modal
title str Required Modal header title
size "sm", "md", "lg", "xl" "md" Modal width
children list[Component] [] Main content components
footer list[Component] or None None Footer components (usually buttons)
closable bool True Show close button in header

Opening Modals

To open a modal, return an OpenModal result from an action:

from ContaraNAS.core.action import action, OpenModal


class MyModule(Module):
    @action
    async def show_details(self):
        """Open the details modal."""
        return OpenModal(modal_id="details_modal")

Wire the action to a button:

Button(label="Details", on_click=self.show_details)

Closing Modals

Automatic Close

Buttons without on_click automatically close the modal:

Modal(
    id="confirm",
    title="Confirm",
    children=[...],
    footer=[
        Button(label="Cancel", variant="secondary"),  # Closes modal
    ],
)

Programmatic Close

Return CloseModal from an action:

from ContaraNAS.core.action import action, CloseModal, Notify


@action
async def save_and_close(self) -> list:
    """Save data and close the modal"""
    # Save logic here...

    return [
        Notify(message="Saved successfully!", variant="success"),
        CloseModal(modal_id="edit_modal"),
    ]

Confirmation Dialog

Modal(
    id="delete_confirm",
    title="Delete Item?",
    children=[
        Text(content="Are you sure? This action cannot be undone."),
    ],
    footer=[
        Button(label="Cancel", variant="secondary"),
        Button(label="Delete", variant="danger", on_click=self.delete_item),
    ],
)

Form Modal

Modal(
    id="edit_settings",
    title="Edit Settings",
    children=[
        Stack(direction="vertical", gap="4", children=[
            Input(name="name", label="Name", value=state.name),
            Select(name="category", label="Category", options=[...], value=state.category),
        ]),
    ],
    footer=[
        Button(label="Cancel", variant="secondary"),
        Button(label="Save", on_click=self.save_settings),
    ],
)

Form field values are passed to your action. See Actions - Form Data.

Providing Modals

Implement get_modals() in your module to define available modals:

def get_modals(self) -> list[Modal]:
    """Return modal definitions for this module"""
    return [
        self._build_details_modal(),
        self._build_settings_modal(),
    ]

Key points:

  • Modals are defined once and reused (not recreated each time they open)
  • Return an empty list if your module has no modals (default behavior)
  • Use helper methods to build complex modals
  • Modal IDs must be unique within your module

Dynamic Modals

Build modals based on state:

def get_modals(self) -> list[Modal]:
    """Create one modal per library"""
    modals = []
    for lib in self.state.libraries:
        modals.append(Modal(
            id=f"library_{lib['path']}",
            title=lib["name"],
            children=[...],
        ))
    return modals

Best Practices

  • Use descriptive IDs ("game_details" not "modal1")
  • Include cancel options in footer
  • Keep modals focused — one purpose per modal
  • Use "danger" variant for destructive actions
  • Don't nest modals — one at a time

See Also