Understanding FastHTML Headers
FastHTML provides default headers for every page, which are also fully customizable. This notebook explores how this works.
```python
from fasthtml.common import *
from fasthtml.jupyter import *
from IPython.display import display,HTML
```
In [00_core](https://github.com/AnswerDotAI/fasthtml/blob/main/nbs/api/00_core.ipynb) there is a `def_hdrs` function that returns the "default headers for a FastHTML app":
```python
def_hdrs()
```
JS files for these libraries are included by default:
* [HTMX](https://htmx.org/): For interactivity! Allows any HTML attribute to have actions, handlers, etc. providing dynamic behavior via DOM element AJAX swaps and modification
* [fasthtml-js](https://github.com/answerdotai/fasthtml-js): JS for FastHTML apps
* [Surreal](https://github.com/gnat/surreal): Inline Locality of Behavior (LoB) for JS + tiny jQuery alternative
* [CSS Scope Inline](https://github.com/gnat/css-scope-inline/): Inline LoB for CSS
In that same 00_core notebook each of the default script headers above is defined with `Script`, like:
```python
htmxsrc = Script(src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js")
htmxsrc
```
The meta headers are defined with `Meta` like:
```python
viewport = Meta(name="viewport", content="width=device-width, initial-scale=1, viewport-fit=cover")
viewport
```
## In the FastHTML app instance
Later in 00_core when `FastHTML` is defined, these are the relevant lines related to headers:
```python
class FastHTML(Starlette):
def __init__(self, ..., hdrs=None)
...
if default_hdrs: hdrs = def_hdrs(htmx, surreal=surreal) + hdrs
hdrs += [Script(src=ext) for ext in exts.values()]
if IN_NOTEBOOK:
hdrs.append(iframe_scr)
from IPython.display import display,HTML
if nb_hdrs: display(HTML(to_xml(tuple(hdrs))))
middleware.append(cors_allow)
self.on_startup,self.on_shutdown,self.lifespan,self.hdrs,self.ftrs = on_startup,on_shutdown,lifespan,hdrs,ftrs
...
```
## In the app from `fast_app`
If you instantiate `FastHTML` via the [fast_app](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/fastapp.py#L29) convenience function, you can see those same default JS headers here, plus Pico and a `sendmsg` JS function:
```python
app,rt = fast_app()
```
```python
app.hdrs
```
One of the things I love about FastHTML is how it's designed to work in Jupyter notebooks, and built that way from its core.
The more I use notebooks, the more I realize they're key to writing simple code that you understand thorougly inside and out. That is what makes code truly maintainable and long-lasting.
```python
IN_NOTEBOOK
```
This is defined earlier in 00_core and appended to the in-notebook headers. Here iframes' height is auto-resized to their content, allowing FastHTML pages to be better shown in-notebook:
```python
iframe_scr
```
If you set `nb_hdrs=True`, the line `display(HTML(to_xml(tuple(app.hdrs))))` will add the headers to the notebook.
## Customizing Headers: Adding a JS File
Here we add a simple 1-file JS library, Tone.js:
```python
Script(src="http://unpkg.com/tone")
```
We see the Tone.js header was added after the default FastHTML headers:
```python
app,rt = fast_app(hdrs=(Script(src="https://unpkg.com/tone"),))
app.hdrs
```
This route handler has code from the Tone.js Hello World example:
```python
@rt
def index():
return Div(
Script('const synth = new Tone.Synth().toDestination();synth.triggerAttackRelease("C4", "8n");'),
P("This page should include the Tone.js header and play a tone")
)
```
```python
server = JupyUvi(app)
```
Uncomment and run this to play a tone:
```python
# HTMX()
```
That works because the Tone.js header file was added.
## Customizing Headers: Adding MonsterUI
MonsterUI has a great [tutorial app](https://monsterui.answer.ai/tutorial_app) showing how to customize FastHTML headers. It starts with adding the MonsterUI headers to `fast_app`.
```python
from monsterui.all import Theme, fast_app
hdrs = Theme.blue.headers()
```
MonsterUI's theme headers include FrankenUI, Tailwind CSS, and theming code:
```python
hdrs
```
MonsterUI is a little different because it comes with its own `fast_app` that extends the one from FastHTML's fastapp.py. That is defined in [monsterui/nbs/01_core.ipynb](https://github.com/AnswerDotAI/MonsterUI/blob/main/nbs/01_core.ipynb) and used here:
```python
app,rt = fast_app(hdrs=hdrs)
app.hdrs
```
The code in MonsterUI for this is:
```python
from fastcore.all import delegates
import fasthtml.common as fh
@delegates(fh.fast_app, but=['pico'])
def fast_app(*args, pico=False, **kwargs):
"Create a FastHTML or FastHTMLWithLiveReload app with `bg-background text-foreground` to bodykw for frankenui themes"
if 'bodykw' not in kwargs: kwargs['bodykw'] = {}
if 'class' not in kwargs['bodykw']: kwargs['bodykw']['class'] = ''
kwargs['bodykw']['class'] = stringify((kwargs['bodykw']['class'],'bg-background text-foreground'))
return fh.fast_app(*args, pico=pico, **kwargs)
```
```python
app.bodykw
```