Hi everyone, I’ve been working quite a bit with streamlit.io lately and I found some real bad practices and code smells in the official docs .
Because of that I decided to use Streamlit in a completely different way using the best code practices, everything tested and composable.
Using the library as a scripting language with files of hundreds of lines, with a lot of complexity and coupling it’s a really bad and it should be avoided
The idea is to create a base class called Component
, this class has a render
method in which you call Streamlit. Using classes we can inject the dependencies in the constructor and because of that make the components super easy to test.
from abc import ABC, abstractmethod
class Component(ABC):
@abstractmethod
def render(self, *args, **kwargs) -> None: # type: ignore
raise NotImplementedError
Using classes allow us too to create reusable components that can be use multiple times across the application.
For example imagine you have a form that makes an HTTP call to an external service:
from collections.abc import Callable
import streamlit as st
import streamlit_pydantic as sp
from pydantic import BaseModel
from src.component import Component
class Form(Component):
def __init__(self, callback: Callable, model: type[BaseModel]) -> None:
self.callback = callback
self.model = model
def render(self) -> None:
data = sp.pydantic_form(key=__name__, model=self.model)
if data:
self.callback(data.model_dump())
With this approach you can isolate the component from the external call, test it without any problems and also develop the service which makes the HTTP call in a completely independent way.
Use the component is as easy as this:
class ExampleModel(BaseModel):
some_text: str
some_number: int
some_boolean: bool
def _callback(data: dict) -> None:
st.json(data)
form = Form(self._callback, ExampleModel)
form.render()
If you want to know more about it you can see the main components deployed at https://clean-components.streamlit.app, you can also can see the code.
Hope this is useful to you!