audrey.feldroy.com

The experimental notebooks of Audrey M. Roy Greenfeld. This website and all its notebooks are open-source at github.com/audreyfeldroy/audrey.feldroy.com


# HTML Title Tag in FastHTML

by Audrey M. Roy Greenfeld | Wed, Jan 8, 2025

I get so lazy about title tags. The point of today's notebook is to make me less lazy, so I actually fix the title of this site. Oh, and to explore `Title` and `Titled` in FastHTML.


from fasthtml.common import *
from fasthtml.jupyter import *
from nb2fasthtml.core import render_nb

To create a <title> tag, you use the Title FT:

Title("Hey")

<title>Hey</title>



title(('Hey',),{})

Titled is a shortcut to give you <title> and <h1> containing the same thing, with the <h1> wrapped in a main container:

to_xml(Titled("Hey this is Titled"))
'Hey this is Titled\n

Hey this is Titled

\n
'

It doesn't make much sense until you add a list of FTs to Titled:

print(to_xml(Titled("Hey this is Titled", P("Here's some page content in a paragraph."), 
             Div(P("And here's a paragraph in a div.")),
             Ol(Li("You can add stuff here"), Li("And more stuff"), Li("And the page can go on and on.")))))

FastHTML App With Titled Pages

Here's a FastHTML app I'm running from this notebook.

app,rt = fast_app(pico=False)
@rt
def index():
    return Titled("My Homepage",
        Div(P("This page has a title and h1.")),
        P("Here's some page content in a paragraph."), 
        Div(P("And here's a paragraph in a div.")),
        Ol(Li("You can add stuff here"), Li("And more stuff"), Li("The page can go on and on.")))
server = JupyUvi(app)
@rt('/pages/{title}')
def page(title:str):
    return Titled(title,
        Div(P("The title and h1 match the title page URL slug")))
server.stop()

Titles From nb2fasthtml

name = "2025-01-07-Verifying-Bluesky-Domain-in-FastHTML"
css = ':root {font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;} p {line-height: 1.5;}'
nb = Path(f'{name}.ipynb')

render_nb from the nb2fasthtml package already turns the notebook's title into an h1, but it appears it doesn't add a title tag.

To get around that, I have my notebook route handler return:

r = (Title(name[11:].replace('-', ' ').replace('_', ' ')),
Style(css),
render_nb(nb, wrapper=Div))
r
(title(('Verifying Bluesky Domain in FastHTML',),{}), style((':root {font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;} p {line-height: 1.5;}',),{}), div((div((h1(('Verifying a Bluesky Domain Handle on a FastHTML Site',),{}),),{'class': 'frontmatter'}), div((div(('I just changed my Bluesky to [@audrey.feldroy.com](https://bsky.app/profile/audrey.feldroy.com). To verify my domain ownership, I added this route handler to my FastHTML website:',),{'class': 'marked'}),),{}), article((div(("\n```python\n@rt('/.well-known/{fname}')\ndef wellknown(fname: str):\n return Path(f'.well-known/{fname}').read_text()\n```\n",),{'class': 'marked'}), ''),{}), div((div(("In my website's repo [arg-blog-fasthtml](https://github.com/audreyfeldroy/arg-blog-fasthtml) I created a `.well-known` directory.",),{'class': 'marked'}),),{}), div((div(('Within that, I created a file called `atproto-did`, containing the verification text string shown under:\n\n> https://bsky.app/settings/account > Change Handle > I have my own domain > No DNS Panel',),{'class': 'marked'}),),{}), div((div(('Then I redeployed my site and clicked the *Verify Text File* button, and my handle was updated.',),{'class': 'marked'}),),{}), div((div(('## A Full Minimal FastHTML App for This',),{'class': 'marked'}),),{}), div((div(("If you have a domain but no website yet, here's a FastHTML app for verifying your domain as your Bluesky handle:",),{'class': 'marked'}),),{}), article((div(('\n```python\nfrom fasthtml.common import *\n\napp,rt = fast_app()\n\n@rt\ndef get(): return Div(P("Welcome to my homepage!"))\n\n@rt(\'/.well-known/{fname}\')\ndef wellknown(fname: str):\n return Path(f\'.well-known/{fname}\').read_text()\n\nserve()\n```\n',),{'class': 'marked'}), ''),{}), article((div(('\n```python\nThen add the `.well-known/atproto-did` file, deploy, and verify.\n```\n',),{'class': 'marked'}), ''),{})),{'class': 'container'}))

Which we can see generally more readably with:

print(to_xml(r))

© 2024-2025 Audrey M. Roy Greenfeld