# FastHTML By Example This tutorial provides an alternate introduction to FastHTML by building out example applications. We also illustrate how to use FastHTML foundations to create custom web apps. Finally, this document serves as minimal context for a LLM to turn it into a FastHTML assistant. Let’s get started. ## FastHTML Basics FastHTML is *just Python*. You can install it with `pip install python-fasthtml`. Extensions/components built for it can likewise be distributed via PyPI or as simple Python files. The core usage of FastHTML is to define routes, and then to define what to do at each route. This is similar to the [FastAPI](https://fastapi.tiangolo.com/) web framework (in fact we implemented much of the functionality to match the FastAPI usage examples), but where FastAPI focuses on returning JSON data to build APIs, FastHTML focuses on returning HTML data. Here’s a simple FastHTML app that returns a “Hello, World” message: ``` python from fasthtml.common import FastHTML, serve app = FastHTML() @app.get("/") def home(): return "

Hello, World

" serve() ``` To run this app, place it in a file, say `app.py`, and then run it with `python app.py`. INFO: Will watch for changes in these directories: ['/home/jonathan/fasthtml-example'] INFO: Uvicorn running on http://127.0.0.1:5001 (Press CTRL+C to quit) INFO: Started reloader process [871942] using WatchFiles INFO: Started server process [871945] INFO: Waiting for application startup. INFO: Application startup complete. If you navigate to in a browser, you’ll see your “Hello, World”. If you edit the `app.py` file and save it, the server will reload and you’ll see the updated message when you refresh the page in your browser. ## Constructing HTML Notice we wrote some HTML in the previous example. We don’t want to do that! Some web frameworks require that you learn HTML, CSS, JavaScript AND some templating language AND python. We want to do as much as possible with just one language. Fortunately, the Python module [fastcore.xml](https://fastcore.fast.ai/xml.html) has all we need for constructing HTML from Python, and FastHTML includes all the tags you need to get started. For example: ``` python from fasthtml.common import * page = Html( Head(Title('Some page')), Body(Div('Some text, ', A('A link', href='https://example.com'), Img(src="https://placehold.co/200"), cls='myclass'))) print(to_xml(page)) ``` Some page
Some text, A link
``` python show(page) ``` Some page
Some text, A link
If that `import *` worries you, you can always import only the tags you need. FastHTML is smart enough to know about fastcore.xml, and so you don’t need to use the `to_xml` function to convert your FT objects to HTML. You can just return them as you would any other Python object. For example, if we modify our previous example to use fastcore.xml, we can return an FT object directly: ``` python from fasthtml.common import * app = FastHTML() @app.get("/") def home(): page = Html( Head(Title('Some page')), Body(Div('Some text, ', A('A link', href='https://example.com'), Img(src="https://placehold.co/200"), cls='myclass'))) return page serve() ``` This will render the HTML in the browser. For debugging, you can right-click on the rendered HTML in the browser and select “Inspect” to see the underlying HTML that was generated. There you’ll also find the ‘network’ tab, which shows you the requests that were made to render the page. Refresh and look for the request to `127.0.0.1` - and you’ll see it’s just a `GET` request to `/`, and the response body is the HTML you just returned.
> **Live Reloading** > > You can also enable [live reloading](../ref/live_reload.ipynb) so you > don’t have to manually refresh your browser to view updates.
You can also use Starlette’s `TestClient` to try it out in a notebook: ``` python from starlette.testclient import TestClient client = TestClient(app) r = client.get("/") print(r.text) ``` Some page
Some text, A link
FastHTML wraps things in an Html tag if you don’t do it yourself (unless the request comes from htmx, in which case you get the element directly). See [FT objects and HTML](#ft-objects-and-html) for more on creating custom components or adding HTML rendering to existing Python objects. To give the page a non-default title, return a Title before your main content: ``` python app = FastHTML() @app.get("/") def home(): return Title("Page Demo"), Div(H1('Hello, World'), P('Some text'), P('Some more text')) client = TestClient(app) print(client.get("/").text) ``` Page Demo

Hello, World

Some text

Some more text

We’ll use this pattern often in the examples to follow. ## Defining Routes The HTTP protocol defines a number of methods (‘verbs’) to send requests to a server. The most common are GET, POST, PUT, DELETE, and HEAD. We saw ‘GET’ in action before - when you navigate to a URL, you’re making a GET request to that URL. We can do different things on a route for different HTTP methods. For example: ``` python @app.route("/", methods='get') def home(): return H1('Hello, World') @app.route("/", methods=['post', 'put']) def post_or_put(): return "got a POST or PUT request" ``` This says that when someone navigates to the root URL “/” (i.e. sends a GET request), they will see the big “Hello, World” heading. When someone submits a POST or PUT request to the same URL, the server should return the string “got a post or put request”.
> **Test the POST request** > > You can test the POST request with > `curl -X POST http://127.0.0.1:8000 -d "some data"`. This sends some > data to the server, you should see the response “got a post or put > request” printed in the terminal.
There are a few other ways you can specify the route+method - FastHTML has `.get`, `.post`, etc. as shorthand for `route(..., methods=['get'])`, etc. ``` python @app.get("/") def my_function(): return "Hello World from a GET request" ``` Or you can use the `@rt` decorator without a method but specify the method with the name of the function. For example: ``` python rt = app.route @rt("/") def post(): return "Hello World from a POST request" ``` ``` python client.post("/").text ``` 'Hello World from a POST request' You’re welcome to pick whichever style you prefer. Using routes lets you show different content on different pages - ‘/home’, ‘/about’ and so on. You can also respond differently to different kinds of requests to the same route, as shown above. You can also pass data via the route:
## `@app.get` ``` python @app.get("/greet/{nm}") def greet(nm:str): return f"Good day to you, {nm}!" client.get("/greet/Dave").text ``` 'Good day to you, Dave!' ## `@rt` ``` python @rt("/greet/{nm}") def get(nm:str): return f"Good day to you, {nm}!" client.get("/greet/Dave").text ``` 'Good day to you, Dave!'
More on this in the [More on Routing and Request Parameters](#more-on-routing-and-request-parameters) section, which goes deeper into the different ways to get information from a request. ## Styling Basics Plain HTML probably isn’t quite what you imagine when you visualize your beautiful web app. CSS is the go-to language for styling HTML. But again, we don’t want to learn extra languages unless we absolutely have to! Fortunately, there are ways to get much more visually appealing sites by relying on the hard work of others, using existing CSS libraries. One of our favourites is [PicoCSS](https://picocss.com/). A common way to add CSS files to web pages is to use a [``](https://www.w3schools.com/tags/tag_link.asp) tag inside your [HTML header](https://www.w3schools.com/tags/tag_header.asp), like this: ``` html
...
``` For convenience, FastHTML already defines a Pico component for you with `picolink`: ``` python print(to_xml(picolink)) ```
> **Note** > > `picolink` also includes a `