CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-fasthtml

The fastest way to create HTML apps - a next-generation Python web framework for building fast, scalable web applications with minimal code

Pending
Overview
Eval results
Files

notifications.mddocs/

Notifications and Toast Messages

User notification system with toast messages, flash messages, and session-based notification management for enhanced user experience.

Capabilities

Toast Message Creation

Create styled toast notifications with different types and behaviors.

def Toast(message: str, typ: str = "info", dismiss: bool = False, duration: int = 5000):
    """
    Create toast notification message.
    
    Generates a toast notification with specified type, styling,
    and behavior options including auto-dismiss functionality.
    
    Args:
        message: Notification message text
        typ: Toast type ('info', 'success', 'warning', 'error')
        dismiss: Whether toast can be manually dismissed
        duration: Auto-dismiss duration in milliseconds (0 = no auto-dismiss)
        
    Returns:
        Toast notification element with styling and behavior
    """

Session-Based Toast Management

Manage toast notifications through user sessions for persistence across requests.

def add_toast(sess, message: str, typ: str = "info", dismiss: bool = False):
    """
    Add toast notification to user session.
    
    Stores toast notification in session for display on next
    page load or HTMX response.
    
    Args:
        sess: User session object
        message: Notification message
        typ: Toast type ('info', 'success', 'warning', 'error')
        dismiss: Whether toast can be dismissed
    """

def render_toasts(sess):
    """
    Render all pending toast notifications from session.
    
    Retrieves and renders all stored toast notifications,
    then clears them from the session.
    
    Args:
        sess: User session object
        
    Returns:
        Collection of toast notification elements
    """

Application Integration

Integrate toast system into FastHTML applications with automatic rendering.

def setup_toasts(app, duration: int = 5000):
    """
    Set up toast notification system in FastHTML app.
    
    Configures automatic toast rendering and JavaScript
    integration for a complete notification system.
    
    Args:
        app: FastHTML application instance
        duration: Default toast duration in milliseconds
    """

def toast_after(resp, req, sess):
    """
    After-request handler for toast notifications.
    
    Automatically adds pending toasts to responses,
    ensuring notifications are displayed to users.
    
    Args:
        resp: HTTP response object
        req: HTTP request object
        sess: User session object
        
    Returns:
        Modified response with toast notifications
    """

Usage Examples

Basic Toast Notifications

from fasthtml.common import *

app, rt = fast_app(secret_key='demo-key')

# Set up toast system
setup_toasts(app, duration=4000)

@rt('/')
def homepage():
    return Titled("Toast Notifications Demo",
        Container(
            H1("Toast Notification System"),
            P("Click the buttons below to see different types of notifications."),
            
            Div(
                Button(
                    "Show Info Toast",
                    hx_post="/toast/info",
                    hx_target="#toast-area",
                    hx_swap="beforeend",
                    cls="primary"
                ),
                Button(
                    "Show Success Toast",
                    hx_post="/toast/success",
                    hx_target="#toast-area",
                    hx_swap="beforeend",
                    cls="secondary"
                ),
                Button(
                    "Show Warning Toast",
                    hx_post="/toast/warning",
                    hx_target="#toast-area",
                    hx_swap="beforeend"
                ),
                Button(
                    "Show Error Toast",
                    hx_post="/toast/error",
                    hx_target="#toast-area",
                    hx_swap="beforeend"
                ),
                style="display: flex; gap: 1rem; margin: 2rem 0;"
            ),
            
            # Toast display area
            Div(id="toast-area", style="position: fixed; top: 1rem; right: 1rem; z-index: 1000;")
        )
    )

@rt('/toast/info', methods=['POST'])
def show_info_toast():
    return Toast("This is an info message!", typ="info", dismiss=True)

@rt('/toast/success', methods=['POST'])
def show_success_toast():
    return Toast("Operation completed successfully!", typ="success", dismiss=True)

@rt('/toast/warning', methods=['POST'])
def show_warning_toast():
    return Toast("Warning: Please check your input!", typ="warning", dismiss=True)

