Coder Social home page Coder Social logo

fasthtml's Introduction

FastHTML

Welcome to the official FastHTML documentation.

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 apps are just Python code, so you can use FastHTML with the full power of the Python language and ecosystem. FastHTML’s functionality maps 1:1 directly to HTML and HTTP, but allows them to be encapsulated using good software engineering practices—so you’ll need to understand these foundations to use this library fully. To understand how and why this works, please read this first: about.fastht.ml.

Installation

Since fasthtml is a Python library, you can install it with:

pip install python-fasthtml

In the near future, we hope to add component libraries that can likewise be installed via pip.

Usage

For a minimal app, create a file “main.py” as follows:

main.py

from fasthtml.common import *

app,rt = fast_app()

@rt('/')
def get(): return Div(P('Hello World!'), hx_get="/change")

serve()

Running the app with python main.py prints out a link to your running app: http://localhost:5001. Visit that link in your browser and you should see a page with the text “Hello World!”. Congratulations, you’ve just created your first FastHTML app!

Adding interactivity is surprisingly easy, thanks to HTMX. Modify the file to add this function:

main.py

@rt('/change')
def get(): return P('Nice to be here!')

You now have a page with a clickable element that changes the text when clicked. When clicking on this link, the server will respond with an “HTML partial”—that is, just a snippet of HTML which will be inserted into the existing page. In this case, the returned element will replace the original P element (since that’s the default behavior of HTMX) with the new version returned by the second route.

This “hypermedia-based” approach to web development is a powerful way to build web applications.

Next Steps

Start with the official sources to learn more about FastHTML:

  • About: Learn about the core ideas behind FastHTML
  • Documentation: Learn from examples how to write FastHTML code
  • Idiomatic app: Heavily commented source code walking through a complete application, including custom authentication, JS library connections, and database use.

We also have a 1-hour intro video:

https://www.youtube.com/embed/Auqrm7WFc0I

The capabilities of FastHTML are vast and growing, and not all the features and patterns have been documented yet. Be prepared to invest time into studying and modifying source code, such as the main FastHTML repo’s notebooks and the official FastHTML examples repo:

Then explore the small but growing third-party ecosystem of FastHTML tutorials, notebooks, libraries, and components:

Finally, join the FastHTML community to ask questions, share your work, and learn from others:

fasthtml's People

Contributors

jph00 avatar pydanny avatar johnowhitaker avatar ncoop57 avatar isaac-flath avatar audreyfeldroy avatar juanfrank77 avatar rian-dolphin avatar khoahyh avatar corneliusroemer avatar comhar avatar yym68686 avatar thomasjryan avatar sizhky avatar swyxio avatar dishb avatar willingc avatar drchrislevy avatar fabge avatar pcky avatar pcamp avatar cthorrez avatar blackary avatar yuan-manx avatar viddesh1 avatar siffogh avatar kafkasl avatar mathematicalmichael avatar mcroydon avatar matdmiller avatar

Stargazers

Juang, Yi-Lin avatar Pablo C. Ruíz  avatar  avatar Jason avatar  avatar null data avatar  avatar Pedro Gaspar Dorantes Ek avatar Giorgio Martini avatar mowi12 avatar Abdulrahman Tabaza avatar Fabio Jose avatar John Fredrik Dall avatar Damian  avatar  avatar  avatar  avatar David Lee avatar  avatar  avatar  avatar Gary Feng avatar Jeffery Lovely avatar Manuel Caballero avatar Shunyo Kawamoto avatar Tom Völker avatar netsuperman avatar Dominic Faulring avatar Pavel Stishenko avatar  avatar Matt Gorman avatar Saurabh  avatar Ayaka Kawabe avatar alg747 avatar DaneshJoy avatar linghugoogle avatar  avatar Peter Christiansen avatar Xenophon avatar Ray avatar Mamdouh avatar Nentropy avatar Gene Sobolev avatar Omar Nomad avatar dahai avatar Roger Lam avatar Anas Saeed avatar Ilya Korennoy avatar Mădălin Ionel Patrașcu avatar Sean Welch avatar  avatar  avatar Yoke Keong Wong avatar Aman Dalmia avatar  avatar Kak avatar Dip Dey avatar  avatar Alexander avatar Greg Bildson avatar Renato da Silva Moreno avatar Emerson Demetrio avatar Hugefiver avatar Daniel Thaagaard Andreasen avatar Philip Nuzhnyi avatar Yongsheng Li avatar Suman Das avatar Marcos Viana avatar  avatar Chris Vouga avatar  avatar Andrew Hinh avatar  avatar Adeel Ahmed Qureshi avatar {MomDontGo.Dev} avatar ✪ vi4m Marcin Kliks avatar  avatar Olivier Milla avatar Martín Villanueva avatar  avatar Masoud Kishani Farahani avatar Lucas A. Bonini avatar  avatar Ahmed Moubtahij avatar Peter avatar Soof Golan avatar 柴诗雨 avatar  avatar Sotnikov Arkadiy avatar Felix avatar Ming-Tung Hong avatar GitKraken avatar  avatar Antoine Jouanjean avatar Zang Yuchen avatar  avatar roadlabs avatar Victor Dumetz avatar Keaton Helm avatar  avatar

