Skip to content

Auth Layout

The AuthLayout component creates a centered authentication page layout perfect for login, registration, and password reset forms. Features branding, form fields, footer links, and responsive design.

Goal

By the end of this guide, you'll be able to create beautiful login and registration pages in minutes with zero CSS.


Quick Start

Here's the simplest way to create a login page.

Live Preview

Welcome Back

Don't have an account? Sign up

AuthLayout(
    FormGroup(Input("email", input_type="email"), label="Email"),
    FormGroup(Input("password", input_type="password"), label="Password"),
    Button("Sign In", type="submit", variant="primary", full_width=True),
    title="Welcome Back"
)

Visual Examples & Use Cases

1. Login Page

Complete login form with branding.

@app.get("/login")
def login_page():
    return AuthLayout(
        FormGroup(
            Input("email", input_type="email", placeholder="you@example.com"),
            label="Email Address",
            required=True
        ),
        FormGroup(
            Input("password", input_type="password"),
            label="Password",
            required=True
        ),
        Div(
            A("Forgot password?", href="/reset-password", cls="text-decoration-none"),
            cls="text-end mb-3"
        ),
        Button("Sign In", type="submit", variant="primary", full_width=True),
        title="Welcome Back",
        subtitle="Sign in to your account",
        logo="/static/logo.png",
        footer_text="Don't have an account?",
        footer_link="/register",
        footer_link_text="Sign up",
        form_attrs={
            "hx_post": "/auth/login",
            "hx_target": "#auth-container"
        }
    )

2. Registration Page

Sign-up form with terms acceptance.

@app.get("/register")
def register_page():
    return AuthLayout(
        FormGroup(
            Input(name="name", placeholder="John Doe"),
            label="Full Name",
            required=True
        ),
        FormGroup(
            Input("email", input_type="email", placeholder="you@example.com"),
            label="Email Address",
            required=True
        ),
        FormGroup(
            Input("password", input_type="password"),
            label="Password",
            help_text="At least 8 characters",
            required=True
        ),
        FormGroup(
            Input("confirm_password", input_type="password"),
            label="Confirm Password",
            required=True
        ),
        Checkbox(
            "I agree to the Terms of Service and Privacy Policy",
            name="agree_terms",
            required=True
        ),
        Button("Create Account", type="submit", variant="primary", full_width=True),
        title="Create Account",
        subtitle="Join thousands of users",
        logo="/static/logo.png",
        footer_text="Already have an account?",
        footer_link="/login",
        footer_link_text="Sign in",
        form_attrs={
            "hx_post": "/auth/register",
            "hx_target": "#auth-container"
        }
    )

3. Password Reset

Simple password reset form.

@app.get("/reset-password")
def reset_password_page():
    return AuthLayout(
        P("Enter your email and we'll send you a reset link.", cls="text-muted mb-4"),
        FormGroup(
            Input("email", input_type="email", placeholder="you@example.com"),
            label="Email Address",
            required=True
        ),
        Button("Send Reset Link", type="submit", variant="primary", full_width=True),
        title="Reset Password",
        footer_text="Remember your password?",
        footer_link="/login",
        footer_link_text="Sign in",
        form_attrs={
            "hx_post": "/auth/reset-password",
            "hx_target": "#auth-container"
        }
    )

Practical Functionality

With Social Login

Add OAuth buttons.

AuthLayout(
    # Social login buttons
    Button(
        Icon("google", cls="me-2"),
        "Continue with Google",
        variant="outline-dark",
        full_width=True,
        cls="mb-2",
        onclick="window.location='/auth/google'"
    ),
    Button(
        Icon("github", cls="me-2"),
        "Continue with GitHub",
        variant="outline-dark",
        full_width=True,
        cls="mb-3",
        onclick="window.location='/auth/github'"
    ),

    # Divider
    Div(
        Hr(cls="flex-grow-1"),
        Span("or", cls="px-3 text-muted"),
        Hr(cls="flex-grow-1"),
        cls="d-flex align-items-center mb-3"
    ),

    # Email/password form
    FormGroup(Input("email", input_type="email"), label="Email"),
    FormGroup(Input("password", input_type="password"), label="Password"),
    Button("Sign In", type="submit", variant="primary", full_width=True),

    title="Welcome Back"
)

With Error Handling

Show validation errors.

@app.post("/auth/login")
def login(email: str, password: str, req):
    # Validate
    if not email or "@" not in email:
        return AuthLayout(
            FormGroup(
                Input("email", input_type="email", value=email),
                label="Email",
                error="Please enter a valid email",
                is_invalid=True
            ),
            FormGroup(
                Input("password", input_type="password"),
                label="Password"
            ),
            Button("Sign In", type="submit", variant="primary", full_width=True),
            title="Welcome Back"
        )

    # Authenticate
    user = authenticate(email, password)
    if not user:
        return ErrorDialog(
            message="Invalid email or password",
            title="Login Failed",
            show=True
        )

    # Success
    req.session["user_id"] = user.id
    return hx_redirect("/dashboard")

With Loading State

Show loading during authentication.

