Skip to main content

Lightweight framework for building dynamic HTML pages in pure Python with t-strings.

Project description

Ludic Logo

test codecov Python 3.14+ Checked with mypy Discord Server

Documentation: https://getludic.dev/docs/


"I've just composed my first PageLayout component and I have no words!"

– Igor Davydenko


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. Version 1.x leverages Python 3.14's new t-strings (template strings) for safer and simpler HTML templating.

Features

  • Seamless </> htmx integration for rapid web development in pure Python
  • Type-Guided components utilizing Python's typing system
  • Uses the power of Starlette and Async for high-performance web development
  • Build HTML with the ease and power of Python t-strings (template strings)
  • Enhanced security with automatic escaping and clear separation of trusted vs untrusted content
  • Add CSS styling to your components with Themes
  • Create simple, responsive layouts adopted from the Every Layout Book

Comparison

Here is a table comparing Ludic to other similar tools:

Feature Ludic FastUI Reflex
HTML rendering Server Side Client Side Client Side
Uses a template engine No No No
UI interactivity </> htmx* React React
Backend framework Starlette, FastAPI, Django* FastAPI FastAPI
Client-Server Communication HTML + REST JSON + REST WebSockets

(*) HTMX as well as Starlette, FastAPI and Django are optional dependencies for Ludic, it does not enforce any frontend or backend frameworks. At it's core, Ludic only generates HTML and allows registering CSS.

Motivation

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:

  • Type-Guided HTML: Catch potential HTML structural errors at development time thanks to type hints. The framework enforces stricter rules than standard HTML, promoting well-structured and maintainable code.
  • Composable Components: Define reusable, dynamic HTML components in pure Python. This aligns with modern web development practices, emphasizing modularity.

Type-Guided HTML

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

Composable Components

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.

Migration from v0.5 to v1.0

Ludic 1.0 requires Python 3.14+ and introduces a major change: t-strings (template strings) replace the previous f-string approach with FormatContext. This provides better performance, simpler code, and enhanced security.

Key Changes

Replace f" with t" when mixing HTML elements with text:

# v0.5 (f-strings)
div(f"Hello {b('World')}")

# v1.0 (t-strings)
div(t"Hello {b('World')}")

Version Compatibility

  • Ludic 0.5.x: Python 3.12, 3.13 (uses f-strings)
  • Ludic 1.0.x: Python 3.14+ (uses t-strings)

Requirements

Python 3.14+

Note: Ludic 1.x requires Python 3.14+ for t-string support. If you're using Python 3.12 or 3.13, please use Ludic 0.5.x which uses f-strings instead.

Installation

pip install "ludic[full]"

You can also use a basic cookiecutter template to get quickly started, using UV, you need to run only one command:

uvx cookiecutter gh:getludic/template

Full Example

components.py:

from typing import override

from ludic import Attrs, Component
from ludic.html import a

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(t"Hello {b("Stranger")}! Click {Link("here", to="https://example.com")}!")

To run the application:

uvicorn web:app

Integrations

Here is a list of integrations and a link to the guide on how to get started:

More Examples

For more complex usage incorporating all capabilities of the framework:

Contributing

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.

  • GitHub Issues - If you encounter a bug, please report it here.
  • GitHub Discussions - To request a new feature, this is the best place to initiate the discussion.
  • Discord - Join our Discord server for support, sharing ideas, and receiving assistance.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ludic-1.0.2.tar.gz (565.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ludic-1.0.2-py3-none-any.whl (68.8 kB view details)

Uploaded Python 3

File details

Details for the file ludic-1.0.2.tar.gz.

File metadata

  • Download URL: ludic-1.0.2.tar.gz
  • Upload date:
  • Size: 565.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ludic-1.0.2.tar.gz
Algorithm Hash digest
SHA256 ab8f4bbe8558f14f9acd041bd24da4c45a7e49996c89c01e2507d02be1fc6680
MD5 e2961d372f165a79cec130745969b8bf
BLAKE2b-256 71d789c1abdf913fa56e6b7f5e1dbe1c2e9423b7a287df36e6bcae716a9864cf

See more details on using hashes here.

File details

Details for the file ludic-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: ludic-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 68.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ludic-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 18aff3e3b3eff9b404ef5f71974d29eb3189b71f8993fd85202b69fbb255c84f
MD5 ab1771f2691ea4cb5387d8253ffbb5f6
BLAKE2b-256 c13d92a1c30c6ae03014ea8e631c51e7aa899910ff0eef5e05068f6da5c1375a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page