Watchers

Luca G. Soave avatar Abhik Khanra avatar  avatar Vik Paruchuri avatar cheng zhang avatar  avatar Nickolay Kolev avatar  avatar Tudor Beleuta avatar tiny Flutter team avatar Rognoni avatar Ruixiang Zhang avatar Kent Sandvik avatar  avatar Griffin Adams avatar Anatol Myshkin avatar Chee Guan avatar Steven Tsao avatar Benjamin Clavié avatar Chenyang avatar  avatar Lorraine David avatar  avatar Jesse C. Lin avatar Gautam Ethiraj avatar Abdullah ÖZCAN avatar  avatar Matt Shaffer avatar eduars avatar valar avatar  avatar Seun Omotosho avatar  avatar Michael Deufel avatar Alexis_Ott avatar  avatar Manuel Caballero avatar

fasthtml's Issues

Link pointing to the wrong URL

Hi there,

Amazing stuff as usual @jph00.

Kind of an insignificant issue, but I noticed that this link is pointing to the wrong place:

image

I think it should be http://fhdocs.answer.ai/.

Also, the fhdocs link leads to a HTTP version of the site:

image

If you visit the URL using https:// it works correctly, so I think you might just be missing something in the redirect from https://answerdotai.github.io/fasthtml/ or enforcing HTTPS in GitHub Pages.

Thank you for all the great work you make available to the community for free.

async script tag not handled properly due to special key word

