Did Google Just Kill Streamlit?
Checking out Google’s Mesop Framework to see if it’s better than Streamlit.
Sitting on my couch, browsing through YouTube recommendations, I came across a video by Prompt Engineering on Google’s latest open-source Python framework named ‘Mesop,’ which is used by teams at Google for internal tooling and rapid prototyping. If you are wondering what ‘Mesop’ means, stop overthinking. Not everything in life makes sense. Will, if you are reading this, drop it in the comments below.
I figured out that maybe I should give it a shot and see if it is actually better than Streamlit. Although I love Streamlit for its super intuitive coding experience and the ability to generate apps out of thin air like magic, the minute you look at the interface, you know it is Streamlit.
To tackle this, I had written a blog on how to develop beautiful web apps purely using Python and Tailwind CSS, which blew up unexpectedly. You can read it here. This process is a lot more production-friendly, but what if you want to build prototypes that are customizable too?
That’s exactly what Mesop does.
TL;DR: Mesop is still in its nascent stage, so don’t start tippy-tapping away on your keyboard building a SaaS startup out of it. Also, it is not officially supported by Google.
Features of Mesop
- Open-Source.
- Easy to get started with pre-built components.
- Code in idiomatic Python.
- Hot-reload.
- Components are basically Python Functions.
- Built on Angular.
Getting Started with Mesop
As I hate to bore people with long literature on the different features of a technology, let’s dive right into building some basic UIs with Mesop. There are mainly three components you must be aware of while building with Mesop:
- State Class: This will act as your session’s state and provides the ability to share data among different components.
- Pages and Events: The main UI of the app.
- Styling Elements: CSS styles (Tailwind support is yet to come)
Before starting out make sure to install mesop using pip install mesop
.
State Class
All of the models used within the apps are instantiated using state classes with the decorator @me.stateclass
. Suppose you are building a GenAI app to generate articles using a title and outline, you will have to create a state class named ‘Article’ which would look like this:
import mesop as me
@me.stateclass
class Article:
title: str
outline: str
response: str
In each function or component we create, this state class data will be persisted throughout and accessible globally.
Pages and Events
Similar to the state class, all pages are tagged using a @me.page()
decorator in Mesop.
@me.page()
def app():
me.input(label="Title", type="text")
me.input(label="Outline", type="text")
The above code would generate a basic page with two input fields. To be able to store the data in the state class we created previously, each field will require a separate function.
def on_title_input(title: me.InputEvent):
s = me.state(Article)
s.title = title.value
def on_outline_input(outline: me.InputEvent):
s = me.state(Article)
s.outline = outline.value
@me.page()
def app():
me.input(label="Title", on_input=on_title_input, type="text")
me.input(label="Outline", on_input=on_outline_input, type="text")
To access the state class, the function used is me.state()
. The instance is then stored in the variable s
.
Events in Mesop are handled using classes such as InputEvent
, ClickEvent
, etc.
We can submit the input data by calling another function for the ClickEvent
.
# Response Schema
class Blog(typing_extensions.TypedDict):
title: str
content: str
def on_title_input(title: me.InputEvent):
s = me.state(Article)
s.title = title.value
def on_outline_input(outline: me.InputEvent):
s = me.state(Article)
s.outline = outline.value
def on_click(click: me.ClickEvent):
s = me.state(Article)
prompt = f"Write a blog on the following article title and outline: <article_title>{s.title}</article_title><article_outline>{s.outline}</article_outline>. Only return the final blog and title in markdown format."
# Add environment variable to store the hook URL
response = model.generate_content(
prompt,
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=Blog,
temperature=0.8
))
data = json.loads(response.text)
s.response = data
@me.page()
def app():
me.input(label="Title", on_input=on_title_input, type="text")
me.input(label="Outline", on_input=on_outline_input, type="text")
me.button("Generate Blog", on_click=on_click)
To make stuff interesting, we will be using the Gemini models to generate a blog using Mesop. The on_click
function will fetch data s.title
and s.outline
from the Article
instance and inject that into the prompt we have created. The response of the LLM will be a JSON containing the blog content.
Styling
Styling elements in Mesop is similar to the existing CSS properties, but all of the properties are parameters in me.style()
.
After defining the styles using me.style()
, they can be stored in variables and called as parameters in the components themselves.
_STYLE_INPUT_WIDTH = me.Style(width="100%")
_STYLE_BUTTON = me.Style(
background="#1976D2",
color="#fff",
padding=me.Padding.symmetric(horizontal=20, vertical=10),
font_size="16px",
cursor="pointer",
margin=me.Margin(bottom=20),
)
@me.page()
def app():
with me.box(style=_STYLE_CONTAINER):
s = me.state(Article)
with me.box(style=_STYLE_MAIN_COLUMN):
me.input(label="Title", on_input=on_title_input, type="text", style=_STYLE_INPUT_WIDTH)
me.input(label="Outline", on_input=on_outline_input, type="text", style=_STYLE_INPUT_WIDTH)
me.button("Generate Blog", on_click=on_click, style=_STYLE_BUTTON)
with me.box(style=_STYLE_PREVIEW_CONTAINER):
if s.response:
me.markdown(f"{s.response['content']}", style=_STYLE_PREVIEW)
Final Interface
Not the best-looking UI, but that’s because I didn’t make the effort to style it enough. It still looks a lot better than raw HTML with just a few tweaks here and there. With the ability to use all CSS properties for the components, you can make the UI unique.
Is Streamlit dead?
No, absolutely not. Mesop is still under development, and the documentation lacks in certain aspects. Deploying a Mesop application is not straightforward and requires containerization unless you are deploying it directly to Google Cloud.
Streamlit still comes with its own set of benefits and is fundamentally stronger compared to Mesop, especially if you are looking to build something in the data visualization domain. Moreover, Streamlit Cloud is a boon for anyone looking to share their applications absolutely free of charge.
However, Mesop has the potential to grow and is receiving updates every day, making it more viable for quick tooling and simple prototypes that work on REST APIs, thanks to its lightweight nature.
Kudos to Will and all the other contributors of Mesop for taking the time and effort to develop and maintain such a framework.
We need more UI frameworks
It is always good to see new Python UI frameworks pop up, such as Streamlit, Nicegui, Gradio, and now Mesop. Python’s idiomatic syntax makes coding much more accessible, which is evident from the sheer number of Streamlit projects out now.
Fortunately or unfortunately, Python was the first programming language I learned, and I fell in love with it immediately. With more frameworks like these, Python is going to grow rapidly in usage, and I hope to see more production-grade Python apps in the future.
I hope you liked this short blog on Mesop that would help you get started. I would’ve delved deeper, but the lack of time has made me keep this blog shorter, and I also considered the goldfish-level attention span we all have. I would really appreciate it if you could clap for this post, share it with more users in the Python and Google community, and follow me for more such articles.