AuthLayout(
    FormGroup(Input("email", input_type="email"), label="Email"),
    FormGroup(Input("password", input_type="password"), label="Password"),
    LoadingButton(
        "Sign In",
        endpoint="/auth/login",
        loading_text="Signing in...",
        variant="primary",
        full_width=True
    ),
    title="Welcome Back",
    form_attrs={
        "hx_post": "/auth/login",
        "hx_indicator": "#loading"
    }
)

Integration Patterns

With HTMX

Full HTMX-powered auth flow.

@app.get("/login")
def login_page():
    return Html(
        Head(Title("Login")),
        Body(
            Div(
                AuthLayout(
                    FormGroup(Input("email", input_type="email"), label="Email"),
                    FormGroup(Input("password", input_type="password"), label="Password"),
                    Button("Sign In", type="submit", variant="primary", full_width=True),
                    title="Welcome Back",
                    form_attrs={
                        "hx_post": "/auth/login",
                        "hx_target": "#auth-container",
                        "hx_swap": "outerHTML"
                    }
                ),
                id="auth-container"
            )
        )
    )

@app.post("/auth/login")
def login(email: str, password: str, req):
    user = authenticate(email, password)

    if user:
        req.session["user_id"] = user.id
        return hx_redirect("/dashboard")
    else:
        # Return form with error
        return Div(
            ErrorDialog(
                message="Invalid credentials",
                show=True,
                hx_swap_oob="true"
            ),
            AuthLayout(...),  # Re-render form
            id="auth-container"
        )

Multi-Step Registration

Wizard-style registration.

@app.get("/register/step1")
def register_step1():
    return AuthLayout(
        FormGroup(Input(name="email"), label="Email"),
        FormGroup(Input("password", input_type="password"), label="Password"),
        Button("Next", type="submit", variant="primary", full_width=True),
        title="Create Account",
        subtitle="Step 1 of 3",
        form_attrs={"hx_post": "/register/step2"}
    )

@app.post("/register/step2")
def register_step2(email: str, password: str):
    # Save to session
    req.session["reg_email"] = email
    req.session["reg_password"] = password

    return AuthLayout(
        FormGroup(Input(name="name"), label="Full Name"),
        FormGroup(Input(name="company"), label="Company"),
        Button("Next", type="submit", variant="primary", full_width=True),
        title="Tell us about yourself",
        subtitle="Step 2 of 3",
        form_attrs={"hx_post": "/register/step3"}
    )

Parameter Reference

Parameter Type Default Description
*fields Any Required Form fields and content
title str \| None None Page title
subtitle str \| None None Page subtitle
logo str \| None None Logo image URL
footer_text str \| None None Footer text (e.g., "Don't have an account?")
footer_link str \| None None Footer link URL
footer_link_text str \| None None Footer link text
form_attrs dict \| None None Additional form attributes (HTMX, etc.)
**kwargs Any - Additional HTML attributes

Best Practices

✅ Do This

# Use clear titles
AuthLayout(
    ...,
    title="Welcome Back",
    subtitle="Sign in to continue"
)

# Include branding
AuthLayout(
    ...,
    logo="/static/logo.png"
)

# Provide navigation
AuthLayout(
    ...,
    footer_text="Don't have an account?",
    footer_link="/register",
    footer_link_text="Sign up"
)

# Use HTMX for smooth UX
form_attrs={
    "hx_post": "/auth/login",
    "hx_target": "#auth-container"
}

❌ Don't Do This

# Don't skip error handling
# Always validate and show errors

# Don't forget footer links
# Users need to navigate between login/register

# Don't use inline styles
# Use Bootstrap classes instead

# Don't skip HTTPS
# Always use secure connections for auth

faststrap.layouts.auth.AuthLayout(*form_fields, title='Sign In', subtitle=None, logo=None, brand_name=None, action='/login', method='post', footer_text=None, footer_link=None, footer_link_text=None, **kwargs)

Centered authentication page layout with branding.

Perfect for login, register, and password reset pages. Provides a clean, centered card with logo, title, form fields, and footer.

Parameters:

Name Type Description Default
*form_fields Any

Form field components (Input, FormGroup, Button, etc.)

()
title str

Page title (e.g., "Sign In", "Create Account")

'Sign In'
subtitle str | None

Optional subtitle text

None
logo str | None

URL to logo image

None
brand_name str | None

Brand name text (shown if no logo)

None
action str

Form action URL

'/login'
method str

Form method (post, get)

'post'
footer_text str | None

Footer text (e.g., "Don't have an account?")

None
footer_link str | None

Footer link URL

None
footer_link_text str | None

Footer link text (e.g., "Sign up")

None
**kwargs Any

Additional HTML attributes for container

{}

Returns:

Type Description
Div

Div with centered auth layout

Example

Login page:

AuthLayout( ... FormGroup(Input(name="email", input_type="email"), label="Email"), ... FormGroup(Input(name="password", input_type="password"), label="Password"), ... Button("Sign In", type="submit", variant="primary", cls="w-100"), ... title="Welcome Back", ... subtitle="Sign in to your account", ... logo="/static/logo.png", ... footer_text="Don't have an account?", ... footer_link="/register", ... footer_link_text="Sign up" ... )

