Skip to main content

Build better UIs faster.

Project description

FastUI

CI pypi versions license

Please note: FastUI is still an active work in progress, do not expect it to be complete.

The Principle (short version)

You can see a simple demo of an application built with FastUI here.

FastUI is a new way to build web application user interfaces defined by declarative Python code.

This means:

  • If you're a Python developer — you can build responsive web applications using React without writing a single line of JavaScript, or touching npm.
  • If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
  • For everyone — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface

At its heart, FastUI is a set of matching Pydantic models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic.

The Practice — Usage

FastUI is made up of 4 things:

Here's a simple but complete FastAPI application that uses FastUI to show some user profiles:

from datetime import date

from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from fastui import FastUI, AnyComponent, prebuilt_html, components as c
from fastui.components.display import DisplayMode, DisplayLookup
from fastui.events import GoToEvent, BackEvent
from pydantic import BaseModel, Field

app = FastAPI()


class User(BaseModel):
    id: int
    name: str
    dob: date = Field(title='Date of Birth')


# define some users
users = [
    User(id=1, name='John', dob=date(1990, 1, 1)),
    User(id=2, name='Jack', dob=date(1991, 1, 1)),
    User(id=3, name='Jill', dob=date(1992, 1, 1)),
    User(id=4, name='Jane', dob=date(1993, 1, 1)),
]


@app.get("/api/", response_model=FastUI, response_model_exclude_none=True)
def users_table() -> list[AnyComponent]:
    """
    Show a table of four users, `/api` is the endpoint the frontend will connect to
    when a user fixes `/` to fetch components to render.
    """
    return [
        c.Page(  # Page provides a basic container for components
            components=[
                c.Heading(text='Users', level=2),  # renders `<h2>Users</h2>`
                c.Table[User](  # c.Table is a generic component parameterized with the model used for rows
                    data=users,
                    # define two columns for the table
                    columns=[
                        # the first is the users, name rendered as a link to their profile
                        DisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')),
                        # the second is the date of birth, rendered as a date
                        DisplayLookup(field='dob', mode=DisplayMode.date),
                    ],
                ),
            ]
        ),
    ]


@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True)
def user_profile(user_id: int) -> list[AnyComponent]:
    """
    User profile page, the frontend will fetch this when the user visits `/user/{id}/`.
    """
    try:
        user = next(u for u in users if u.id == user_id)
    except StopIteration:
        raise HTTPException(status_code=404, detail="User not found")
    return [
        c.Page(
            components=[
                c.Heading(text=user.name, level=2),
                c.Link(components=[c.Text(text='Back')], on_click=BackEvent()),
                c.Details(data=user),
            ]
        ),
    ]


@app.get('/{path:path}')
async def html_landing() -> HTMLResponse:
    """Simple HTML page which serves the React app, comes last as it matches all paths."""
    return HTMLResponse(prebuilt_html(title='FastUI Demo'))

Which renders like this:

screenshot

Of course, that's a very simple application, the full demo is more complete.

Components

FastUI already defines the following components, all are shown in the demo app:

  • Text - renders a string
  • Paragraph - renders a string as a paragraph
  • PageTitle - renders nothing, sets the HTML page title
  • Div - renders a <div> with arbitrary components inside
  • Page - a container for components
  • Heading - renders a heading <h1> to <h6>
  • Markdown - renders markdown, example
  • Code - renders code with highlighting in a <pre>
  • Button - renders a <button>
  • Link - renders a link <a>
  • LinkList - renders a list of links
  • Navbar - renders a navbar <nav>
  • Modal - renders a modal dialog that opens triggered by an event
  • ServerLoad - render components fetched from the server, also provides SSE mode to update components based on server sent events
  • Table - renders a table
  • Details - renders a table of key/value pairs as a <dl>
  • Display - renders a value based on a display mode
  • Table - renders a table from a list of Pydantic models
  • Pagination - renders a pagination component
  • FormFieldInput - renders a form field using <input>
  • FormFieldCheckbox - renders a form field for a boolean using <input type="checkbox">
  • FormFieldSelect - renders a form field using <select> or react-select
  • FormFieldSelectSearch - renders a form field using react-select with options updated from the server on search
  • Form - renders a form using a list of FormField components
  • ModelForm - renders a form based on a Pydantic model; the model's JSON Schema is used to build a list of FormField components

The Principle (long version)

FastUI is an implementation of the RESTful principle; but not as it's usually understood, instead I mean the principle defined in the original pHD dissertation by Roy Fielding, and excellently summarised in this essay on htmx.org (HTMX people, I'm sorry to use your article to promote React which I know you despise 🙏).

The RESTful principle as described in the HTMX article is that the frontend doesn't need to (and shouldn't) know anything about the application your building. Instead, it should just provide all the components you need to construct the interface, the backend can then tell the frontend what to do.

Think of your frontend as a puppet, and the backend as the hand within it — the uppet doesn't need to know what to say, that's kind of the point.

Building an application this way has a number of significant advantages:

  • you only need to write code in one place to build a new feature — add a new view, change the behavior of an existing view or alter the URL structure
  • deploying the front and backend can be completely decoupled, provided the frontend knows how to render all the components the backend is going to ask it to use, you're good to go
  • You should be able to reuse a rich set of opensource components, they should end up being better tested and more reliable than anything you could build yourself, this is possible because the components need no context about how they're going to be used (note: since FastUI is brand new, this isn't true yet, hopefully we get there)
  • We can use Pydantic, TypeScript and JSON Schema to provide guarantees that the two sides are communicating with an agreed schema (note: this is not complete yet, see #TODO)

In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.

Beyond Python and React

Of course, this principle shouldn't be limited to Python and React applications — provided we use the same set of agreed schemas and encoding to communicate, we should be able to use any frontend and backend that implements the schema. Interchangeably.

This could mean:

  • implementing web a frontend using another JS framework like Vue — lots of work, limited value IMHO
  • implementing web a frontend using an edge server, so the browser just sees HTML — lots of work but very valuable
  • implementing frontends for other platforms like mobile or IOT — lots of work, no idea if it's actually a good idea?
  • implementing the component models in another language like Rust or Go — since there's actually not that much code in the backend, so this would be a relatively small and mechanical task

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

fastui-0.2.0.tar.gz (95.5 kB view details)

Uploaded Source

Built Distribution

fastui-0.2.0-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file fastui-0.2.0.tar.gz.

File metadata

  • Download URL: fastui-0.2.0.tar.gz
  • Upload date:
  • Size: 95.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.5

File hashes

Hashes for fastui-0.2.0.tar.gz
Algorithm Hash digest
SHA256 319bf8c2101517717a7e4882f2d5037cbfe442bfc26343a1277cd8e2a3cac9c5
MD5 b5ef53cf4105bf9f3f6c856bb254f045
BLAKE2b-256 682b7f055b2cb03ec903337dca39f5f680ee8ccbc337b9862dac85c393d8a0a2

See more details on using hashes here.

Provenance

File details

Details for the file fastui-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: fastui-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.5

File hashes

Hashes for fastui-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 070b02f3c7f588a98e3755c525c67fcf28b2a65d34f3ff488ef65ebf9fd87c9d
MD5 b139ab73358e30b9467ccef527530b08
BLAKE2b-256 a37fd0ba85aa492f3d55177267b7a6b1c21f95631e7e375baf4ad41468d07d19

See more details on using hashes here.

Provenance

Supported by

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