@rt('/toast/error', methods=['POST'])
def show_error_toast():
    return Toast("Error: Something went wrong!", typ="error", dismiss=True, duration=0)  # No auto-dismiss for errors

Session-Based Toast Messages

from fasthtml.common import *

app, rt = fast_app(secret_key='demo-key')
setup_toasts(app)

@rt('/')
def form_page(request):
    # Render any pending toasts
    toasts = render_toasts(request.session)
    
    return Titled("Form with Notifications",
        Container(
            H1("User Registration Form"),
            
            # Display toast area
            Div(*toasts, id="toast-container"),
            
            Form(
                Div(
                    Label("Username:", for_="username"),
                    Input(type="text", name="username", id="username", required=True),
                    cls="form-group"
                ),
                Div(
                    Label("Email:", for_="email"),
                    Input(type="email", name="email", id="email", required=True),
                    cls="form-group"
                ),
                Div(
                    Label("Password:", for_="password"),
                    Input(type="password", name="password", id="password", required=True),
                    cls="form-group"
                ),
                Button("Register", type="submit"),
                method="post",
                action="/register"
            )
        )
    )

@rt('/register', methods=['POST'])
def register_user(username: str, email: str, password: str, request):
    # Simulate validation
    if len(username) < 3:
        add_toast(request.session, "Username must be at least 3 characters long", "error")
        return Redirect('/')
    
    if len(password) < 6:
        add_toast(request.session, "Password must be at least 6 characters long", "error")
        return Redirect('/')
    
    # Simulate checking if user exists
    if username.lower() in ['admin', 'root', 'test']:
        add_toast(request.session, f"Username '{username}' is already taken", "warning")
        return Redirect('/')
    
    # Simulate successful registration
    add_toast(request.session, f"Welcome {username}! Your account has been created successfully.", "success")
    add_toast(request.session, "Please check your email to verify your account.", "info")
    
    return Redirect('/dashboard')

@rt('/dashboard')
def dashboard(request):
    toasts = render_toasts(request.session)
    
    return Titled("Dashboard",
        Container(
            Div(*toasts, id="toast-container"),
            H1("User Dashboard"),
            P("Welcome to your dashboard!"),
            
            Div(
                Button(
                    "Save Settings",
                    hx_post="/save-settings",
                    hx_target="#toast-container",
                    hx_swap="innerHTML"
                ),
                Button(
                    "Delete Account",
                    hx_post="/delete-account",
                    hx_target="#toast-container",
                    hx_swap="innerHTML",
                    hx_confirm="Are you sure you want to delete your account?"
                )
            )
        )
    )

@rt('/save-settings', methods=['POST'])
def save_settings(request):
    add_toast(request.session, "Settings saved successfully!", "success")
    return render_toasts(request.session)

@rt('/delete-account', methods=['POST'])
def delete_account(request):
    add_toast(request.session, "Account deletion failed. Please contact support.", "error")
    return render_toasts(request.session)

Advanced Toast System with Custom Styling

from fasthtml.common import *

app, rt = fast_app(secret_key='demo-key')

