DEV Community

Cover image for pydantic-ui
Idling Mind
Idling Mind

Posted on

pydantic-ui

I recently ran into an issue at work. We have an engineering analysis process that required large amount of inputs to be collected from engineers which contained deeply nested structure with multiple input types. As any sane python developer will do these days, we used pydantic models to both structure our mental model and also to validate this massive input (which comes in the form of a yaml file)

Even though yaml is a decent format to use for structured input, it's still quite verbose to hand-craft and it can be quite tricky to get all the indents right. So we needed a decent UI which can be used to fill in this input. The thought was, Instead of writing a UI specifically for our data structure, why not infer the structure dynamically from the already existing pydantic model? It can also give us proper (and upfront) validation errors if there are errors in the input. If we can sprinkle a little more customization on top of the existing pydantic model, it can be quite powerful. That's exactly what I ended up doing (with a lot of help from claude opus 4.5). We thought of open-sourcing our solution.

light-mode screenshot

Here's pydantic-ui. It a bit opinionated, but also lets you configure quite a lot. Please do test it out and any feedback is welcome!

Example usage

from fastapi import FastAPI
from pydantic import BaseModel, Field

from pydantic_ui import DisplayConfig, FieldConfig, Renderer, UIConfig, create_pydantic_ui


# Define your Pydantic model
class Address(BaseModel):
    street: str
    city: str
    zipcode: str


class Person(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    age: int = Field(gt=0, lt=150)
    email: str
    address: Address = Field(description="Permanent address")
    tags: list[str] = []


# Create FastAPI app and mount pydantic-ui
app = FastAPI()

app.include_router(
    create_pydantic_ui(
        Person,
        prefix="/editor",
        ui_config=UIConfig(
            title="Person Editor",
            show_save_reset=True,
            attr_configs={
                "age": FieldConfig(
                    renderer=Renderer.SLIDER,
                    display=DisplayConfig(
                        title="User Age",
                    )
                )
            },
        ),
    ),
)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
Enter fullscreen mode Exit fullscreen mode

This will be rendered like below.

Top comments (0)