🌳 Lightweight framework for building dynamic HTML pages in pure Python.
Documentation: https://ludic.readthedocs.io/
Ludic is a lightweight framework for building HTML pages with a component approach similar to React. It is built to be used together with htmx.org so that developers don't need to write almost any JavaScript to create dynamic web services. Its potential can be leveraged together with its web framework which is a wrapper around the powerful Starlette framework. It is built with the latest Python 3.12 features heavily incorporating typing.
[!IMPORTANT] The framework is in a very early development/experimental stage. There are a lot of half-functioning features at the moment. Contributions are welcome to help out with the progress!
This framework allows HTML generation in Python while utilizing Python's typing system. Our goal is to enable the creation of dynamic web applications with reusable components, all while offering a greater level of type safety than raw HTML.
Key Ideas:
Here is an example of how Python's type system can be leveraged to enforce HTML structure:
br("Hello, World!") # type error (<br> can't have children)
br() # ok
html(body(...)) # type error (first child must be a <head>)
html(head(...), body(...)) # ok
div("Test", href="test") # type error (unknown attribute)
a("Test", href="...") # ok
Instead of using only basic HTML elements, it is possible to create modular components with the support of Python's type system. Let's take a look at an example:
Table(
TableHead("Id", "Name"),
TableRow("1", "John"),
TableRow("2", "Jane"),
TableRow("3", "Bob"),
)
This structure can be type-checked thanks to Python's rich type system. Additionally, this Table
component could have dynamic properties like sorting or filtering.
Python 3.12+
pip install "ludic[full]"
Similar to Starlette, you'll also want to install an ASGI server:
pip install uvicorn
components.py:
from typing import override
from ludic.html import a
from ludic.types import Attrs, Component
class LinkAttrs(Attrs):
to: str
class Link(Component[str, LinkAttrs]):
classes = ["link"]
@override
def render(self) -> a:
return a(
*self.children,
href=self.attrs["to"],
style={"color": self.theme.colors.primary},
)
Now you can use it like this:
link = Link("Hello, World!", to="/home")
web.py:
from ludic.web import LudicApp
from ludic.html import b, p
from .components import Link
app = LudicApp()
@app.get("/")
async def homepage() -> p:
return p(f"Hello {b("Stranger")}! Click {Link("here", to="https://example.com")}!")
To run the application:
uvicorn web:app
For more complex usage incorporating all capabilities of the framework, please visit the folder with examples on GitHub.
Any contributions to the framework are warmly welcome! Your help will make it a better resource for the community. If you're ready to contribute, read the contribution guide.