# Custom toast setup with styling
def custom_toast_setup():
    toast_styles = Style("""
        .toast-container {
            position: fixed;
            top: 1rem;
            right: 1rem;
            z-index: 1000;
            max-width: 400px;
        }
        
        .toast {
            margin-bottom: 0.5rem;
            padding: 0.75rem 1rem;
            border-radius: 0.375rem;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            display: flex;
            align-items: center;
            justify-content: space-between;
            animation: slideIn 0.3s ease-out;
            position: relative;
            overflow: hidden;
        }
        
        .toast-info {
            background-color: #dbeafe;
            border-left: 4px solid #3b82f6;
            color: #1e40af;
        }
        
        .toast-success {
            background-color: #dcfce7;
            border-left: 4px solid #22c55e;
            color: #15803d;
        }
        
        .toast-warning {
            background-color: #fef3c7;
            border-left: 4px solid #f59e0b;
            color: #92400e;
        }
        
        .toast-error {
            background-color: #fee2e2;
            border-left: 4px solid #ef4444;
            color: #dc2626;
        }
        
        .toast-dismiss {
            background: none;
            border: none;
            font-size: 1.2rem;
            cursor: pointer;
            opacity: 0.7;
            margin-left: 1rem;
        }
        
        .toast-dismiss:hover {
            opacity: 1;
        }
        
        .toast-progress {
            position: absolute;
            bottom: 0;
            left: 0;
            height: 3px;
            background-color: rgba(0, 0, 0, 0.2);
            animation: progress linear;
        }
        
        @keyframes slideIn {
            from {
                transform: translateX(100%);
                opacity: 0;
            }
            to {
                transform: translateX(0);
                opacity: 1;
            }
        }
        
        @keyframes progress {
            from { width: 100%; }
            to { width: 0%; }
        }
        
        .toast-auto-dismiss {
            animation: slideOut 0.3s ease-in forwards;
            animation-delay: var(--dismiss-delay, 4.7s);
        }
        
        @keyframes slideOut {
            to {
                transform: translateX(100%);
                opacity: 0;
                margin-bottom: -100px;
            }
        }
    """)
    
    toast_script = Script("""
        function createToast(message, type = 'info', dismiss = true, duration = 5000) {
            const container = document.getElementById('toast-container') || 
                            (() => {
                                const div = document.createElement('div');
                                div.id = 'toast-container';
                                div.className = 'toast-container';
                                document.body.appendChild(div);
                                return div;
                            })();
            
            const toast = document.createElement('div');
            toast.className = `toast toast-${type}`;
            
            const messageSpan = document.createElement('span');
            messageSpan.textContent = message;
            toast.appendChild(messageSpan);
            
            if (dismiss) {
                const dismissBtn = document.createElement('button');
                dismissBtn.className = 'toast-dismiss';
                dismissBtn.innerHTML = '×';
                dismissBtn.onclick = () => removeToast(toast);
                toast.appendChild(dismissBtn);
            }
            
            if (duration > 0) {
                const progress = document.createElement('div');
                progress.className = 'toast-progress';
                progress.style.animationDuration = `${duration}ms`;
                toast.appendChild(progress);
                
                setTimeout(() => removeToast(toast), duration);
            }
            
            container.appendChild(toast);
            return toast;
        }
        
        function removeToast(toast) {
            toast.style.animation = 'slideOut 0.3s ease-in forwards';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }
        
        // Auto-remove toasts with auto-dismiss class
        document.addEventListener('DOMContentLoaded', function() {
            document.querySelectorAll('.toast-auto-dismiss').forEach(toast => {
                const duration = parseInt(toast.dataset.duration) || 5000;
                setTimeout(() => removeToast(toast), duration);
            });
        });
    """)
    
    return [toast_styles, toast_script]

def CustomToast(message: str, typ: str = "info", dismiss: bool = True, duration: int = 5000):
    """Custom toast with enhanced styling and behavior."""
    toast_class = f"toast toast-{typ}"
    if duration > 0:
        toast_class += " toast-auto-dismiss"
    
    elements = [Span(message)]
    
    if dismiss:
        elements.append(
            Button("×", 
                   cls="toast-dismiss",
                   onclick="removeToast(this.parentElement)")
        )
    
    if duration > 0:
        elements.append(
            Div(cls="toast-progress", 
                style=f"animation-duration: {duration}ms;")
        )
    
    return Div(*elements, 
               cls=toast_class,
               data_duration=str(duration) if duration > 0 else None)