The script tag can have the async keyword (e.g., <script async id="search-script" src="../assets/js/search.js"></script>, which is not currently handled by fasthtml nor the html2xt function since async is a special keyword in python causing a syntax error:

Script(async='', id='search-script', src='../assets/js/search.js')
           ^
SyntaxError: invalid syntax

I think we will need to specify a synonym for what we will use async for in python. Perhaps we could use capital Async? Script(Async='', id='search-script', src='../assets/js/search.js')

why not to use Notebook as a development environment?

Hello,

First of all, thank you very much for the new library. I really appreciate the effort to lower the barrier in web development.

When I assist DS/ML professionals in understanding how to use web frameworks, I find it most helpful when they can experiment within a notebook environment. This is what I anticipated from the FastHTML examples, given Jeremy's previous tools.

From my experience, the only issue is the need to redefine routers each time due to how Starlette handles them, but this can be addressed with the following code example as a decorator for routers (probably can be added to app.route in fasthtml):

def hot_realod(func):
    idx_to_del = [
        i for i, r in enumerate(app.routes) if func.__name__ == getattr(r, "name", id)
    ]
    for i, idx in enumerate(idx_to_del):
        del app.routes[idx - i]

    @wraps(func)
    async def inner_func(*args, **kwargs):
        return await func(*args, **kwargs)

    return inner_func

Here is an example: we start the app, create a router, change it (to simulate a cell re-run) and then export everything with nbdev (to run with uvicorn in prod)

image

Could you please explain why you chose not to develop in Jupyter?

Variable type routing error should throw 404 errors

Assuming this view:

from fasthtml.fastapp import * 

app = fast_app()

@app.get("/{name}/{age}")
def namer(name: str, age: int):
  return Titled(f"Hello {name.title()}, age {age}")

run_uv()

If I go to this URL: http://127.0.0.1:5001/uma/five, bcause the second named variable is a str instead of an int, it throws an error.

INFO:     127.0.0.1:52909 - "GET /Uma/asdsad HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 399, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 70, in __call__
    return await self.app(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/middleware/sessions.py", line 85, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/fasthtml/core.py", line 227, in _f
    wreq = await _wrap_req(req, params)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/fasthtml/core.py", line 167, in _wrap_req
    return [await _find_p(req, arg, p) for arg,p in params.items()]
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/fasthtml/core.py", line 167, in <listcomp>
    return [await _find_p(req, arg, p) for arg,p in params.items()]
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/fasthtml/core.py", line 164, in _find_p
    return [anno(o) for o in res] if isinstance(res,list) else anno(res)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/super-dashboard/lib/python3.10/site-packages/fasthtml/core.py", line 77, in str2int
    return 0 if not s else int(s)
ValueError: invalid literal for int() with base 10: 'asdsad'

If I'm not mistaken, this should be a 404 error, right?

WebSocket support when we don't have a form (potentially a Starlette issue, not fasthtml)

Context

I'm making an app that runs a background worker that pushes notifications to the client over a websocket connection. This means that the UI will be updated without the user doing anything at all (no form submission).

I have updated your example to showcase what I want to achieve: see here.

What are you expecting?

I'm expecting the code in the ws function to run in this example. Note that I don't have a form.

Are you sure it should run considering you don't have a form?

Here is a small script using FastAPI and htmx that proves that what I want is indeed supported outside Starlette/fasthtml. This example works as expected (server sends messages to the client without the client sending a message first).

What do you think is going on?

This is probably a Starlette issue happening here. I think the desired behavior is to not run the endpoint code before the form is submitted, but I don't have a form and this is a websocket connection, but it behaves like a HTTP one :) .

Let me know what you think and if it can be worked around/solved in fasthtml.

Thank You!

Thanks Jeremy, as always, you are creating something super useful that will make many people's lives easier.

core._find_p throws TypeError on non-subclassable arguments

Discovering running adv_app.py.

Steps to reproduce:

  1. Run uvicorn adv_app:app --reload
  2. Create two or more TODO items
  3. Reorder the items. The result is a stacktrace that looks like this:
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/uvicorn-0.30.1-py3.10.egg/uvicorn/protocols/http/httptools_impl.py", line 399, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/uvicorn-0.30.1-py3.10.egg/uvicorn/middleware/proxy_headers.py", line 70, in __call__
    return await self.app(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/middleware/sessions.py", line 85, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/daniel.roy.greenfeld/.virtualenvs/fasthtml/lib/python3.10/site-packages/starlette-0.37.2-py3.10.egg/starlette/routing.py", line 72, in app
    response = await func(request)
  File "/Users/daniel.roy.greenfeld/projects/fasthtml/fasthtml/core.py", line 232, in _f
    wreq = await _wrap_req(req, params)
  File "/Users/daniel.roy.greenfeld/projects/fasthtml/fasthtml/core.py", line 171, in _wrap_req
    return [await _find_p(req, arg, p) for arg,p in params.items()]
  File "/Users/daniel.roy.greenfeld/projects/fasthtml/fasthtml/core.py", line 171, in <listcomp>
    return [await _find_p(req, arg, p) for arg,p in params.items()]
  File "/Users/daniel.roy.greenfeld/projects/fasthtml/fasthtml/core.py", line 139, in _find_p
    if issubclass(anno, Request): return req
  File "/Users/daniel.roy.greenfeld/.pyenv/versions/3.10.6/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class

If I modify the offending to line to this:

try:
    if issubclass(anno, Request): return req
except TypeError: 
    raise TypeError(f"Parameter {p} has an '{anno}' which is not a subclass. Rather it is {type(anno)}")

Then we discover the type passed in is a list[int].

Parameter id: list[int] and 'list[int]' is not a subclass. Rather it is <class 'types.GenericAlias'>

This is because in adv_app the id argument of the [/reorder view] is a list[int] (reference), which isn't supported by core._find_p.

Better error messages when variables aren't passed into a view

Assuming this view:

@rt('/')
def post(slug:str):
   return P(slug)

If a slug isn't passed in, and a default isn't specified, the 500 error returned isn't descriptive. Looks something like this:

... <snip a bunch of starlette stuff>
    response = await func(request)
  File "/Users/daniel.roy.greenfeld/projects/fasthtml/fasthtml/core.py", line 230, in _f
    resp = f(*wreq)
  File "/Users/daniel.roy.greenfeld/projects/fh-ui/fh_ui/main.py", line 26, in get
KeyError: <class 'inspect._empty'>

It would be nice if KeyError: <class 'inspect._empty'> was replace with something meaningful, both in terms of what is seen in the shell and what can be captured in the code.

No precedence in view definitions

If I define this path:

@app.get("/{slug/")
def markdown_page(slug: str)
    return ...

that will collide with this no matter if they are before or after each other:

@app.get("/skyrocket/")
def skyrocket()
    return ...

Note: In FastAPI and Django the first item has precedence.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.