Building My First Web App with FastHTML: What I Learned

When I first heard about FastHTML, I was skeptical. After spending a week building an internal tool with it, I realized FastHTML isn't just another framework—it's a completely different way of thinking about web development.

Why I Tried FastHTML

I needed to build an internal dashboard for our team at Divami. Normally, I'd reach for React, but I wanted to try something different. FastHTML's pitch was simple: write everything in Python, no JavaScript required. Since I'm still learning frontend development.

What Confused Me Initially

you write HTML as Python functions:

def home():
    return Div(
        H1("Welcome"),
        P("This is my content")
    )

My first reaction: "This looks wrong. Where are the templates?"

But then it clicked—this Python code IS the template. And because it's Python, I get autocomplete, type checking, and proper error messages.

The Aha Moment

The architecture finally made sense when I understood the request flow:

graph LR
    A[Python Code] --> B[FastHTML App]
    B --> C[Starlette ASGI]
    C --> D[HTML Response]
    D --> E[HTMX Frontend]
    E --> F[Dynamic Updates]
    F --> C

This is powerful because when you click a button, instead of reloading the page, HTMX sends a request to Python, which returns just the HTML fragment that needs updating. No JSON parsing, no state management, no useEffect hooks. Just Python functions returning HTML.

Building a Real Feature: The Todo List

I started with a simple todo app to learn the basics. Here's what I actually built:

from fasthtml.common import *

app, rt = fast_app()
todos = []

@rt("/")
def home():
    return Titled(
        "My Todo App",
        Form(
            Input(name="task", placeholder="What needs to be done?"),
            Button("Add Task"),
            hx_post="/add",
            hx_target="#todo-list",
            hx_swap="beforeend"
        ),
        Ul(*[Li(todo) for todo in todos], id="todo-list")
    )

@rt("/add")
def add(task: str):
    if task.strip():
        todos.append(task.strip())
        return Li(task)

serve()

1. The hx_post attribute

When I submit the form, HTMX intercepts it, sends a request to /add, FastHTML handles it, I return just a <li> element, and HTMX inserts it into #todo-list. No page reload. The task appears instantly.

2. No JSON serialization needed

In React, I would:

  • Frontend: Serialize form → POST JSON → Parse response → Update state → Re-render
  • Backend: Parse JSON → Process → Return JSON

In FastHTML:

  • Return HTML.

3. Type hints actually work

The task: str parameter automatically extracts data from the form field. No request.form.get("task") needed. It felt like magic.

The Challenges I Faced

Challenge 1: Debugging HTMX Requests

When something broke, it was hard to see what was happening. I learned to use hx-on::after-request to log responses:

Button("Add", hx_on__after_request="console.log(event.detail)")

This helped me see exactly what HTML was being returned.

Challenge 2: Styling Components

FastHTML doesn't have built-in CSS-in-Python. You either add classes or use inline styles. Coming from React with Tailwind, this felt like a step back. I ended up including Tailwind via CDN in the head.

Challenge 3: Form Validation

Basic validation works (required=True), but custom validation requires writing your own logic. There's no built-in form library like WTForms integration yet.

Speed of development: I had a working CRUD app in under 2 hours. No npm install, no build step, no webpack config.

Performance: Server-side rendering is fast. Initial page loads are instant, updates feel snappy.

Simplicity: Everything is Python. No context switching between languages.

When Would I Use FastHTML?

After this experience, I'd reach for FastHTML when:

✅ Building internal tools and admin dashboards
✅ Prototyping ideas quickly
✅ Projects where the team knows Python but not React ✅ Apps that need SEO-friendly server-side rendering

I wouldn't use it for:

❌ Highly interactive SPAs with complex client-side state
❌ Projects requiring heavy frontend libraries (charts, maps)
❌ Teams that already have strong React expertise

My Verdict

FastHTML isn't trying to replace React . It's for developers who want to build interactive web apps in pure Python without reaching for a separate frontend framework.

For me as someone learning AI engineering, this is valuable. I can now build demos and internal tools without learning a full JavaScript framework. The first hour was confusing, but once it clicked, everything made sense.

Would I use it in production at scale? Not yet—I haven't tested it with complex state or large datasets. But for the right use case, it's a powerful tool.

Resources That Helped Me

  • Official docs: docs.fastht.ml - Actually well-written and practical
  • HTMX docs: htmx.org - You need to understand HTMX to really get FastHTML
  • FastHTML GitHub: The examples directory has really good patterns