""

FastHTML nella pratica: quando passare a Django

Quando usare FastHTML e quando passare a Django

A maggio 2026 il nostro CTO e Solution Architect, Iacopo Spalletti, ha portato a PyCon Italia, a Bologna, un talk su FastHTML. Questo articolo ne riprende i contenuti e racconta come il framework ha retto alla prova di un nostro progetto, e dove si è fermato. Per vedere il codice più da vicino, dalle query di MiniDataAPI allo streaming delle risposte di Claude, qui sotto ci sono il video, le slide e l’app di esempio.

Talk: FastHTML, applicazioni HTML in modo Pythonico
Speaker: Iacopo Spalletti, CTO and Solution Architect di Nephila
Evento: PyCon Italia 2026, Bologna, 28 maggio 2026
Lingua: Italiano, slide in inglese
Video: Guarda su YouTube
Slide: Speaker Deck
App di esempio: yakky/FastHTML-example, una chat con Claude realizzata con FastHTML

Il nostro strumento interno e i limiti di FastHTML

In Nephila ci serviva un piccolo strumento interno per gestire token e credenziali dei nostri progetti. Fino ad allora li tenevamo su Bitwarden e volevamo un modo più strutturato di gestirli. Il modello dati era semplice e lineare, quindi un buon banco di prova per sperimentare FastHTML su un progetto reale.

La prima iterazione è andata bene. MiniDataAPI, il piccolo strato di astrazione del database incluso in FastHTML, copriva tutto quello che ci serviva, l'autenticazione era gestita da un gateway esterno e il codice era molto semplice. Eravamo contenti.

Sono state le ultime parole famose. Come sempre, i requisiti sono cresciuti: dovevamo supportare più casi d'uso, il modello dati è diventato decisamente più complesso e ci serviva un sistema di permessi. A quel punto eravamo oltre i limiti di MiniDataAPI. Il passo successivo sarebbe stato SQLAlchemy, e lì ci siamo chiesti perché complicarci la vita, visto che Django ha già tutto quello che ci serve. Così siamo passati a Django, senza pentirci di essere partiti con FastHTML.

La lezione che ne ho tratto è la stessa con cui ho chiuso il talk. Con un modello semplice, FastHTML è la scelta giusta. Con un modello complesso e i permessi, non lo è più. Il punto è capire in quale dei due casi ci si trova prima di essere a metà progetto.

Prima di scegliere un framework conviene conoscere bene il proprio modello dati, ed essere pronti a cambiare approccio quando cresce.

Due linguaggi per una sola applicazione

Chi sviluppa in Python sa scrivere codice elegante e ben testato, poi succede che all'applicazione serva un pulsante che faccia qualcosa, anche di banale, e il frontend finisce per diventare il problema di qualcun altro.

Sul backend la scelta è nota: Django, Flask o FastAPI, a seconda di cosa serve al progetto. Quando il frontend richiede molta interattività, l'elenco delle tecnologie si allunga: template Jinja o Django, API scritte con DRF o FastAPI, un framework JavaScript come React o Vue, npm per i pacchetti, Vite per la build e di solito qualche altro strumento ancora. Sembra tutto giustificato, e in effetti tutto ha senso. Però, per l'applicazione che si ha davanti, spesso è decisamente troppo. Il conto, per una singola applicazione, è di due linguaggi, due modelli mentali, due ecosistemi e due sistemi di dipendenze.

È così che oggi si costruisce gran parte del web, e la maggior parte di chi programma in Python l'ha accettato come il prezzo da pagare. Lo schema dominante è semplice da descrivere: il backend restituisce JSON, JavaScript compone l'interfaccia e l'unico contratto che tiene insieme le due parti è la descrizione delle API.

Il contratto ha anche un costo che nel codice non si vede. Spesso backend e frontend sono in mano a persone diverse, che devono mantenere allineato il proprio lavoro. In un'applicazione grande, lo sforzo si ripaga. In uno strumento interno, una dashboard o un prototipo, è difficile da giustificare.

Ma la domanda, a questo punto, è una: tutto questo è sempre necessario?

Il web aveva ragione fin dall'inizio

Il modello originale del web risale al 1991: HTTP e HTML, con il server che genera l'HTML e il browser che lo renderizza. Ha funzionato per più di trent'anni. I browser sono molto bravi a renderizzare HTML, e HTTP è un protocollo abbastanza flessibile da reggere interazioni molto più complesse di una pagina di link. Questo modello ha preso il nome di hypermedia.

Il web si è allontanato da quel modello perché servivano interazioni più ricche, e per molto tempo JavaScript scritto e gestito a mano è stato l'unico modo per costruirle. Oggi non è più così.

Nell'approccio basato su API il backend restituisce JSON e JavaScript lo trasforma in interfaccia. Nell'approccio hypermedia il backend restituisce HTML e il browser lo renderizza, senza fare altro.