Register page:

AuthLayout( ... Input(name="name", placeholder="Full Name"), ... Input(name="email", input_type="email", placeholder="Email"), ... Input(name="password", input_type="password", placeholder="Password"), ... Button("Create Account", type="submit"), ... title="Get Started", ... brand_name="MyApp", ... action="/register", ... footer_text="Already have an account?", ... footer_link="/login", ... footer_link_text="Sign in" ... )

With HTMX:

AuthLayout( ... Input(name="email"), ... Input(name="password"), ... Button("Sign In"), ... title="Sign In", ... hx_post="/auth/login", ... hx_target="#auth-container" ... )

Note

The layout is responsive and centers vertically on the page. Form fields should include their own labels or use FormGroup.

Source code in src/faststrap/layouts/auth.py
def AuthLayout(
    *form_fields: Any,
    title: str = "Sign In",
    subtitle: str | None = None,
    logo: str | None = None,
    brand_name: str | None = None,
    action: str = "/login",
    method: str = "post",
    footer_text: str | None = None,
    footer_link: str | None = None,
    footer_link_text: str | None = None,
    **kwargs: Any,
) -> Div:
    """Centered authentication page layout with branding.

    Perfect for login, register, and password reset pages. Provides
    a clean, centered card with logo, title, form fields, and footer.

    Args:
        *form_fields: Form field components (Input, FormGroup, Button, etc.)
        title: Page title (e.g., "Sign In", "Create Account")
        subtitle: Optional subtitle text
        logo: URL to logo image
        brand_name: Brand name text (shown if no logo)
        action: Form action URL
        method: Form method (post, get)
        footer_text: Footer text (e.g., "Don't have an account?")
        footer_link: Footer link URL
        footer_link_text: Footer link text (e.g., "Sign up")
        **kwargs: Additional HTML attributes for container

    Returns:
        Div with centered auth layout

    Example:
        Login page:
        >>> AuthLayout(
        ...     FormGroup(Input(name="email", input_type="email"), label="Email"),
        ...     FormGroup(Input(name="password", input_type="password"), label="Password"),
        ...     Button("Sign In", type="submit", variant="primary", cls="w-100"),
        ...     title="Welcome Back",
        ...     subtitle="Sign in to your account",
        ...     logo="/static/logo.png",
        ...     footer_text="Don't have an account?",
        ...     footer_link="/register",
        ...     footer_link_text="Sign up"
        ... )

        Register page:
        >>> AuthLayout(
        ...     Input(name="name", placeholder="Full Name"),
        ...     Input(name="email", input_type="email", placeholder="Email"),
        ...     Input(name="password", input_type="password", placeholder="Password"),
        ...     Button("Create Account", type="submit"),
        ...     title="Get Started",
        ...     brand_name="MyApp",
        ...     action="/register",
        ...     footer_text="Already have an account?",
        ...     footer_link="/login",
        ...     footer_link_text="Sign in"
        ... )

        With HTMX:
        >>> AuthLayout(
        ...     Input(name="email"),
        ...     Input(name="password"),
        ...     Button("Sign In"),
        ...     title="Sign In",
        ...     hx_post="/auth/login",
        ...     hx_target="#auth-container"
        ... )

    Note:
        The layout is responsive and centers vertically on the page.
        Form fields should include their own labels or use FormGroup.
    """
    # Build header with logo/brand
    header_elements = []

    if logo:
        header_elements.append(
            Img(
                src=logo,
                alt=brand_name or "Logo",
                cls="mb-4",
                style="max-width: 150px; height: auto;",
            )
        )
    elif brand_name:
        header_elements.append(P(brand_name, cls="h3 mb-4"))

    header_elements.append(H1(title, cls="h4 mb-2"))

    if subtitle:
        header_elements.append(P(subtitle, cls="text-muted mb-4"))

    # Build form
    form_element = Form(
        *form_fields,
        action=action,
        method=method,
    )

    # Build footer
    footer_element = None
    if footer_text or footer_link:
        footer_content = []
        if footer_text:
            footer_content.append(footer_text)
            footer_content.append(" ")
        if footer_link and footer_link_text:
            footer_content.append(A(footer_link_text, href=footer_link, cls="text-decoration-none"))

        footer_element = P(
            *footer_content,
            cls="text-center text-muted mt-4 mb-0",
        )

    # Build card content
    card_content = []
    card_content.extend(header_elements)
    card_content.append(form_element)
    if footer_element:
        card_content.append(footer_element)

    # Build card
    auth_card = Card(
        *card_content,
        cls="shadow-sm",
        style="max-width: 400px; width: 100%;",
    )

    # Build container
    base_classes = [
        "min-vh-100",
        "d-flex",
        "align-items-center",
        "justify-content-center",
        "py-5",
    ]
    user_cls = kwargs.pop("cls", "")
    all_classes = merge_classes(" ".join(base_classes), user_cls)

    attrs: dict[str, Any] = {"cls": all_classes}
    attrs.update(convert_attrs(kwargs))

    return Div(
        Container(
            Div(
                auth_card,
                cls="d-flex justify-content-center",
            )
        ),
        **attrs,
    )