FastHTML and Heroku
Learn how to build and deploy a FastHTML app in just a few minutes. Gain knowledge of FastHTML and deploying to Heroku in this tutorial.
When creating a new app or service, what begins as learning just one new tool can quickly turn into needing a whole set of tools and frameworks. For Python devs, jumping into HTML, CSS, and JavaScript to build a usable app can be daunting. For web devs, many Python-first backend tools work in JavaScript but are often outdated. You’re left with a choice: Stick with JavaScript or switch to Python for access to the latest features.
FastHTML bridges the gap between these two groups. For Python devs, it makes creating a web app straightforward—no JavaScript required! For web devs, it makes creating a Python app quick and easy, with the option to extend using JavaScript—you’re not locked in.
As a web developer, I’m always looking for ways to make Python dev more accessible. So, let’s see how quickly we can build and deploy a FastHTML app. I’ll follow the image generation tutorial and then deploy it to Heroku. Let’s go!
Intro to FastHTML
Never heard of FastHTML before? Here’s how FastHTML describes itself:
FastHTML is a new next-generation web framework for fast, scalable web applications with minimal, compact code. It’s designed to be:
Powerful and expressive enough to build the most advanced, interactive web apps you can imagine.
Fast and lightweight, so you can write less code and get more done.
Easy to learn and use, with a simple, intuitive syntax that makes it easy to build complex apps quickly.
FastHTML promises to enable you to generate usable, lightweight apps quickly. Too many web apps are bloated and heavy, requiring a lot of processing and bandwidth for simple tasks. Most web apps just need something simple, beautiful, and easy to use. FastHTML aims to make that task easy.
You may have heard of FastAPI, designed to make creating APIs with Python a breeze. FastHTML is inspired by FastAPI’s philosophy, seeking to do the same for frontend applications.
Opinionated about simplicity and ease of use
Part of the FastHTML vision is to “make it the easiest way to create quick prototypes, and also the easiest way to create scalable, powerful, rich applications.” As a developer tool, FastHTML seems to be opinionated about the right things—simplicity and ease of use without limiting you in the future.
FastHTML gets you up and running quickly while also making it easy for your users. It does this by selecting key core technologies such as ASGI and HTMX. The foundations page from FastHTML introduces these technologies and gives the basics (though you don’t need to know about these to get started).
Get Up and Running Quickly
The tutorials from FastHTML offer several examples of different apps, each with its own use case. I was curious about the Image Generation App tutorial and wanted to see how quickly I could get a text-to-image model into a real, working app. The verdict? It was fast. Really fast.
In less than 60 lines of code, I created a fully functioning web app where a user can type in a prompt and receive an image from the free Pollinations text-to-image model.
Here’s a short demo of the tutorial app:
In this tutorial app, I got a brief glimpse of the power of FastHTML. I learned how to:
Submit data through a form
Interact with external APIs
Display some loading text while waiting
What’s impressive is that it only took one tiny Python file to complete this, and the final app is lightweight and looks good. Here’s the file I ended up with:
from fastcore.parallel import threaded
from fasthtml.common import *
import os, uvicorn, requests, replicate
from PIL import Image
app = FastHTML(hdrs=(picolink,))
# Store our generations
generations = []
folder = f"gens/"
os.makedirs(folder, exist_ok=True)
# Main page
@app.get("/")
def home():
inp = Input(id="new-prompt", name="prompt", placeholder="Enter a prompt")
add = Form(Group(inp, Button("Generate")), hx_post="/", target_id='gen-list', hx_swap="afterbegin")
gen_list = Div(id='gen-list')
return Title('Image Generation Demo'), Main(H1('Magic Image Generation'), add, gen_list, cls='container')
# A pending preview keeps polling this route until we return the image preview
def generation_preview(id):
if os.path.exists(f"gens/{id}.png"):
return Div(Img(src=f"/gens/{id}.png"), id=f'gen-{id}')
else:
return Div("Generating...", id=f'gen-{id}',
hx_post=f"/generations/{id}",
hx_trigger='every 1s', hx_swap='outerHTML')
@app.post("/generations/{id}")
def get(id:int): return generation_preview(id)
# For images, CSS, etc.
@app.get("/{fname:path}.{ext:static}")
def static(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')
# Generation route
@app.post("/")
def post(prompt:str):
id = len(generations)
generate_and_save(prompt, id)
generations.append(prompt)
clear_input = Input(id="new-prompt", name="prompt", placeholder="Enter a prompt", hx_swap_oob='true')
return generation_preview(id), clear_input
# URL (for image generation)
def get_url(prompt):
return f"https://image.pollinations.ai/prompt/{prompt.replace(' ', '%20')}?model=flux&width=1024&height=1024&seed=42&nologo=true&enhance=true"
@threaded
def generate_and_save(prompt, id):
full_url = get_url(prompt)
Image.open(requests.get(full_url, stream=True).raw).save(f"{folder}/{id}.png")
return True
if __name__ == '__main__': uvicorn.run("app:app", host='0.0.0.0', port=int(os.getenv("PORT", default=5000)))
Looking for more functionality? The tutorial continues, adding some CSS styling, user sessions, and even payment tracking with Stripe. While I didn’t go through it all the way, the potential is clear: lots of functionality and usability without a lot of boilerplate or using both Python and JavaScript.
Deploy Quickly to Heroku
Okay, so now that I have a pure Python app running locally, what do I need to do to deploy it? Heroku makes this easy. I added a single file called Procfile
with just one line in it:
web: python app.py
This simple text file tells Heroku how to run the app. With the Procfile in place, I can use the Heroku CLI to create and deploy my app. And it’s fast…from zero to done in less than 45 seconds.
With two commands, I created my project, built it, and deployed it to Heroku. And let’s just do a quick check. Did it actually work?
And it’s up for the world to see!
Conclusion
When I find a new tool that makes it easier and quicker to build an app, my mind starts spinning with the possibilities. If it’s that easy, then maybe next time I need to spin up something, I can do it this way and integrate it with this tool, and that other thing… So much of programming is assembling the right tools for the job.
FastHTML has opened the door to a whole set of Python-based applications for me, and Heroku makes it easy to get those apps off my local machine and into the world. That said, several of the foundations of FastHTML are new to me, and I look forward to understanding them more deeply as I use it more.
I hope you have fun with FastHTML and Heroku! Happy coding!