@rt('/')
def advanced_toast_demo():
    return Html(
        Head(
            Title("Advanced Toast System"),
            Meta(charset="utf-8"),
            Meta(name="viewport", content="width=device-width, initial-scale=1"),
            *custom_toast_setup()
        ),
        Body(
            Container(
                H1("Advanced Toast Notification System"),
                
                Div(
                    Button(
                        "Custom Info Toast",
                        onclick="createToast('This is a custom info message!', 'info', true, 4000)"
                    ),
                    Button(
                        "Persistent Error Toast",
                        onclick="createToast('This error stays until dismissed!', 'error', true, 0)"
                    ),
                    Button(
                        "Quick Success Toast",
                        onclick="createToast('Quick success!', 'success', false, 2000)"
                    ),
                    Button(
                        "Warning with Long Message",
                        onclick="createToast('This is a longer warning message that demonstrates how the toast system handles extended content gracefully.', 'warning', true, 6000)"
                    ),
                    style="display: flex; flex-direction: column; gap: 1rem; max-width: 300px;"
                ),
                
                # Server-side toast examples
                Div(
                    H2("Server-Side Toasts"),
                    Button(
                        "HTMX Success Toast",
                        hx_post="/custom-toast/success",
                        hx_target="#toast-container",
                        hx_swap="beforeend"
                    ),
                    Button(
                        "HTMX Error Toast",
                        hx_post="/custom-toast/error",
                        hx_target="#toast-container",
                        hx_swap="beforeend"
                    ),
                    style="margin-top: 2rem;"
                )
            ),
            
            # Toast container
            Div(id="toast-container", cls="toast-container")
        )
    )

@rt('/custom-toast/success', methods=['POST'])
def custom_success_toast():
    return CustomToast("Server-side success notification!", "success", True, 3000)

@rt('/custom-toast/error', methods=['POST'])
def custom_error_toast():
    return CustomToast("Server-side error notification!", "error", True, 0)

Integration with Form Validation

from fasthtml.common import *

app, rt = fast_app(secret_key='demo-key')
setup_toasts(app)

@rt('/')
def validation_form(request):
    toasts = render_toasts(request.session)
    
    return Titled("Form Validation with Toasts",
        Container(
            Div(*toasts, id="toast-container"),
            
            H1("User Profile Form"),
            
            Form(
                Div(
                    Label("Full Name:", for_="name"),
                    Input(type="text", name="name", id="name", required=True),
                    cls="form-group"
                ),
                Div(
                    Label("Email:", for_="email"),
                    Input(type="email", name="email", id="email", required=True),
                    cls="form-group"
                ),
                Div(
                    Label("Age:", for_="age"),
                    Input(type="number", name="age", id="age", min="18", max="100"),
                    cls="form-group"
                ),
                Div(
                    Label("Bio:", for_="bio"),
                    Textarea(name="bio", id="bio", rows="4", maxlength="500"),
                    cls="form-group"
                ),
                Button("Save Profile", type="submit"),
                hx_post="/validate-profile",
                hx_target="#toast-container",
                hx_swap="innerHTML"
            )
        )
    )

@rt('/validate-profile', methods=['POST'])
def validate_profile(name: str, email: str, age: int = None, bio: str = "", request):
    errors = []
    warnings = []
    
    # Validation logic
    if len(name.strip()) < 2:
        errors.append("Name must be at least 2 characters long")
    
    if not email or '@' not in email:
        errors.append("Please provide a valid email address")
    
    if age is not None:
        if age < 18:
            errors.append("You must be at least 18 years old")
        elif age > 100:
            warnings.append("Please verify your age is correct")
    
    if len(bio) > 500:
        errors.append("Bio must be 500 characters or less")
    elif len(bio) < 10 and bio:
        warnings.append("Consider adding more details to your bio")
    
    # Create response toasts
    toasts = []
    
    if errors:
        for error in errors:
            toasts.append(Toast(error, "error", dismiss=True, duration=0))
    elif warnings:
        for warning in warnings:
            toasts.append(Toast(warning, "warning", dismiss=True, duration=8000))
        toasts.append(Toast("Profile saved with warnings", "success", dismiss=True))
    else:
        toasts.append(Toast("Profile saved successfully!", "success", dismiss=True))
        if not bio:
            toasts.append(Toast("Tip: Add a bio to complete your profile", "info", dismiss=True))
    
    return toasts

Install with Tessl CLI

npx tessl i tessl/pypi-python-fasthtml

docs

application-routing.md

authentication.md

css-styling.md

development-tools.md

form-handling.md

html-components.md

htmx-integration.md

index.md

javascript-integration.md

notifications.md

svg-components.md

tile.json