""

La differenza che conta di più riguarda lo stato. Con l'approccio hypermedia non c'è uno stato lato client da gestire, da sempre fonte di mal di testa, e il server resta la fonte di verità. Un esempio è l'app che ho costruito per il talk, una chat con Claude. Quando si cancella la conversazione, il server rimanda una pagina vuota e il browser la mostra. Nessun codice nel browser deve capire cosa è cambiato.

A rendere tutto questo praticabile oggi è HTMX, il pezzo che mancava. È una libreria JavaScript di circa 14 KB che non ha bisogno né di build né di framework. Con HTMX qualunque elemento del DOM può inviare una richiesta su qualunque evento, con qualunque metodo HTTP, e la risposta sostituisce solo la parte della pagina che deve cambiare.

Per chi sviluppa, il cambiamento si vede nella domanda che ci si pone mentre si lavora:

Come gestisco lo stato lato client?

Quale HTML deve restituire il server?

Per approfondire la teoria, il libro che ho citato nel talk è Hypermedia Systems, di Carson Gross, Adam Stepinski e Deniz Akşimşek. FastHTML nasce proprio da questa idea.

Dentro FastHTML

FastHTML prende questa idea e ci costruisce intorno un framework Python. Answer.AI l'ha rilasciato nel 2024, quindi è ancora giovane. È basato su Starlette, usa HTMX come elemento strutturale e porta nelle applicazioni hypermedia l'approccio di FastAPI, da cui prende ispirazione. È nativamente asincrono, e la libreria è di circa mille righe di codice, con poche astrazioni: si comincia a usarla molto in fretta.

L'idea di fondo sta in una riga. Ogni tag HTML è una funzione Python:

Div(P("Hello"))

che produce:

<div><p>Hello</p></div>

I route handler (le view, per chi conosce Django) restituiscono direttamente l'interfaccia. Niente template e niente cambi di contesto tra Python, HTML e JavaScript mentre si lavora.

Questa è l'app minima dell'esempio che ho costruito per il talk, la chat con Claude:

from fasthtml.common import (
    H1, Button, Div, Form,
    Link, Style, Textarea,
    fast_app, serve,
)

app, rt = fast_app(
    hdrs=[
        Link(rel="stylesheet", href=PICO_CDN),
        Style(OVERRIDES),
    ]
)

@rt("/")
def get():
    return _page(history)

serve(port=5001)


Si importano i tag che servono, si crea l'applicazione con fast_app (qui caricando un CSS negli header), si dichiara una route con il decoratore @rt, in stile FastAPI, e si avvia il server con serve().

Gli esempi di codice di questo articolo sono estratti dall'app di esempio. Nomi come _page, history ePICO_CDN sono definiti altrove, e il codice completo è nel repository.

L'interattività arriva dagli attributi HTMX, passati come argomenti keyword. Questo è il pulsante che cancella la conversazione, con la route che lo gestisce:

Button(
    "Clear conversation",
    hx_post="/clear",
    hx_target="body",
    hx_swap="outerHTML",
)

@rt("/clear", methods=["POST"])
def post_clear():
    history.clear()
    return _page(history)

hx_post indica quale endpoint chiamare al clic, hx_target quale elemento sostituire con la risposta e hx_swap come sostituirlo. La route /clear svuota la cronologia della chat e restituisce la pagina. Qui viene sostituito tutto il body, mentre in altre applicazioni si può sostituire solo un frammento. Si fa clic, parte una richiesta al server, il backend restituisce un frammento HTML e il DOM si aggiorna: l'intero ciclo di elaborazione resta in Python.

FastHTML fornisce inoltre gli elementi essenziali per partire: sessioni e autenticazione, MiniDataAPI come piccolo strato di astrazione del database e PicoCSS per gli stili di base, anche se si può caricare qualunque framework CSS. C'è poi un supporto di base per WebSocket e Server-Sent Events, per funzionalità in tempo reale che vanno oltre quello che HTMX fa da solo.

Nel loro insieme, questi elementi permettono a una sola persona di realizzare un tool interno completo, senza build per il frontend e senza una seconda codebase. Quello che FastHTML non include conta altrettanto, ed è lì che il nostro strumento interno ha trovato i suoi limiti.

Quando FastHTML smette di essere la scelta giusta

I limiti di FastHTML si vedono in anticipo, e quasi tutti sono voluti. Il primo riguarda i dati. MiniDataAPI usa SQLite di default e la lettura dei dati funziona così:

todos(where="done=0")                              # filtered
todos(order_by='created_at')                       # sorted
todos(limit=10, offset=20)                         # paginated
todos(where="done=0", order_by='task', limit=5)    # combined

Il vocabolario è più o meno tutto qui. Ci sono la ricerca per chiave primaria, un filtro persistente per i dati di ciascun utente e i soliti insert, upsert e delete. Join e annotazioni non ci sono. Per un modello con una o due tabelle è più che sufficiente. Con quindici tabelle e molte join, il lavoro finisce per farlo il codice Python e si perde buona parte di quello che rendeva interessante FastHTML. Si può usare SQLAlchemy, e se lo si conosce bene funziona, ma a quel punto si è sostituito un pezzo importante del framework.

Quando abbiamo dovuto estendere il nostro strumento interno oltre i requisiti iniziali, ci siamo scontrati abbastanza presto con quel limite:

  • il modello dati era cresciuto ben oltre un paio di tabelle

  • ci serviva un sistema di permessi

  • MiniDataAPI non bastava più, e il passo successivo era SQLAlchemy

Quello che è rimasto fuori fin dall’inizio. Non c'è un admin, e chi arriva da Django se ne accorge dal primo giorno: in uno strumento interno, ogni schermata che Django genera in automatico va costruita da zero. Non c'è un ORM vero e proprio e non ci sono le migrazioni, quindi ogni modifica allo schema va gestita a mano. Mancano infine il fingerprinting degli asset e il cache busting: tocca a chi sviluppa assicurarsi che i browser carichino la nuova versione del CSS.

Resta la questione dell'ecosistema, ancora piccolo. Il progetto è giovane, ci sono meno plugin e meno risposte su Stack Overflow, e quando ci si blocca serve più tempo per trovare qualcuno che ha avuto lo stesso problema.

C'è una cosa che ancora non so dire: come si comporta FastHTML su larga scala. Con decine di pagine e componenti complessi, la leggibilità dipende da quanto bene si strutturano i componenti, e su un progetto di quelle dimensioni non l'ho ancora provato.

Tra Streamlit e Django

Nel talk ho collocato FastHTML nello spazio tra due strumenti ben noti a chi programma in Python. Come FastHTML, Streamlit permette di costruire applicazioni in fretta ed esclusivamente in Python, ma impone un modo preciso di lavorare: quello che si costruisce è un'applicazione Streamlit, dentro il modello di Streamlit. Django ha molti punti di forza, e padroneggiarlo richiede un impegno serio. FastHTML si trova nel mezzo.

""

In concreto, questi sono i criteri che seguirei:

  • Per uno strumento interno, una dashboard o un prototipo con un modello dati semplice, sviluppato da una o due persone, FastHTML è una buona scelta.

  • Se il modello dati è complesso, richiede un sistema di permessi o è chiaramente destinato a crescere, conviene partire da Django. Il nostro strumento interno, del resto, è finito proprio lì.

  • Chi conosce FastAPI troverà FastHTML familiare fin dalla prima route.

  • Se i dati complessi stanno già in un altro backend, FastHTML può comunque fare da interfaccia sopra quei dati. Non pretende di essere il proprietario del database: basta una dataclass che mappi la tabella esistente.

Così piccolo da entrare nel contesto di un'AI

Le dimensioni ridotte che rendono l'ecosistema ancora scarno hanno però un lato positivo. Una libreria di circa mille righe, con poche astrazioni, entra facilmente nel contesto di un modello di AI: il modello ha meno da indovinare e meno occasioni di sbagliare.

L'ho sperimentato di persona. L'app di esempio del talk è stata creata con un solo prompt, o quasi, e al primo avvio ha funzionato esattamente come doveva. Il codice generato conteneva pochissime allucinazioni. Con un buon prompt, per una dashboard, un admin semplice o un chatbot di base possono bastare pochi minuti.

Questa affermazione va presa nella giusta misura. Vale per le applicazioni piccole, cioè proprio quelle per cui FastHTML è pensato. Detto questo, per uno strumento interno che una sola persona deve realizzare in fretta, costruirlo quasi interamente con l'AI è una strada percorribile, e la considero un punto a favore di FastHTML. Chi vuole vedere il risultato e giudicare da solo trova il codice nel repository di esempio.

***

Risorse

Per saperne di più sui temi di questo articolo:

Questo articolo si concentra su uno dei temi affrontati nel talk, ovvero la scelta di FastHTML come framework. Per vedere nel dettaglio le parti più tecniche, ecco l’indice dei capitoli del video:

Per approfondire gli argomenti del talk, il punto di partenza migliore è l'app di esempio che Iacopo ha costruito per l'occasione: yakky/FastHTML-example. Per provare FastHTML partendo da zero, invece, conviene iniziare dalla documentazione ufficiale.

Chi ha trovato utili queste risorse o vuole condividere la propria opinione su FastHTML ci trova sulla nostra pagina LinkedIn: scambiare idee e confrontare punti di vista è la parte che ci piace di più.

***

Abbiamo scritto questo articolo con il supporto dell'AI. Se vuoi saperne di più su come usiamo questi strumenti per la scrittura, ecco la spiegazione.