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.
Welcome Back
Don't have an account? Sign up
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
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | |