Building Custom Components

Learn how to construct custom reusable UI components and leverage Metupy's dynamic component resolution mechanism.

1. Directory Structure & Naming Rules

Metupy resolves components on-the-fly using components/__init__.py. Create your Python component files directly inside the local components/ directory.

Naming Convention
Class names written in PascalCase automatically map to snake_case filenames.
Badgecomponents/badge.py
InfoCardcomponents/info_card.py

2. Creating a Component Class

A Metupy component is simply a Python class that implements the str magic method to return HTML string output:

components/status_badge.py
# components/status_badge.py

class StatusBadge:
    def __init__(self, text: str, status: str = "active"):
        self.text = text
        self.status = status

    def __str__(self) -> str:
        color = "var(--accent)" if self.status == "active" else "var(--text-muted)"
        bg = "var(--accent-glow)" if self.status == "active" else "var(--bg-surface)"

        return f'''<span style="
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 3px 10px;
            font-size: 0.825rem;
            font-weight: 600;
            border-radius: 20px;
            background: {bg};
            color: {color};
            border: 1px solid var(--border-color);
        ">
            <span style="width: 6px; height: 6px; border-radius: 50%; background: {color};"></span>
            {self.text}
        </span>'''

3. Importing & Using Custom Components

Once the component file is created, import it directly from components inside any page:

pages/demo.py
# pages/demo.py
from metupy.page import Page
from components import StatusBadge

page = Page(title="Custom Component Demo")

badge = StatusBadge("System Online", status="active")

page.title("System Overview")
page.raw(f"<p>Current status: {badge}</p>")
Styling Best Practice
Always reference Metupy's native CSS custom properties (like var(--bg-surface), var(--text-main), and var(--border-color)) inside component inline styles to guarantee full dark mode compatibility.