DEV Community

Cover image for Building a Dynamic Todo App with FastAPI and HTMX
Bek Brace
Bek Brace

Posted on

Building a Dynamic Todo App with FastAPI and HTMX

Hey everyone! 👋 Amir from Bek Brace channel here.
Today, I want to share with you something exciting: building a dynamic, server-rendered Todo App using FastAPI and HTMX.
Trust me, this combo is a game-changer for creating responsive web apps without drowning in JavaScript. Let’s get started!

Why FastAPI and HTMX?

FastAPI: The Modern Python Framework

FastAPI is like the Swiss Army knife of Python web frameworks (and i really mean it!). It’s fast (hence the name), easy to use, and packed with features like automatic API documentation and seamless asynchronous programming. Whether you’re a beginner or an experienced developer, FastAPI makes building APIs feel like a breeze.

HTMX: JavaScript Without Writing JavaScript

HTMX is a lightweight JavaScript library that lets you add interactivity to your app using just HTML attributes. Forget about writing complex JavaScript—HTMX handles it for you. From making HTTP requests to swapping out page content dynamically, it’s the perfect tool for full-stack developers who want to keep things simple.

Here's the video tutorial:

What We’ll Build

We’ll create a fully functional Todo App that:

  • Lists all your tasks dynamically.
  • Lets you add, edit, and delete tasks without reloading the page.
  • Uses HTMX to send requests and update the UI seamlessly.
  • Relies on FastAPI for the backend logic and templating.

By the end of this tutorial, you’ll have a powerful understanding of how these tools work together.


Setting Up Your Project

1. Install Dependencies

First things first, let’s get the required libraries:

pip install fastapi uvicorn jinja2
npm install htmx.org@2.0.4
Enter fullscreen mode Exit fullscreen mode

2. Initialize Your FastAPI App

Create a new Python file (main.py) and set up a basic FastAPI application:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
async def home():
    return "<h1>Welcome to your Todo App</h1>"
Enter fullscreen mode Exit fullscreen mode

Run your app with:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

OR

fastapi dev main.py
Enter fullscreen mode Exit fullscreen mode

Open your browser and visit http://127.0.0.1:8000. Voila, your FastAPI app is live!


Adding Templates with Jinja2

Server-rendered HTML is essential for our app. Let’s configure Jinja2:

Set Up Templates Directory

Create a templates/ folder and add an index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Todo App</title>
</head>
<body>
    <h1>Todo List</h1>
    <ul id="todo-list">
        <!-- Todos will go here -->
    </ul>
    <form id="add-todo-form">
        <input type="text" name="todo" placeholder="Add a new todo">
        <button type="submit">Add</button>
    </form>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Update your main.py to use Jinja2:

from fastapi.templating import Jinja2Templates
from fastapi.requests import Request

templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})
Enter fullscreen mode Exit fullscreen mode

Making It Interactive with HTMX

HTMX makes adding interactivity super easy. Let’s enhance our app.

Adding a Todo

In your index.html, update the form to use HTMX:

<form id="add-todo-form" hx-post="/todos" hx-swap="beforeend">
    <input type="text" name="todo" placeholder="Add a new todo">
    <button type="submit">Add</button>
</form>
Enter fullscreen mode Exit fullscreen mode

In main.py, add the /todos endpoint:

from fastapi import Form
from uuid import uuid4

class Todo:
    def __init__(self, text):
        self.id = uuid4()
        self.text = text
        self.done = False

@app.post("/todos", response_class=HTMLResponse)
async def add_todo(todo: str = Form(...)):
    new_todo = Todo(todo)
    todos.append(new_todo)
    return f"<li>{new_todo.text}</li>"
Enter fullscreen mode Exit fullscreen mode

Listing Todos

Add a route to render the list of todos:

@app.get("/todos", response_class=HTMLResponse)
async def list_todos():
    return "".join(f"<li>{todo.text}</li>" for todo in todos)
Enter fullscreen mode Exit fullscreen mode

Update your index.html:

<ul id="todo-list" hx-get="/todos" hx-trigger="load">
    <!-- Todos will load here -->
</ul>
Enter fullscreen mode Exit fullscreen mode

Wrapping Up 🙃

That’s it! We’ve built a fully interactive Todo App using FastAPI and HTMX. You’ve learned how to:

  1. Set up a FastAPI project.
  2. Use Jinja2 templates for server-rendered HTML.
  3. Leverage HTMX for dynamic interactivity without writing JavaScript.

If you enjoyed this tutorial, share it with your friends / fellow devs/ neighbors or anyone else 😆!
🎄 Merry Christmas and HAppy New Year 2025 🎄 everyone

Top comments (0)