# Technical Documentation: mesop-dev/mesop

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/mesop-dev/mesop)

### Overview (`README.md`)

# Mesop: Build delightful web apps quickly in Python 🚀


> [!WARNING]
> **Mesop is no longer maintained as of September 30, 2026.** No further updates, bug fixes, or support will be provided.

[](https://pypi.org/project/mesop/)
[](https://pypi.org/project/mesop/)
[](https://twitter.com/mesop_dev)

- If you're interested in learning how to use Mesop, please [read our main docs](https://mesop-dev.github.io/mesop/).

- If you're interested in contributing to the core Mesop framework, please [read our contributing guide](https://mesop-dev.github.io/mesop/internal/contributing/).

### Used at Google for rapid internal app development

Mesop is a Python-based UI framework that allows you to rapidly build web apps like demos and internal apps:

**Intuitive for UI novices ✨**

- Write UI in idiomatic Python code
- Easy to understand reactive UI paradigm
- Ready to use components

**Frictionless developer workflows 🏎️**

- Hot reload so the browser automatically reloads and preserves state
- Rich IDE support with strong type safety

**Flexible for delightful demos 🤩**

- Build custom UIs _without_ writing Javascript/CSS/HTML
- Compose your UI into components, which are just Python functions

## Write your first Mesop app in less than 10 lines of code...

[Demo app](https://mesop-dev.github.io/mesop/demo/?demo=text_to_text)

```python
import mesop as me
import mesop.labs as mel


@me.page(path="/text_to_text", title="Text to Text Example")
def app():
  mel.text_to_text(
    upper_case_stream,
    title="Text to Text Example",
  )


def upper_case_stream(s: str):
  return "Echo: " + s.capitalize()
```

</div>

## Try it

### Colab

You can try [Mesop on Colab](https://colab.research.google.com/github/mesop-dev/mesop/blob/main/notebooks/mesop_colab_getting_started.ipynb)!

### Locally

**Step 1:** Install it

```sh
pip install mesop
```

**Step 2:** Copy the example above into `main.py`

**Step 3:** Run the app

```sh
mesop main.py
```

Learn more in [Getting Started](https://mesop-dev.github.io/mesop/getting-started/installing/).

## Reporting a security issue

If you identify a security vulnerability with Mesop, please file a [GitHub security issue](https://github.com/mesop-dev/mesop/security/policy) and we will promptly respond to it.

## Disclaimer

_This is not an officially supported Google product._

---

### Overview (`.yarn/README.md`)

# Yarn Vendoring
We utilize Yarn's `yarn-path` configuration in a shared `.yarnrc` file to enforce
everyone using the same version of Yarn.  Yarn checks the `.yarnrc` file to
determine if yarn should delegate the command to a vendored version at the
provided path.

## How to update
To update to the latest version of Yarn as our vendored version:
- Run this command
```sh
yarn policies set-version latest
```
- Remove the previous version

---

### Overview (`demo/README.md`)

---
title: Mesop Demo Gallery
emoji: 👓
colorFrom: blue
colorTo: purple
sdk: docker
pinned: false
license: apache-2.0
app_port: 8080
---

# Mesop demo app

This app demonstrates Mesop's various components and features. Create your own Cloud Run app by following: https://mesop-dev.github.io/mesop/guides/deployment/

## Development

### Setup

From workspace root:

```sh
rm -rf demo/venv && \
virtualenv --python python3 demo/venv && \
source demo/venv/bin/activate && \
pip install -r demo/requirements.txt
```

### Run

1. `cd demo`
1. `mesop main.py`

## Generate screenshots

If you add more demos and want to re-generate screenshots, do the following steps:

1. in `demo/screenshot.ts` change `test.skip` to `test`
1. Run: `yarn playwright test demo/screenshot.ts`
1. Install cwebp using `brew install webp` or download from [here](https://developers.google.com/speed/webp/docs/precompiled).
1. From the workspace root, run:

```sh
`for file in demo/screenshots/*; do cwebp -q 50 "$file" -o "${file%.*}.webp"; done`
```

## Deployment

**Pre-requisites:**

- Make sure you [generate screenshots](#generate-screenshots) before deploying!
- Ensure a recent version of Mesop has been published to pip, otherwise the demos may not work (because they rely on a new API).

### Deploy to Cloud Run

This app is deployed to Google Cloud Run.

```sh
gcloud run deploy mesop --source .
```

See our Mesop deployment [docs](https://mesop-dev.github.io/mesop/guides/deployment/#deploy-to-google-cloud-run) for more background.

### Deploy to Hugging Face Spaces

> NOTE: You need to update demo/requirements.txt to point to the latest Mesop version because Hugging Face Spaces may use a cached version of Mesop which is too old.

> NOTE: You'll need to install git xet to binary images. We need this for uploading screenshots. See https://huggingface.co/docs/hub/xet/using-xet-storage#using-git-xet.

```sh
./demo/deploy_to_hf.sh ../hf_demo
```

You can change `../hf_demo` to any dir path outside of your Mesop repo.

> Note: if you get an error in Hugging Face Spaces "No app file", then you can create an "app.py" file in the Spaces UI to manually trigger a build. This seems like a bug with Hugging Face.

---

### Overview (`scripts/README.md`)

# Scripts

All these scripts should be executed from the root directory of the Git repo.

**Example shell script usage:**

```shell
scripts/${script_name}.sh
```

**Example Python script usage:**

```shell
python scripts/scaffold_component.py ${component_name}
```

---

### Overview (`showcase/README.md`)

---
title: Mesop Showcase
emoji: 👓
colorFrom: red
colorTo: yellow
sdk: docker
pinned: false
license: apache-2.0
app_port: 8080
---

---

### Overview (`tools/README.md`)

# Tools

The files in this subtree are forked from: https://github.com/angular/components/tree/ff67a416d19e9237607605bec0d7cc372025387f/tools

These tools provide Bazel tooling support for Angular.

---

### Overview (`build_defs/app_bundle/README.md`)

Forked from https://github.com/angular/dev-infra/tree/301473cb5fbcd6cca8b70a6369dcdf3dc2d12e08/bazel/app-bundling

---

### Overview (`mesop/pip_package/README.md`)

# Mesop

Mesop is an open-source package to build UIs in Python.

Source code: github.com/mesop-dev/mesop

---

### Overview (`mesop/examples/web_component/firebase_auth/README.md`)

# README

```sh
gcloud config set project mesop-auth-test
```

```sh
gcloud auth application-default login
```

---

### Index (`docs/index.md`)

---
hide:
  - navigation
  - toc
---

<style>
  :root {
    --modern-ui-principles-offset: 560px;
  }

  .md-typeset h1 {
    display: none;
  }

  .md-content__inner {
    margin-left: 1.2rem;
    margin-right: 1.2rem;
  }

  .tabbed-set, .highlight {
    max-width: 768px;
  }

  .tabbed-set {
    margin-top: -40px !important;
  }

  #modern-ui-principles + .highlight {
    margin-top: calc(-1 * var(--modern-ui-principles-offset));
  }

  code {
    background: none !important;
  }
</style>

<header>
  <div class="headline">Rapidly build AI apps in Python</div>
  <div class="sub-headline">
    Create web apps without the complexity of frontend development.</br>
    Used at Google for rapid AI app development.
  </div>
  <div class="header-buttons">
    <a class="primary-button" href="./getting-started/installing">Get started</a>
    <a class="secondary-button" href="./showcase">View showcase</a>
  </div>
</header>

<section style="padding: 72px 0">
  <div class="center column">
    <div class="section-headline">
      Create your AI chat app in minutes
    </div>
    <div class="main-demo-iframe-container">
      <iframe src="https://mesop-dev.github.io/mesop/demo/?demo=embed/chat" class="main-demo-iframe box-shadow"></iframe>
      <div class="embed-overlay">
          <button class="open-button" onclick="openDemoGalleryInNewTab()">
              <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
              </svg>
              Open in new tab
          </button>
      </div>
    </div>
  </div>
</section>

<script>
function openDemoGalleryInNewTab() {
  window.open('https://mesop-dev.github.io/mesop/demo/', '_blank');
}
</script>
<section class="container">
  <div class="section-chip teal">Python-native</div>
  <div class="section-headline">
    Write your UI in idiomatic Python
  </div>
  <div class="section-body">
    With Mesop, you can leverage your existing Python expertise to build UIs effortlessly. No new languages to learn - just write clean, idiomatic Python using familiar constructs like functions, loops, and conditionals.
  </div>
</section>

=== "State management"

    ``` python
    @me.stateclass
    class State:
      val: str

    @me.page()
    def page():
        state = me.state(State)
        me.text(state.val)
    ```

=== "Control flow"

    ``` python
    @me.stateclass
    class State:
      is_loaded: bool

    @me.page()
    def page():
        if me.state(State).is_loaded:
          me.text("Loaded")
        else:
          me.progress_spinner()
    ```

=== "Streaming UI"

    ```python
    --8<-- "mesop/examples/docs/streaming.py"
    ```

=== "Composable"

    ``` python
    @me.content_component
    def scaffold(url: str):
      with me.box():
        menu(url=url)
        me.slot()

    @me.component
    def menu(url: str):
      ...

    def page1():
      with scaffold(url="/page1"):
        some_content(...)
    ```


<section id="modern-ui-principles" class="container blue-gradient-background" style="padding-bottom: var(--modern-ui-principles-offset)">
  <div class="section-chip blue">Modern UI principles</div>
  <div class="section-headline">
    Declarative UI that's easy to understand
  </div>
  <div class="section-body">
    Mesop streamlines UI development with a declarative approach. Build expressive, maintainable interfaces using battle-tested patterns in Python. Say goodbye to complex imperative logic and hello to intuitive, clean code.
  </div>
</section>

```py
@me.stateclass
class State:
    image_data: str
    detections: list[Detection]


@me.page()
def object_detector():
    state = me.state(State)

    me.text("Real-time Object Detection", type="headline-4")
    me.uploader(label="Upload an image", on_upload=on_image_upload)

    if state.image_data:
        me.image(src=f"data:image/jpeg;base64,{state.image_data}")

    if state.detections:
        me.text("Detected Objects:", type="headline-5")
        for detection in state.detections:
            detection_component(detection)

def detection_component(detection):
    me.text(f"{detection.obj}: {detection.confidence:.2f}")

def on_image_upload(e: me.UploadEvent):
    state = me.state(State)
    state.image_data = base64.b64encode(e.file.read()).decode()
    state.detections = detect_objects(e.file)
```

<section class="container pink-gradient-background">
  <div class="section-chip pink">Building blocks</div>
  <div class="section-headline">
    Jumpstart with ready-to-use components
  </div>
  <div class="section-body">
    Mesop provides a versatile range of <a href="./components/">30 components</a>, from low-level building blocks to high-level, AI-focused components. This flexibility lets you rapidly prototype ML apps or build custom UIs, all within a single framework that adapts to your project's use case.

    <a class="section-button" href="./components/chat">
      AI components
      <div class="section-button-subtext">e.g. chat, text to image</div>
    </a>
    <a class="section-button" href="./components/input">
      Form components
      <div class="section-button-subtext">e,g. input, checkbox, radio</div>
    </a>
    <a class="section-button" href="./components/table">
      Data display components
      <div class="section-button-subtext">e,g. table, plot</div>
    </a>
  </div>
</section>

<section class="container amber-gradient-background">
  <div class="section-chip amber">Build anything</div>
  <div class="section-headline">
    Build any user interface you can imagine
  </div>
  <div class="section-body">
With Mesop, you can build virtually any web-based user interface or application you can imagine. From quick prototypes to enterprise tools, Mesop provides the customizability to bring your ideas to life.
  </div>
  <iframe src="https://mesop-dev.github.io/mesop/demo/?demo=embed/chat_inputs" class="chat-inputs-iframe box-shadow"></iframe>
</section>

<section class="container">
  <div class="section-chip teal">Extensible</div>
  <div class="section-headline">
  Seamlessly integrate JS with web components
  </div>
  <div class="section-body">
    Get the best of both worlds with Mesop <a href="./web-components/">web components</a>. Leverage Python's simplicity for core logic, while accessing the vast ecosystem of JS libraries
  </div>
</section>

=== "counter_component.py"

    ```python
    --8<-- "mesop/examples/web_component/quickstart/counter_component.py"
    ```
=== "counter_component.js"

    ```python
    --8<-- "mesop/examples/web_component/quickstart/counter_component.js"
    ```

<section class="container blue-gradient-background">
  <div class="section-chip blue">Simple deployment</div>
  <div class="section-headline">
    Deploy your app and share in minutes
  </div>
  <div class="section-body">
Mesop streamlines cloud deployment, enabling you to share your AI application with the world in minutes. With step-by-step guides for deploying to Google Cloud Run or any cloud service that takes a container, you can go from local development to production-ready deployment without wrestling with complex server setups.
<a class="section-button" href="./guides/deployment/#cloud-run" style="display: flex">
<img src="https://www.gstatic.com/bricks/image/f2e0986a2802c0b6c4be7f1355599d5aadfb21a63b7e9643d96697ff9334a1e1.svg" style="width: 40px; margin-right: 12px">
  <div>
      Google Cloud Run
      <div class="section-button-subtext">Free for small apps</div>
      </div>
    </a>
  </div>
</section>

<section class="container pink-gradient-background">
  <div class="section-chip pink">Developer experience</div>
  <div class="section-headline">
    Delightful developer experience
  </div>
  <div class="section-body">
Mesop streamlines app development with features like hot reload and strong IDE support with static types, eliminating friction and boosting productivity.
</div>
  <div class="dev-gif-row">
    <div class="gif-caption">
      Instant hot reload
    </div>
    <img src="./assets/hot-reload.gif" class="dev-gif box-shadow">
  </div>
  <div class="dev-gif-row row-reverse">
  <div class="gif-caption">
      IDE support with static types
    </div>
    <img src="./assets/ide-support.gif" class="dev-gif box-shadow">
  </div>
</section>

<section class="container teal-gradient-background">
  <div class="section-chip teal">Community</div>
  <div class="section-headline">
    See what others are saying
  </div>
  <div class="section-body">
 Join developers around the world who are building AI apps in Mesop.
  </div>
    <div class="center">
      <iframe
        class="youtube-embed box-shadow"
        style="margin-bottom: 16px"
        width="560"
        height="315"
        src="https://www.youtube.com/embed/Zi1DnGOrYho?si=DYNApUBow3AfT47y"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        referrerpolicy="strict-origin-when-cross-origin"
        allowfullscreen
      ></iframe>
    </div>

    <div class="cloud-run-tutorials">
      <iframe
        class="youtube-embed box-shadow"
        width="560"
        height="315"
        src="https://www.youtube.com/embed/KUfPiSUJrwE?si=dkVt0rBOQUMapk5D"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        referrerpolicy="strict-origin-when-cross-origin"
        allowfullscreen
      ></iframe>

      <iframe
        class="youtube-embed box-shadow"
        width="560"
        height="315"
        src="https://www.youtube.com/embed/s9Ag_YNdl0M?si=aj4RqzS2MmWvxFos"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        referrerpolicy="strict-origin-when-cross-origin"
        allowfullscreen
      ></iframe>
    </div>

    <div class="twitter-row">
      <blockquote class="twitter-tweet">
        <p lang="en" dir="ltr">
          is this the thing that will finally save me from ever learning front
          end?<a href="https://t.co/eDgY0AfG6U">https://t.co/eDgY0AfG6U</a>
        </p>
        &mdash; xlr8harder (@xlr8harder)
        <a
          href="https://twitter.com/xlr8harder/status/1798673386425786724?ref_src=twsrc%5Etfw"
          >June 6, 2024</a
        >
      </blockquote>
      <script
        async
        src="https://platform.twitter.com/widgets.js"
        charset="utf-8"
      ></script>

      <blockquote class="twitter-tweet">
        <p lang="en" dir="ltr">
          i hate writing frontend code, but can’t resist a sleek UI. just
          tried Google’s quietly released Mesop—what a find! no frontend fuss,
          just python. <br /><br />if you value your sanity and good design,
          you should def try it. gives a balance between streamlit and
          gradio<br /><br />mesop docs :…
          <a href="https://t.co/SmBAH5Leri">pic.twitter.com/SmBAH5Leri</a>
        </p>
        &mdash; Sanchay Thalnerkar (@7anchay)
        <a
          href="https://twitter.com/7anchay/status/1804890091816644906?ref_src=twsrc%5Etfw"
          >June 23, 2024</a
        >
      </blockquote>
      <script
        async
        src="https://platform.twitter.com/widgets.js"
        charset="utf-8"
      ></script>

      <blockquote class="twitter-tweet">
        <p lang="en" dir="ltr">
          New Python-based UI framework in town - have you tried Mesop from
          Google?<br /><br />I gave it a spin, here&#39;s what I found out:<br /><br />📌
          Provides a unique approach to building web hashtag#UIs in
          hashtag#Python with component-based architecture for customized UIs
          by taking inspiration from…
        </p>
        &mdash; Harshit Tyagi (@dswharshit)
        <a
          href="https://twitter.com/dswharshit/status/1800557173073691000?ref_src=twsrc%5Etfw"
          >June 11, 2024</a
        >
      </blockquote>
      <script
        async
        src="https://platform.twitter.com/widgets.js"
        charset="utf-8"
      ></script>
  </div>

</section>

## Disclaimer

_This is not an officially supported Google product._

---

### Codelab Index (`docs/codelab/index.md`)

# Mesop DuoChat Codelab

This tutorial walks you through building DuoChat, an interactive web application for chatting with multiple AI models simultaneously. You'll learn how to leverage Mesop's powerful features to create a responsive UI and integrate with AI APIs like Google Gemini and Anthropic Claude.

### What you will build

By the end of this codelab, you will build [DuoChat (demo)](https://huggingface.co/spaces/wwwillchen/mesop-duo-chat) that will allow users to:

- Select multiple AI models to chat with
- Compare responses from different models side-by-side
- Provide their own API keys

If you want to dive straight into the code, you can look at the [DuoChat repo](https://github.com/wwwillchen/mesop-duo-chat) and each branch represents the completed code after each section.

### Setting Up the Development Environment

Let's start by setting up our development environment:

1. Create a new directory for your project:

```bash
mkdir duochat
cd duochat
```

2. Follow the [Mesop command-line installation guide](../getting-started/installing.md#b-command-line) and create a [virtual environment](../getting-started/installing.md#create-a-venv-environment) and activate it.

3. Create a `requirements.txt` file with the following content:

```
mesop
gunicorn
anthropic
google-generativeai
```

4. Install the dependencies:

```bash
pip install -r requirements.txt
```

### Setting Up the Main Application

Let's start by creating a basic Mesop application. Create `main.py` and add the following code:

```python title="main.py"
import mesop as me

@me.page(path="/")
def page():
    me.text("Welcome to DuoChat!")
```

This creates a simple Mesop application with a welcome message.

### Running the Application

To run your Mesop application:

```bash
mesop main.py
```

Navigate to `http://localhost:32123` in your web browser. You should see the welcome message.

### Getting API keys

Later on, you will need API keys to call the respective AI models:

- [Get a Google Gemini API Key](https://ai.google.dev/gemini-api/docs/api-key) and use the Gemini API [free tier](https://ai.google.dev/pricing).
- [Get an Anthropic API Key](https://docs.anthropic.com/en/docs/quickstart#prerequisites) and setup billing. Check their docs for pricing.

> TIP: You can get started with the Gemini API key, which has a free tier, first and create the Anthropic API key later.

### Troubleshooting

If you're having trouble, compare your code to the [solution](https://github.com/wwwillchen/mesop-duo-chat/tree/1_completed).

### Next Steps

In the next section, we'll start building the user interface for DuoChat, including the header, chat input area, and basic styling. We'll explore Mesop's components and styling system to create an attractive and functional layout.

<a href="./2" class="next-step">
    Building the basic UI
</a>

---

### Components Index (`docs/components/index.md`)

# Components

Please read [Core Concepts](../getting-started/core-concepts.md) before this as it explains the basics of components. This page provides an overview of the different types of components in Mesop.

## Types of components

### Native components

Native components are components implemented using Angular/Javascript. Many of these components wrap [Angular Material components](https://material.angular.io/components/). Other components are simple wrappers around DOM elements.

If you have a use case that's not supported by the existing native components, please [file an issue on GitHub](https://github.com/mesop-dev/mesop/issues/new) to explain your use case. Given our limited bandwidth, we may not be able to build it soon, but in the future, we will enable Mesop developers to build their own custom native components.

### User-defined components

User-defined components are essentially Python functions which call other components, which can be native components or other user-defined components. It's very easy to write your own components, and it's encouraged to split your app into modular components for better maintainability and reusability.

### Web components

Web components in Mesop are custom HTML elements created using JavaScript and CSS. They enable custom JavaScript execution and bi-directional communication between the browser and server. They can wrap JavaScript libraries and provide stateful client-side interactions. [Learn more about web components](../web-components/index.md).

## Content components

Content components allow you to compose components more flexibly than regular components by accepting child(ren) components. A commonly used content component is the [button](./button.md) component, which accepts a child component which oftentimes the [text](./text.md) component.

Example:

```python
with me.button():
  me.text("Child")
```

You can also have multiple content components nested:

```python
with me.box():
  with me.box():
    me.text("Grand-child")
```

Sometimes, you may want to define your own content component for better reusability. For example, let's say I want to define a scaffold component which includes a menu positioned on the left and a main content area, I could do the following:

```python
@me.content_component
def scaffold(url: str):
  with me.box(style=me.Style(background="white")):
    menu(url=url)
    with me.box(style=me.Style(padding=me.Padding(left=MENU_WIDTH))):
      me.slot()
```

Now other components can re-use this `scaffold` component:

```python
def page1():
  with scaffold(url="/page1"):
    some_content(...)
```

This is similar to Angular's [Content Projection](https://angular.io/guide/content-projection).

### Advanced content component usage

#### Multi-slot projection

Mesop supports multi-slot projection using named slots.

Here is an example:

```python
@me.slotclass
class LayoutSlots:
  header: me.NamedSlot
  content: me.NamedSlot
  footer: me.NamedSlot

@me.content_component(named_slots=LayoutSlots)
def layout():
  with me.box(style=me.Style(background="black")):
    me.slot("header")
  with me.box(style=me.Style(background="white")):
    me.slot("content")
  with me.box(style=me.Style(background="black")):
    me.slot("footer")
```

Now other components can re-use this `layout` component:

```python
def page1():
  with layout() as c:
    with c.header():
      me.text("Header")
    with c.content():
      me.text("Content")
    with c.footer():
      me.text("Footer")
```

#### Composed content components

Content components can also use other content components, but you need to be careful since
slot rendering cannot be deferred to the parent component.

???+ failure "Slot rendering cannot be deferred by setting another slot."

    ```python
    @me.content_component
    def inner():
        me.slot()

    @me.content_component
    def outer():
      with inner():
        me.slot()
    ```

???+ success "Content components can use content components so long as the slots get rendered by the parent content component."

    ```python
    @me.content_component
    def header(background_color: str):
      with me.box(style=me.Style(background=background_color)):
        me.slot()


    @me.content_component
    def footer(background_color: str):
      with me.box(style=me.Style(background=background_color)):
        me.slot()


    @me.content_component()
    def content_layout():
      with header(background_color="black"):
        me.text("Header")
      with me.box(style=me.Style(background="white")):
        me.slot()
      with footer(background_color="red")
        me.text("Footer")
    ```

    Now other components can re-use this `content_layout` component:

    ```python
    def page1():
      with content_layout():
        me.text("Content")
    ```

## Component Key

Every native component in Mesop accepts a `key` argument which is a component identifier. This is used by Mesop to tell [Angular whether to reuse the DOM element](https://angular.io/api/core/TrackByFunction#description).

### Resetting a component

You can reset a component to the initial state (e.g. reset a [select](./select.md) component to the unselected state) by giving it a new key value across renders.

For example, you can reset a component by "incrementing" the key:

```py
class State:
  select_menu_key: int

def reset(event):
  state = me.state(State)
  state.select_menu_key += 1

def main():
  state = me.state(State)
  me.select(key=str(state.select_menu_key),
            options=[me.SelectOption(label="o1", value="o1")])
  me.button(label="Reset", on_click=reset)
```

### Event handlers

Every Mesop event includes the key of the component which emitted the event. This makes it useful when you want to reuse an event handler for multiple instances of a component:

```py
def buttons():
  for fruit in ["Apple", "Banana"]:
    me.button(fruit, key=fruit, on_click=on_click)

def on_click(event: me.ClickEvent):
  fruit = event.key
  print("fruit name", fruit)
```

Because a key is a `str` type, you may sometimes want to store more complex data like a dataclass or a proto object for retrieval in the event handler. To do this, you can serialize and deserialize:

```py
import json
from dataclasses import dataclass

@dataclass
class Person:
  name: str

def buttons():
  for person in [Person(name="Alice"), Person(name="Bob")]:
    # serialize dataclass into str
    key = json.dumps(person.asdict())
    me.button(person.name, key=key, on_click=on_click)

def on_click(event: me.ClickEvent):
  person_dict = json.loads(event.key)
  # modify this for more complex deserialization
  person = Person(**person_dict)
```

!!! Tip "Use component key for reusable event handler"

    This avoids a [subtle issue with using closure variables in event handlers](../guides/interactivity.md#avoid-using-closure-variables-in-event-handler).

---

### Getting Started Quickstart (`docs/getting-started/quickstart.md`)

# Quickstart

Let's build a simple interactive Mesop app.

## Before you start

Make sure you've installed Mesop, otherwise please follow the [Installing Guide](./installing.md).

## Starter kit

The simplest way to get started with Mesop is to use the starter kit by running `mesop init`. You can also copy and paste the code.

```python
--8<-- "mesop/examples/starter_kit/starter_kit.py"
```

## Running a Mesop app

Once you've created your Mesop app using the starter kit, you can run the Mesop app by running the following command in your terminal:

```sh
mesop main.py
```

> If you've named it something else, replace `main.py` with the filename of your Python module.

Open the URL printed in the terminal (i.e. http://localhost:32123) in the browser to see your Mesop app loaded.

## Hot reload

If you make changes to the code, the Mesop app should be automatically hot reloaded. This means that you can keep the `mesop` CLI command running in the background in your terminal and your UI will automatically be updated in the browser.

You can also manually trigger a hot reload using a keyboard shortcut:

- **MacOS**: `Cmd + Shift + R`
- **ChromeOS**: `Alt + Shift + R`
- **Windows/Linux**: `Ctrl + Shift + R`

## Next steps

Learn more about the core concepts of Mesop as you learn how to build your own Mesop app:

<a href="../core-concepts" class="next-step">
    Core Concepts
</a>

---

### Web Components Index (`docs/web-components/index.md`)

# Web Components

> Note: Web components are a new experimental feature released under labs and may have breaking changes.

Mesop allows you to define custom components with web components which is a set of web standards that allows you to use JavaScript and CSS to define custom HTML elements.

## Use cases

- **Custom JavaScript** - You can execute custom JavaScript and have simple bi-directional communication between the JavaScript code running in the browser and the Python code running the server.

- **JavaScript libraries** - If you want to use a JavaScript library, you can wrap them with a web component.

- **Rich-client side interactivity** - You can use web components to deliver stateful client-side interactions without a network roundtrip.

## Anatomy of a web component

Mesop web component consists of two parts:

- **Python module** - defines a Python API so that your Mesop app can use the web component seamlessly.
- **JavaScript module** - implements the web component.

## Next steps

Learn how to build your first web component in the [quickstart](./quickstart.md) page.

---

### Web Components Quickstart (`docs/web-components/quickstart.md`)

# Quickstart

> Note: Web components are a new experimental feature released under labs and may have breaking changes.

You will learn how to build your first web component step-by-step, a counter component.

Although it's a simple example, it will show you the core APIs of defining your own web component and how to support bi-directional communication between the Python code running on the server and JavaScript code running on the browser.

### Python module

Let's first take a look at the Python module which defines the interface so that the rest of your Mesop app can call the web component in a Pythonic way.

```python title="counter_component.py"
--8<-- "mesop/examples/web_component/quickstart/counter_component.py"
```

The first part you will notice is the decorator: `@me.web_component`. This annotates a function as a web component and specifies where the corresponding JavaScript module is located, relative to the location of this Python module.

We've defined the function parameters just like a regular Python function.

> Tip: We recommend annotating your parameter with types because Mesop will do runtime validation which will catch type issues earlier.

Finally, we call the function `me.insert_web_component` with the following arguments:

- `name` - This is the web component name and must match the name defined in the JavaScript module.
- `key` - Like all components, web components accept a key which is a unique identifier. See the [component key docs](../components/index.md#component-key) for more info.
- `events` - A dictionary where the key is the event name. This must match a property name, defined in JavaScript. The value is the event handler (callback) function.
- `properties` - A dictionary where the key is the property name that's defined in JavaScript and the value is the property value which is plumbed to the JavaScript component.

> Note: Keys for events and properties must not be "src", "srcdoc", or start with "on" to avoid web security risks.

In summary, when you see a string literal, it should match something on the JavaScript side which is explained next.

### JavaScript module

Let's now take a look at how we implement in the web component in JavaScript:

```javascript title="counter_component.js"
--8<-- "mesop/examples/web_component/quickstart/counter_component.js"
```

In this example, we have used [Lit](https://lit.dev/) which is a small library built on top of web standards in a simple, secure and declarative manner.

> Note: you can write your web components using any web technologies (e.g. TypeScript) or frameworks as long as they conform to the interface defined by your Python module.

#### Properties

The static property named `properties` defines two kinds of properties:

- **Regular properties** - these were defined in the `properties` argument of `insert_web_component`. The property name in JS must match one of the `properties` dictionary key. You also should make sure the Python and JS types are compatible to avoid issues.
- **Event properties** - these were defined in the `events` argument of `insert_web_component`. The property name in JS must match one of the `events` dictionary key. Event properties are always type `String` because the value is a handler id which identifies the Python event handler function.

#### Triggering an event

To trigger an event in your component, let's look at the `_onDecrement` method implementation:

```javascript
this.dispatchEvent(
  new MesopEvent(this.decrementEvent, {
    value: this.value - 1,
  }),
);
```

`this.dispatchEvent` is a [standard web API](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) where a DOM element can emit an event. For Mesop web components, we will always emit a `MesopEvent` which is a class provided on the global object (`window`). The first argument is the event handler id so Mesop knows which Python function to call as the event handler and the second argument is the payload which is a JSON-serializable value (oftentimes an object) that the Python event handler can access.

#### Learn more about Lit

I didn't cover the `render` function which is a [standard Lit method](https://lit.dev/docs/components/rendering/). I recommend reading through [Lit's docs](https://lit.dev/docs/getting-started/) which are excellent ahd have interactive tutorials.

### Using the component

Finally, let's use the web component we defined. When you click on the decrement button, the value will decrease from 10 to 9 and so on.

```python title="counter_component_app.py"
--8<-- "mesop/examples/web_component/quickstart/counter_component_app.py"
```

Even though this was a toy example, you've learned how to build a web component from scratch which does bi-directional communication between the Python server and JavaScript client.

## Next steps

To learn more, read the [API docs](./api.md) or look at the [examples](https://github.com/mesop-dev/mesop/tree/main/mesop/examples/web_component/).

---

### Comparison (`docs/comparison.md`)

# Comparison with Other Python UI Frameworks

This page aims to provide an objective comparison between Mesop and other popular Python-based web application frameworks, specifically Streamlit and Gradio. This is a difficult doc to write but we feel that it's important to explain the differences as this is frequently asked.

 While we believe Mesop offers a [unique philosophy for building UIs](https://mesop-dev.github.io/mesop/blog/2024/05/13/why-mesop/), we strive to be fair and accurate in highlighting the strengths of each framework.

 Because this is a fast-moving space, some of the information may be out of date. Please file an [issue](https://github.com/mesop-dev/mesop/issues/new/choose) and let us know what we should fix.

## Streamlit

Streamlit and Mesop share similar goals in terms of enabling Python developers to rapidly build web apps, particularly for AI use cases.

However, there are some key differences:

### Execution Model

Streamlit executes apps in a [script-like manner](https://docs.streamlit.io/get-started/fundamentals/main-concepts#data-flow) where the entire app reruns on each user interaction. This enables a boilerplate-free UI development model that's easy to get started with, but requires mechanisms like [caching](https://docs.streamlit.io/develop/concepts/architecture/caching) and [fragments](https://docs.streamlit.io/develop/concepts/architecture/fragments) to optimize the performance with this model.

Mesop uses a function-based model commonly found in web frameworks where the program is executed once on server initialization and then the page and component functions are executed in each render loop. This provides regular Python execution semantics because top-level initialization code is executed exactly once.

### Styling and Customization

Streamlit offers pre-styled components with customization primarily through [themes](https://docs.streamlit.io/develop/concepts/configuration/theming), prioritizing consistency and ease of use over flexibility.

In addition to providing Material-themed components, Mesop offers a low-level [Style](./api/style.md) API to configure CSS properties. Mesop provides [limited theming support](./guides/theming.md) with dark theming and doesn't [support theming to other colors](https://github.com/mesop-dev/mesop/issues/669).

### Components

Both Streamlit and Mesop offer a range of standard components (e.g., forms, tables, chat interfaces), with Streamlit providing a larger set of built-in components, especially for data science use cases like data visualization.

Streamlit supports [custom components](https://docs.streamlit.io/develop/concepts/custom-components/intro) rendered in iframes for isolation. It offers first-class support for React components and can accommodate other frameworks through a framework-agnostic template.

Mesop enables creating custom [web components](./web-components/index.md) based on open web standards, facilitating interoperability with components from different frameworks like Lit. Mesop web components are rendered in the same frame as the rest of the Mesop app which provides more flexibility but less isolation.

Streamlit has a more established ecosystem of community-developed components, while Mesop's community and component ecosystem are still developing.

## Gradio

Gradio and Mesop both enable rapid ML/AI app development but with different approaches.

Gradio has a strong focus on creating demos and interfaces for machine learning models and makes it easy to build a UI for a model. Gradio also offers a lower-level abstraction known as [Blocks](https://www.gradio.app/docs/gradio/blocks) for more general web applications.

Mesop, while well-suited for ML/AI use cases, is a more general-purpose framework that can be used for a wide range of web applications.

### Components

Gradio provides a set of pre-built components optimized for common ML inputs and outputs (e.g. image classification, text generation). This makes it fast to set up standard model interfaces. In addition to built-in components, you can create [custom components](https://www.gradio.app/guides/custom-components-in-five-minutes) in Python and JavaScript (Svelte).

Mesop provides general-purpose UI components, which can be used for a variety of layout and UI designs. Higher-level components like the [chat](./components/chat.md) component are [built](https://github.com/mesop-dev/mesop/blob/main/mesop/labs/chat.py) on top of these low-level UI components. This makes it better suited for building custom interfaces, such as the [demo gallery](./demo.md). Mesop also supports creating custom [web components](./web-components/index.md) based on open web standards, facilitating interoperability with components from different frameworks.

### Styling and Customization

Gradio features a robust [theming system](https://www.gradio.app/guides/theming-guide) with prebuilt options and extensive UI customization. It also supports [custom CSS](https://www.gradio.app/guides/custom-CSS-and-JS) via direct string construction.

Mesop offers a statically typed [Style](./api/style.md) API for CSS properties. While it includes [dark theme support](./guides/theming.md), Mesop's theming capabilities are currently limited and does not allow custom color schemes.

### State management

Gradio uses an imperative approach to [state management](https://www.gradio.app/guides/state-in-blocks), coupling state with component updates. State is typically managed through function parameters and return values, which can be straightforward for simple interfaces but may become complex as applications grow.

Mesop adopts a declarative [state management](guides/state-management.md) approach, separating state updates from UI rendering. It uses dataclasses for state, providing type-safety and structure for complex states. This separation allows for more granular control over UI updates but may have a steeper learning curve for beginners.

### Deployment

Gradio makes it easy to share demos via Hugging Face Spaces. Mesop apps can also be deployed on Hugging Face Spaces, but requires a [few more steps](./guides/deployment.md#hugging-face-spaces).

## Conclusion

Both Streamlit and Gradio offer gentle learning curves, making it easy for Python developers to quickly build standard AI applications.

Mesop embraces a declarative UI paradigm, which introduces additional concepts but can provide more flexibility for custom applications.

Ultimately, the best choice depends on your specific use case, desired level of customization, and development preferences. We encourage you to explore each framework to determine which best fits your needs.

---

### Demo (`docs/demo.md`)

---
hide:
  - navigation
  - toc
---

<link rel="stylesheet" href="https://mesop-dev.github.io/mesop/stylesheets/demo.css">
<script src="https://mesop-dev.github.io/mesop/javascript/demo.js"></script>

---

### Faq (`docs/faq.md`)

# Frequently Asked Questions

## General

### What kinds of apps is Mesop suited for?

Mesop is well-suited for ML/AI demos and internal tools because it enables developers without frontend experience to quickly build web apps. For use cases that prioritize developer experience and velocity, Mesop can be a good choice.

Demanding consumer-facing apps, which have strict requirements in terms of performance, custom UI components, and i18n/localization would not be a good fit for Mesop and other UI frameworks may be more suitable.

### How does Mesop compare to other Python UI frameworks?

We have written a [comparison](./comparison.md) doc to answer this question in-depth.

### Is Mesop production-ready?

Dozens of teams at Google have used Mesop to build demos and internal apps.

Although Mesop is pre-v1, we take backwards-compatibilty seriously and avoid backwards incompatible change. This is critical to us because many teams within Google rely on Mesop and we need to not break them.

Occasionally, we will do minor clean-up for our APIs, but we will provide warnings/deprecation notices and provide at least 1 release to migrate to the newer APIs.

### Which modules should I import from Mesop?

Only import from these two modules:

```py
import mesop as me
import mesop.labs as mel
```

All other modules are considered internal implementation details and may change without notice in future releases.

### Is Mesop an official Google product?

No, Mesop is not an official Google product and Mesop is a 20% project maintained by a small core team of Google engineers with contributions from the broader community.

## Deployment

### How do I share or deploy my Mesop app?

The best way to share your Mesop app is to deploy it to a cloud service. You can follow our [deployment guide](./guides/deployment.md) for step-by-step instructions to deploy to Google Cloud Run.

> Note: you should be able to deploy Mesop on any cloud service that takes a container. Please read the above deployment guide as it should be similar steps.

---

### Goals (`docs/goals.md`)

# Goals

I think it's helpful to explicitly state the goals of a project because it provides clarity for not only the development team, but also developers who are evaluating Mesop amongst other options:

1. **Prioritize Python developer experience** - Provide the best possible developer experience for Python engineers with minimal frontend experience. Traditional web UI frameworks (e.g. React) prioritize developer experience, but they are focused on web developers who are familiar with the web ecosystem (e.g. HTML, node.js, etc.).
1. **Rich user interactions** - You should be able to build reasonably sophisticated web applications and demos (e.g. LLM chat) without building custom native components.
1. **Simple deployment** - Make deployment simple by packaging Mesop into a container which can be deployed as a standalone server.

## Examples of applying these goals

- **Web performance**: This doesn't mean other goals like web performance have no weight, but we will consistently rank these goals as higher priorities. For example, we could improve performance by serving static assets via CDN, but this would complicate our deployment. For instance, we'd need to ensure that pushing a new Python server binary and JS static assets happened at the same time, or you can get version skews which can cause cryptic errors.

- **Template vs. code**: Mesop adopts the pattern of UI-as-code instead of using a separate templating language. Our belief is that writing Python code is a significantly better learning curve for our target developers. Rather than making them learn a new templating language (DSL) that they are unfamiliar with, they can write Python code which allows them idiomatic ways of expressing conditional logic and looping.

---

### Showcase (`docs/showcase.md`)

---
hide:
  - navigation
  - toc
---

<link rel="stylesheet" href="https://mesop-dev.github.io/mesop/stylesheets/demo.css">
<script src="https://mesop-dev.github.io/mesop/javascript/showcase.js"></script>

---

### Api Config (`docs/api/config.md`)

# Config

## Overview

Mesop is configured at the application level using environment variables.

## Configuration values

### MESOP_STATIC_FOLDER

> **NOTE:** This feature will be enabled by default in the next Mesop release and set to use `static` as the default static folder. You can still set it to another directory or `""` to disable the feature.

Allows access to static files from the Mesop server.

It is important to know that the specified folder path is relative to the current
working directory where the Mesop command is run. Absolute paths are not allowed.

Example:

In this case, the current working directory is `/srv`, which means Mesop will make
`/srv/static` the static folder.

```bash
cd /srv
MESOP_STATIC_FOLDER=static mesop app/main.py
```

Here are some examples of valid paths. Let's assume the current working directory is
`/srv/`

- `static` becomes `/srv/static`
- `static/` becomes `/srv/static`
- `static/assets` becomes `/srv/static/assets`
- `./static` becomes `/srv/static`
- `./static/` becomes `/srv/static`
- `./static/assets` becomes `/srv/static/assets`

Invalid paths will raise `MesopDeveloperException`. Here are some examples:

- Absolute paths (e.g. `/absolute/path`)
- `.`
- `./`
- `..`
- `../`

### MESOP_STATIC_URL_PATH

This is the base URL path from which files for your specified static folder will be
made viewable.

The static URL path is only recognized if `MESOP_STATIC_FOLDER` is set.

For example, given `MESOP_STATIC_FOLDER=static` and `MESOP_STATIC_URL_PATH=/assets`, the
file `static/js/script.js` can be viewable from the URL path `/assets/js/script.js`.

**Default:** `/static`

### MESOP_STATE_SESSION_BACKEND

Sets the backend to use for caching state data server-side. This makes it so state does
not have to be sent to the server on every request, reducing bandwidth, especially if
you have large state objects.

The backend options available at the moment are `memory`, `file`, `sql`, and `firestore`.

#### memory

Users should be careful when using the `memory` backend. Each Mesop process has their
own RAM, which means cache misses will be common if each server has multiple processes
and there is no session affinity. In addition, the amount of RAM must be carefully
specified per instance in accordance with the expected user traffic and state size.

The safest option for using the `memory` backend is to use a single process with a
good amount of RAM. Python is not the most memory efficient, especially when saving data
structures such as dicts.

The drawback of being limited to a single process is that requests will take longer to
process since only one request can be handled at a time. This is especially problematic
if your application contains long running API calls.

If session affinity is available, you can scale up multiple instances, each running
single processes.

#### file

Users should be careful when using the `file` backend. Each Mesop instance has their
own disk, which can be shared among multiple processes. This means cache misses will be
common if there are multiple instances and no session affinity.

If session affinity is available, you can scale up multiple instances, each running
multiple Mesop processes. If no session affinity is available, then you can only
vertically scale a single instance.

The bottleneck with this backend is the disk read/write performance. The amount of disk
space must also be carefully specified per instance in accordance with the expected user
traffic and state size.

You will also need to specify a directory to write the state data using
`MESOP_STATE_SESSION_BACKEND_FILE_BASE_DIR`.

#### SQL

> NOTE: Setting up and configuring databases is out of scope of this document.

This option uses [SqlAlchemy](https://www.sqlalchemy.org/) to store Mesop state sessions
in supported SQL databases, such as SQLite3 and PostgreSQL. You can also connect to
hosted options, such as GCP CloudSQL.

If you use SQLite3, you cannot use an in-memory database. It has to be a file. This
option has similar pros/cons as the `file` backend. Mesop uses the default configuration
for SQLite3, so the performance will not be optimized for Mesop's usage patterns.
SQLite3 is OK for development purposes.

Using a database like PostgreSQL will allow for better scalability, both vertically and
horizontally, since the database is decoupled from the Mesop server.

The drawback here is that this requires knowledge of the database you're using. At
minimum, you will need to create a database and a database user with the right
privileges. You will also need to create the database table, which you can create
with this script. You will need to update the CONNECTION_URI and TABLE_NAME to match
your database and settings. Also the database user for this script will need privileges
to create tables on the target database.

```python
from sqlalchemy import (
  Column,
  DateTime,
  LargeBinary,
  MetaData,
  String,
  Table,
  create_engine,
)

CONNECTION_URI = "your-database-connection-uri"
# Update to "your-table-name" if you've overridden `MESOP_STATE_SESSION_BACKEND_SQL_TABLE`.
TABLE_NAME = "mesop_state_session"

db = create_engine(CONNECTION_URI)
metadata = MetaData()
table = Table(
  TABLE_NAME,
  metadata,
  Column("token", String(23), primary_key=True),
  Column("states", LargeBinary, nullable=False),
  Column("created_at", DateTime, nullable=False, index=True),
)

metadata.create_all(db)
```

The Mesop server will raise a `sqlalchemy.exc.ProgrammingError` if there is a
database configuration issue.

By default, Mesop will use the table name `mesop_state_session`, but this can be
overridden using `MESOP_STATE_SESSION_BACKEND_SQL_TABLE`.

#### GCP Firestore

This options uses [GCP Firestore](https://cloud.google.com/firestore?hl=en) to store
Mesop state sessions. The `(default)` database has a free tier that can be used for
for small demo applications with low traffic and moderate amounts of state data.

Since Firestore is decoupled from your Mesop server, it allows you to scale vertically
and horizontally without the considerations you'd need to make for the `memory` and
`file` backends.

In order to use Firestore, you will need a Google Cloud account with Firestore enabled.
Follow the instructions for [creating a Firestore in Native mode database](https://cloud.google.com/firestore/docs/create-database-server-client-library#create_a_in_native_mode_database).

Mesop is configured to use the `(default)` Firestore only. The GCP project is determined
using the Application Default Credentials (ADC) which is automatically configured for
you on GCP services, such as Cloud Run.

For local development, you can run this command:

```sh
gcloud auth application-default login
```

If you have multiple GCP projects, you may need to update the project associated
with the ADC:

```sh
GCP_PROJECT=gcp-project
gcloud config set project $GCP_PROJECT
gcloud auth application-default set-quota-project $GCP_PROJECT
```

Mesop leverages Firestore's [TTL policies](https://firebase.google.com/docs/firestore/ttl)
to delete stale state sessions. This needs to be set up using the following command,
otherwise old data will accumulate unnecessarily.

```sh
COLLECTION_NAME=collection_name
gcloud firestore fields ttls update expiresAt \
  --collection-group=$COLLECTION_NAME
```

By default, Mesop will use the collection name `mesop_state_sessions`, but this can be
overridden using `MESOP_STATE_SESSION_BACKEND_FIRESTORE_COLLECTION`.

**Default:** `none`

### MESOP_STATE_SESSION_BACKEND_FILE_BASE_DIR

This is only used when the `MESOP_STATE_SESSION_BACKEND` is set to `file`. This
parameter specifies where Mesop will read/write the session state. This means the
directory must be readable and writeable by the Mesop server processes.

### MESOP_STATE_SESSION_BACKEND_FIRESTORE_COLLECTION

This is only used when the `MESOP_STATE_SESSION_BACKEND` is set to `firestore`. This
parameter specifies which Firestore collection that Mesop will write state sessions to.

**Default:** `mesop_state_sessions`

### MESOP_STATE_SESSION_BACKEND_SQL_CONNECTION_URI

This is only used when the `MESOP_STATE_SESSION_BACKEND` is set to `sql`. This
parameter specifies the database connection string. See the [SqlAlchemy docs for more details](https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls).

**Default:** `mesop_state_session`

### MESOP_STATE_SESSION_BACKEND_SQL_TABLE

This is only used when the `MESOP_STATE_SESSION_BACKEND` is set to `sql`. This
parameter specifies which SQL database table that Mesop will write state sessions to.

**Default:** `mesop_state_session`

### MESOP_PROD_UNREDACTED_ERRORS

Mesop, by default, only shows unredacted errors in debug mode.

If you run in prod mode, the errors are redacted (internal errors are given a generic message "Sorry, there was an error. Please contact the developer.") and the tracebacks are not shown.

If you want to show unredacted errors, including in prod mode, set this to `true`. This may be useful if you're deploying a Mesop app internally and want to get error details even in production.

## Experimental configuration values

These configuration values are experimental and are subject to breaking change, including removal in future releases.

### MESOP_CONCURRENT_UPDATES_ENABLED (DEPRECATED)

!!! danger "Deprecated experimental feature"

      This experimental feature has been removed in Mesop v1. Use `MESOP_WEBSOCKETS_ENABLED` instead.

Allows concurrent updates to state in the same session. If this is not updated, then updates are queued and processed sequentially.

By default, this is not enabled. You can enable this by setting it to `true`.

### MESOP_WEBSOCKETS_ENABLED

!!! warning "Experimental feature"

    This is an experimental feature and is subject to breaking change. Please follow [https://github.com/mesop-dev/mesop/issues/1028](https://github.com/mesop-dev/mesop/issues/1028) for updates.

This uses WebSockets instead of HTTP Server-Sent Events (SSE) as the transport protocol for UI updates. Using WebSockets also changes the Mesop server architecture to be stateful (e.g. meaning a server restart or routing to a different server instance means the user will lose state). If you enable this feature, then you should ensure the following:

- Either a single server instance is used *or* you route a particular user to the same server for a given session (i.e. [session affinity](https://cloud.google.com/run/docs/configuring/session-affinity)).
- Either ensure user state is *not* critical (e.g. it's OK if it's cleared out due to server restart, which could happen at any time on typical cloud infrastructure) or is persisted somewhere (e.g. written to a database).

Enabling this also allows user events to be handled concurrently, which is particularly useful supporting interactivity for long-running event handlers.

By default, this is not enabled. You can enable this by setting it to `true`.

### MESOP_APP_BASE_PATH

This is the base path used to resolve other paths, particularly for serving static files. Must be an absolute path. This is rarely needed because the default of using the current working directory is usually sufficient.

### MESOP_BASE_URL_PATH

Base URL path for serving the Mesop application. Set this if your app is served from a non-root path (e.g. `/myapp`). All HTTP routes, static assets, and API calls will be prefixed with this path. The value must start with a slash but should not include a trailing slash.

### MESOP_HTTP_CACHE_JS_BUNDLE

Enables HTTP caching for the main JS bundle (i.e. prod_bundle.js) served by Mesop. This minimizes unnecessary HTTP requests for the JS bundle.

Note: this does *not* cache other JS files like web components, which are configured separately (see below).

### MESOP_WEB_COMPONENTS_HTTP_CACHE_KEY

Enables HTTP caching for the web component entry point modules.

Note: this does *not* cache JS files imported by the web component entry modules.

### MESOP_COOKIE_SECRET_KEY

!!! warning "Experimental"
    The signed/encrypted cookie API is experimental and may change in future releases.

Secret key required for using Mesop cookies.

- **Signed cookies** (``signed=True``): The value is HMAC-signed so any
  client-side tampering is detected on the next read.  Contents are still
  visible (Base64-encoded) in browser DevTools.
- **Encrypted cookies** (``encrypted=True``): The value is Fernet-encrypted,
  hiding the contents entirely.  Also requires ``pip install cryptography``.

Generate a strong key for production:

```sh
python -c "import secrets; print(secrets.token_hex(32))"
```

Set it before starting Mesop:

```sh
MESOP_COOKIE_SECRET_KEY=your-random-secret mesop main.py
```

Or in a `.env` file:

```sh title=".env"
MESOP_COOKIE_SECRET_KEY=your-random-secret
```

**Security notes:**

- Use a long, random value (32+ bytes of entropy).
- Rotate the key if it is ever compromised — all signed/encrypted cookies
  issued with the old key will become invalid on the next read (users will
  get default instances rather than an error).
- Never commit the key to source control.

**Multi-worker single-use behaviour:**

When ``me.set_cookie()`` is called, Mesop issues a short-lived signed token
that the browser exchanges for the actual ``Set-Cookie`` header.  The token
is single-use *within a single server process*: once a worker has seen a
token's nonce it will reject replays.  In multi-worker deployments, a
replay request routed to a *different* worker would be accepted because each
worker tracks used nonces independently.  In practice this is mitigated by
the CSRF ``Origin`` check on ``/__apply-cookies`` (cross-origin replay is
blocked) and the 60-second token TTL, but it is not cryptographically
guaranteed across workers.  If strict single-use across all workers is
required, ensure session affinity (sticky sessions) so that
``/__apply-cookies`` always returns to the originating worker.

## Usage Examples

### One-liner

You can specify the environment variables before the mesop command.

```sh
MESOP_STATE_SESSION_BACKEND=memory mesop main.py
```

### Use a .env file

Mesop also supports `.env` files. This is nice since you don't have to keep setting
the environment variables. In addition, the variables are only set when the application
is run.

```sh title=".env"
MESOP_STATE_SESSION_BACKEND=file
MESOP_STATE_SESSION_BACKEND_FILE_BASE_DIR=/tmp/mesop-sessions
```

When you run your Mesop app, the .env file will then be read.

```sh
mesop main.py
```

---

### Api Page (`docs/api/page.md`)

# Page API

## Overview

Pages allow you to build multi-page applications by decorating Python functions with `me.page`. To learn more, read the see [multi-pages guide](../guides/multi-pages.md).

## Examples

### Simple, 1-page setup

To create a simple Mesop app, you can use `me.page()` like this:

```python
import mesop as me

@me.page()
def foo():
    me.text("bar")
```

> NOTE: If you do not provide a `path` argument, then it defaults to the root path `"/"`.

### Explicit 1-page setup

This is the same as the above example which explicitly sets the route to `"/"`.

```python
import mesop as me

@me.page(path="/")
def foo():
    me.text("bar")
```

## API

::: mesop.features.page.page
::: mesop.security.security_policy.SecurityPolicy
::: mesop.events.events.LoadEvent

## `on_load`

You may want to do some sort of data-processing when a page is first loaded in a session.

### Simple handler

An `on_load` handler is similar to a regular event handler where you can mutate state.

```python
--8<-- "mesop/examples/docs/on_load.py"
```

### Generator handler

The `on_load` handler can also be a generator function. This is useful if you need to call a slow or streaming API and want to return intermediate results before all the data has been received.

```python
--8<-- "mesop/examples/docs/on_load_generator.py"
```

---

### Api Query Params (`docs/api/query-params.md`)

# Query Params API

## Overview

Query params, also sometimes called query string, provide a way to manage state in the URLs. They are useful for providing deep-links into your Mesop app.

## Example

Here's a simple working example that shows how you can read and write query params.

```py
@me.page(path="/examples/query_params/page_2")
def page_2():
  me.text(f"query_params={me.query_params}")
  me.button("Add query param", on_click=add_query_param)
  me.button("Navigate", on_click=navigate)

def add_query_param(e: me.ClickEvent):
  me.query_params["key"] = "value"

def navigate(e: me.ClickEvent):
  me.navigate("/examples/query_params", query_params=me.query_params)
```

## Usage

You can use query parameters from `me.query_params`, which has a dictionary-like interface, where the key is the parameter name and value is the parameter value.

### Get a query param value

```py
value: str = me.query_params['param_name']
```
This will raise a KeyError if the parameter doesn't exist. You can use `in` to check whether a key exists in `me.query_params`:

```py
if 'key' in me.query_params:
    print(me.query_params['key'])
```

???+ NOTE "Repeated query params"
    If a query param key is repeated, then you will get the _first_ value. If you want all the values use `get_all`.

### Get all values

To get all the values for a particular query parameter key, you can use `me.query_params.get_all`, which returns a sequence of parameter values (currently implemented as a `tuple`).

```py
all_values = me.query_params.get_all('param_name')
```

### Iterate

```py
for key in query_params:
  value = query_params[key]
```

### Set query param

```py
query_params['new_param'] = 'value'
```

### Set repeated query param

```py
query_params['repeated_param'] = ['value1', 'value2']
```

### Delete

```py
del query_params['param_to_delete']
```

## Patterns

### Navigate with existing query params

Here's an example of how to navigate to a new page with query parameters:

```py
def click_navigate_button(e: me.ClickEvent):
    me.query_params['q'] = "value"
    me.navigate('/search', query_params=me.query_params)
```

### Navigate with only new query params

You can also navigate by passing in a dictionary to `query_params` parameter for `me.navigate` if you do _not_ want to keep the existing query parameters.

```py
def click_navigate_button(e: me.ClickEvent):
    me.navigate('/search', query_params={"q": "value})
```

---

### Api Style (`docs/api/style.md`)

## Overview

Mesop provides a Python API that wraps the browser's native CSS [style API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style).

## API

::: mesop.component_helpers.style.Style
::: mesop.component_helpers.style.Border
::: mesop.component_helpers.style.BorderSide
::: mesop.component_helpers.style.Margin
::: mesop.component_helpers.style.Padding

---

### Api Viewport Size (`docs/api/viewport-size.md`)

# Viewport size

## Overview
The viewport size API allows you to access the current viewport size. This can be useful for creating responsive and adaptive designs that are suitable for the user's screen size.

## Examples

### Responsive Design

Responsive design is having a single fluid layout that adapts to all screen sizes.

You can use the viewport size to dynamically set the property of a style. This can be useful if you want to fit two boxes in a row for larger screens (e.g. desktop) and a single box for smaller screens (e.g. mobile) as shown in the example below:

```py
import mesop as me

@me.page()
def page():
    if me.viewport_size().width > 640:
        width = me.viewport_size().width / 2
    else:
        width = me.viewport_size().width
    for i in range(8):
      me.box(style=me.Style(width=width))
```

> Tip: Responsive design tends to take less work and is usually a good starting point.

### Adaptive Design

Adaptive design is having multiple fixed layouts for specific device categories at specific breakpoints, typically viewport width.

For example, oftentimes you will hide the nav component on a mobile device and instead show a hamburger menu, while for a larger device you will always show the nav component on the left side.

```py
import mesop as me

@me.page()
def page():
    if me.viewport_size().width > 480:
        nav_component()
        body()
    else:
        body(show_menu_button=True)
```

> Tip: Adaptive design tends to take more work and is best for optimizing complex mobile and desktop experiences.

## API

::: mesop.features.viewport_size.viewport_size

::: mesop.features.viewport_size.Size

---

### Codelab 2 (`docs/codelab/2.md`)

# DuoChat Codelab Part 2: Building the basic UI

In this section, we'll create the main layout for our DuoChat application, including the header, chat input area, and some basic styling. We'll use Mesop's components and styling system to create an attractive and functional UI.

### Updating the Main Layout

Let's start by updating our `main.py` file to include a more structured layout. We'll use Mesop's `box` component for layout and add some custom styles.

Replace the content of `main.py` with the following:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Run the Mesop app and look at the changes:

```sh
mesop main.py
```

Let's review the changes:

1. We've added a `ROOT_BOX_STYLE` to set the overall layout and background color.
2. We're importing a custom font (Inter) using the `stylesheets` parameter in the `@me.page` decorator.
3. We've created separate functions for the `header` and `chat_input` components.
4. The main layout uses nested `box` components with custom styles to create a centered, responsive design.

### Understanding Mesop's Styling System

Mesop's styling system is based on Python classes that correspond to CSS properties. You can learn more by reading the [Style API docs](../api/style.md).

### Adding Interactivity

Now, let's add some basic interactivity to our chat input. We'll update the `chat_input` function to handle user input:

```python title="main.py"
@me.stateclass
class State:
    input: str = ""

def on_blur(e: me.InputBlurEvent):
    state = me.state(State)
    state.input = e.value

def chat_input():
    state = me.state(State)
    with me.box(
        style=me.Style(
            border_radius=16,
            padding=me.Padding.all(8),
            background="white",
            display="flex",
            width="100%",
        )
    ):
        with me.box(style=me.Style(flex_grow=1)):
            me.native_textarea(
                value=state.input,
                placeholder="Enter a prompt",
                on_blur=on_blur,
                style=me.Style(
                    padding=me.Padding(top=16, left=16),
                    outline="none",
                    width="100%",
                    border=me.Border.all(me.BorderSide(style="none")),
                ),
            )
        with me.content_button(type="icon", on_click=send_prompt):
            me.icon("send")

def send_prompt(e: me.ClickEvent):
    state = me.state(State)
    print(f"Sending prompt: {state.input}")
    state.input = ""
```

Here's what we've added:

1. A `State` class to manage the application state, including the user's input.
2. An `on_blur` function to update the state when the user switches focus from the textarea.
3. A `send_prompt` function that will be called when the send button is clicked.

### Running the Updated Application

Run the application again with `mesop main.py` and navigate to `http://localhost:32123`. You should now see a styled header, a centered layout, and a functional chat input area.

### Troubleshooting

If you're having trouble, compare your code to the [solution](https://github.com/wwwillchen/mesop-duo-chat/tree/2_completed).

### Next Steps

In the next section, we'll dive deeper into state management and implement the model picker dialog.

<a href="../3" class="next-step">
    Managing state & dialogs
</a>

---

### Codelab 3 (`docs/codelab/3.md`)

# DuoChat Codelab Part 3: Managing state & dialogs

In this section, we'll expand our application's state management capabilities and implement a dialog for selecting AI models. We'll use Mesop's state management system and dialog components to create an interactive model selection experience.

### Expanding the State Management

First, let's create a `data_model.py` file with a more comprehensive state structure:

```python title="data_model.py"
from dataclasses import dataclass, field
from typing import Literal
from enum import Enum

import mesop as me

Role = Literal["user", "model"]

@dataclass(kw_only=True)
class ChatMessage:
    role: Role = "user"
    content: str = ""
    in_progress: bool = False

class Models(Enum):
    GEMINI_1_5_FLASH = "Gemini 1.5 Flash"
    GEMINI_1_5_PRO = "Gemini 1.5 Pro"
    CLAUDE_3_5_SONNET = "Claude 3.5 Sonnet"

@dataclass
class Conversation:
    model: str = ""
    messages: list[ChatMessage] = field(default_factory=list)

@me.stateclass
class State:
    is_model_picker_dialog_open: bool = False
    input: str = ""
    conversations: list[Conversation] = field(default_factory=list)
    models: list[str] = field(default_factory=list)
    gemini_api_key: str = ""
    claude_api_key: str = ""

@me.stateclass
class ModelDialogState:
    selected_models: list[str] = field(default_factory=list)
```

This expanded state structure allows us to manage multiple conversations, selected models, and API keys.

### Implementing the Model Picker Dialog

Now, let's implement the model picker dialog in our `main.py` file. First, we'll create a new file called `dialog.py` with the following content, which is based on the [dialog pattern](https://mesop-dev.github.io/mesop/demo/?demo=embed/dialog) from the demo gallery:

```python title="dialog.py"
import mesop as me

@me.content_component
def dialog(is_open: bool):
    with me.box(
        style=me.Style(
            background="rgba(0,0,0,0.4)",
            display="block" if is_open else "none",
            height="100%",
            overflow_x="auto",
            overflow_y="auto",
            position="fixed",
            width="100%",
            z_index=1000,
        )
    ):
        with me.box(
            style=me.Style(
                align_items="center",
                display="grid",
                height="100vh",
                justify_items="center",
            )
        ):
            with me.box(
                style=me.Style(
                    background="#fff",
                    border_radius=20,
                    box_sizing="content-box",
                    box_shadow=(
                        "0 3px 1px -2px #0003, 0 2px 2px #00000024, 0 1px 5px #0000001f"
                    ),
                    margin=me.Margin.symmetric(vertical="0", horizontal="auto"),
                    padding=me.Padding.all(20),
                )
            ):
                me.slot()

@me.content_component
def dialog_actions():
    with me.box(
        style=me.Style(
            display="flex", justify_content="end", margin=me.Margin(top=20)
        )
    ):
        me.slot()
```

Now, let's update our `main.py` file to include the model picker dialog. Copy the following code and replace `main.py` with it:

```
/* Detailed source-code truncated for AI context efficiency. */
```

This updated code adds the following features:

1. A model picker dialog that allows users to select AI models and enter API keys.
2. State management for selected models and API keys.
3. A model switcher in the chat input area that opens the model picker dialog.
4. Disabling of models based on whether the corresponding API key has been entered.

### Running the Updated Application

Run the application again with `mesop main.py` and navigate to `http://localhost:32123`. You should now see a chat input area with a model switcher. Clicking on the model switcher will open the model picker dialog, allowing you to select models and enter API keys.

### Troubleshooting

If you're having trouble, compare your code to the [solution](https://github.com/wwwillchen/mesop-duo-chat/tree/3_completed).

### Next Steps

In the next section, we'll integrate multiple AI models into our application. We'll set up connections to Gemini and Claude, implement model-specific chat functions, and create a way to interact with multiple models.

<a href="../4" class="next-step">
    Integrating AI APIs
</a>

---

### Codelab 4 (`docs/codelab/4.md`)

# DuoChat Codelab Part 4: Integrating AI APIs

In this section, we'll set up connections to Gemini and Claude, implement model-specific chat functions, and create a unified interface for interacting with multiple models simultaneously.

### Setting Up AI Model Connections

First, let's create separate files for our Gemini and Claude integrations.

Create a new file called `gemini.py`:

```python title="gemini.py"
import google.generativeai as genai
from typing import Iterable

from data_model import ChatMessage, State
import mesop as me

generation_config = {
    "temperature": 1,
    "top_p": 0.95,
    "top_k": 64,
    "max_output_tokens": 8192,
}

def configure_gemini():
    state = me.state(State)
    genai.configure(api_key=state.gemini_api_key)

def send_prompt_pro(prompt: str, history: list[ChatMessage]) -> Iterable[str]:
    configure_gemini()
    model = genai.GenerativeModel(
        model_name="gemini-2.5-pro",
        generation_config=generation_config,
    )
    chat_session = model.start_chat(
        history=[
            {"role": message.role, "parts": [message.content]} for message in history
        ]
    )
    for chunk in chat_session.send_message(prompt, stream=True):
        yield chunk.text

def send_prompt_flash(prompt: str, history: list[ChatMessage]) -> Iterable[str]:
    configure_gemini()
    model = genai.GenerativeModel(
        model_name="gemini-flash-latest",
        generation_config=generation_config,
    )
    chat_session = model.start_chat(
        history=[
            {"role": message.role, "parts": [message.content]} for message in history
        ]
    )
    for chunk in chat_session.send_message(prompt, stream=True):
        yield chunk.text
```

Now, create a new file called `claude.py`:

```python title="claude.py"
import anthropic
from typing import Iterable

from data_model import ChatMessage, State
import mesop as me

def call_claude_sonnet(input: str, history: list[ChatMessage]) -> Iterable[str]:
    state = me.state(State)
    client = anthropic.Anthropic(api_key=state.claude_api_key)
    messages = [
        {
            "role": "assistant" if message.role == "model" else message.role,
            "content": message.content,
        }
        for message in history
    ] + [{"role": "user", "content": input}]

    with client.messages.stream(
        max_tokens=1024,
        messages=messages,
        model="claude-3-sonnet-20240229",
    ) as stream:
        for text in stream.text_stream:
            yield text
```

### Updating the Main Application

Now, let's update our `main.py` file to integrate these AI models. We'll update the `page` function, add the `display_conversations` and `display_message` functions and modify the `send_prompt` function to handle multiple models and display their responses:

```
/* Detailed source-code truncated for AI context efficiency. */
```

This updated code adds the following features:

1. A `display_conversations` function that shows the chat history for each selected model.
2. A `display_message` function that renders individual messages with appropriate styling.
3. An updated `send_prompt` function that sends the user's input to all selected models and displays their responses in real-time.

### Handling Streaming Responses

The `send_prompt` function now uses Python generators to handle streaming responses from the AI models. This allows us to update the UI in real-time as the models generate their responses.

### Running the Updated Application

Run the application again with `mesop main.py` and navigate to `http://localhost:32123`. You should now be able to:

1. Select multiple AI models using the model picker dialog.
2. Enter API keys for Gemini and Claude.
3. Send prompts to the selected models.
4. See the responses from multiple models displayed simultaneously.

### Troubleshooting

If you're having trouble, compare your code to the [solution](https://github.com/wwwillchen/mesop-duo-chat/tree/4_completed).

### Next Steps

In the final section, we'll refine the user interface by creating a dedicated conversation page with a multi-column layout for different model responses. We'll also add some finishing touches to improve the overall user experience.

<a href="../5" class="next-step">
    Wrapping it up
</a>

---

### Codelab 5 (`docs/codelab/5.md`)

# DuoChat Codelab Part 5: Wrapping it up

In this section, we'll create a multi-column layout for different model responses, implement user and model message components, add auto-scroll functionality, and finalize the chat experience with multiple models.

### Creating a New Conversation Page

First, let's create a new page for our conversations. Update the `main.py` file to include a new route and function for the conversation page:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Try running the app: `mesop main.py` and now you should navigate to the conversation page once you click the send button.

### Adding Example Prompts

Let's add some example prompts to the home page to help users get started. Add the following functions to `main.py`:

```python title="main.py"
EXAMPLES = [
    "Create a file-lock in Python",
    "Write an email to Congress to have free milk for all",
    "Make a nice box shadow in CSS",
]

def examples_row():
    with me.box(
        style=me.Style(
            display="flex", flex_direction="row", gap=16, margin=me.Margin(bottom=24)
        )
    ):
        for i in EXAMPLES:
            example(i)

def example(text: str):
    with me.box(
        key=text,
        on_click=click_example,
        style=me.Style(
            cursor="pointer",
            background="#b9e1ff",
            width="215px",
            height=160,
            font_weight=500,
            line_height="1.5",
            padding=me.Padding.all(16),
            border_radius=16,
            border=me.Border.all(me.BorderSide(width=1, color="blue", style="none")),
        ),
    ):
        me.text(text)

def click_example(e: me.ClickEvent):
    state = me.state(State)
    state.input = e.key
```

And then uncomment the callsite for `examples_row` in `home_page`.

### Updating the Header

Let's update `header` to allow users to return to the home page when they click on the header box:

```python title="main.py"
def header():
    def navigate_home(e: me.ClickEvent):
        me.navigate("/")
        state = me.state(State)
        state.conversations = []

    with me.box(
        on_click=navigate_home,
        style=me.Style(
            cursor="pointer",
            padding=me.Padding.all(16),
        ),
    ):
        me.text(
            "DuoChat",
            style=me.Style(
                font_weight=500,
                font_size=24,
                color="#3D3929",
                letter_spacing="0.3px",
            ),
        )
```

### Finalizing the Chat Experience

Now that we have a dedicated conversation page with a multi-column layout, let's make some final improvements to enhance the user experience:

1. Navigate to the conversations page at the start of a conversation.
2. Implement auto-scrolling to keep the latest messages in view.

Update the `send_prompt` function in `main.py`:

```python title="main.py"
def send_prompt(e: me.ClickEvent):
    state = me.state(State)
    if not state.conversations:
        me.navigate("/conversation")
        for model in state.models:
            state.conversations.append(Conversation(model=model, messages=[]))
    input = state.input
    state.input = ""

    for conversation in state.conversations:
        model = conversation.model
        messages = conversation.messages
        history = messages[:]
        messages.append(ChatMessage(role="user", content=input))
        messages.append(ChatMessage(role="model", in_progress=True))
        yield
        me.scroll_into_view(key="end_of_messages")
        if model == Models.GEMINI_1_5_FLASH.value:
            llm_response = gemini.send_prompt_flash(input, history)
        elif model == Models.GEMINI_1_5_PRO.value:
            llm_response = gemini.send_prompt_pro(input, history)
        elif model == Models.CLAUDE_3_5_SONNET.value:
            llm_response = claude.call_claude_sonnet(input, history)
        else:
            raise Exception("Unhandled model", model)
        for chunk in llm_response:
            messages[-1].content += chunk
            yield
        messages[-1].in_progress = False
        yield
```

### Running the Final Application

Run the application with `mesop main.py` and navigate to `http://localhost:32123`. You should now have a fully functional DuoChat application with the following features:

1. A home page with example prompts and a model picker.
2. A conversation page with a multi-column layout for different model responses.
3. Real-time streaming of model responses with loading indicators.
4. Auto-scrolling to keep the latest message in view.

### Troubleshooting

If you're having trouble, compare your code to the [solution](https://github.com/wwwillchen/mesop-duo-chat/tree/5_completed).

### Conclusion

Congratulations! You've successfully built DuoChat, a Mesop application that allows users to interact with multiple AI models simultaneously. This project demonstrates Mesop's capabilities for creating responsive UIs, managing complex state, and integrating with external APIs.

Some potential next steps to further improve the application:

1. [Deploy your app](../guides/deployment.md).
2. Add the ability to save and load conversations.
3. Add support for additional AI models or services.

Feel free to experiment with these ideas or come up with your own improvements to enhance the DuoChat experience!

---

### Codelab Cloud Run (`docs/codelab/cloud-run.md`)

This is a video tutorial that goes through the process of building and deploying a Mesop
app to Cloud Run.

Here is a screenshot of the app you will be building.



## Part 1: Building a Gemini powered Mesop app

This video describes how to build a Gemini powered Mesop app.

<iframe width="560" height="315" src="https://www.youtube.com/embed/KUfPiSUJrwE?si=dkVt0rBOQUMapk5D" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## Part 2: Deploying to Cloud Run

This video describes how deploy the Gemini powered Mesop app to Cloud Run.

<iframe width="560" height="315" src="https://www.youtube.com/embed/s9Ag_YNdl0M?si=aj4RqzS2MmWvxFos" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## GitHub Code

View the [source code](https://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/sample-apps/gemini-mesop-cloudrun).

---

### Components Audio (`docs/components/audio.md`)

## Overview

Audio is the equivalent of an [`<audio>` HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio). Audio displays the browser's native audio controls.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=audio" style="height: 80px"></iframe>

```python
--8<-- "demo/audio.py"
```

## API

::: mesop.components.audio.audio.audio

---

### Components Autocomplete (`docs/components/autocomplete.md`)

## Overview

Autocomplete allows the user to enter free text or select from a list of dynamic values
and is based on the [Angular Material autocomplete component](https://material.angular.io/components/autocomplete/overview).

This components only renders text labels and values.

The autocomplete filters by case-insensitively matching substrings of the option label.

Currently, there is no on blur event with this component since the blur event does not
get the selected value on the first blur. Due to this ambiguous behavior, the blur event
has been left out.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=autocomplete" style="height: 200px"></iframe>

```python
--8<-- "demo/autocomplete.py"
```

## API

::: mesop.components.autocomplete.autocomplete.autocomplete
::: mesop.components.autocomplete.autocomplete.AutocompleteOption
::: mesop.components.autocomplete.autocomplete.AutocompleteOptionGroup
::: mesop.components.autocomplete.autocomplete.AutocompleteEnterEvent
::: mesop.components.autocomplete.autocomplete.AutocompleteSelectionChangeEvent
::: mesop.events.InputEvent

---

### Components Badge (`docs/components/badge.md`)

## Overview

Badge decorates a UI component and is oftentimes used for unread message count and is based on the [Angular Material badge component](https://material.angular.io/components/badge/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=badge" style="height: 100px"></iframe>

```python
--8<-- "demo/badge.py"
```

## API

::: mesop.components.badge.badge.badge

---

### Components Box (`docs/components/box.md`)

## Overview

Box is a [content component](../components/index.md#content-components) which acts as a container to group children components and styling them.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=box" style="height: 160px"></iframe>

```python
--8<-- "demo/box.py"
```

## API

::: mesop.components.box.box.box

---

### Components Button (`docs/components/button.md`)

## Overview

Button is based on the [Angular Material button component](https://material.angular.io/components/button/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=button" style="height: 200px"></iframe>

```python
--8<-- "demo/button.py"
```

## API

::: mesop.components.button.button.button
::: mesop.components.button.button.content_button
::: mesop.events.ClickEvent

---

### Components Button Toggle (`docs/components/button_toggle.md`)

## Overview

Button toggle is based on the [Angular Material button toggle component](https://material.angular.io/components/button-toggle/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=button_toggle" style="height: 200px"></iframe>

```python
--8<-- "demo/button_toggle.py"
```

## API

::: mesop.components.button_toggle.button_toggle.button_toggle
::: mesop.components.button_toggle.button_toggle.ButtonToggleButton
::: mesop.components.button_toggle.button_toggle.ButtonToggleChangeEvent

---

### Components Card (`docs/components/card.md`)

## Overview

Card is based on the [Angular Material card component](https://material.angular.io/components/card/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=card" style="height: 200px"></iframe>

```python
--8<-- "demo/card.py"
```

## API

::: mesop.components.card.card.card
::: mesop.components.card_header.card_header.card_header
::: mesop.components.card_content.card_content.card_content
::: mesop.components.card_actions.card_actions.card_actions

---

### Components Chat (`docs/components/chat.md`)

## Overview

Chat component is a quick way to create a simple chat interface. Chat is part of [Mesop Labs](../guides/labs.md).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=chat"></iframe>

```python
--8<-- "demo/chat.py"
```

## API

::: mesop.labs.chat.chat

---

### Components Checkbox (`docs/components/checkbox.md`)

## Overview

Checkbox is a multi-selection form control and is based on the [Angular Material checkbox component](https://material.angular.io/components/checkbox/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=checkbox" style="height: 100px"></iframe>

```python
--8<-- "demo/checkbox.py"
```

## API

::: mesop.components.checkbox.checkbox.checkbox
::: mesop.components.checkbox.checkbox.content_checkbox
::: mesop.components.checkbox.checkbox.CheckboxChangeEvent
::: mesop.components.checkbox.checkbox.CheckboxIndeterminateChangeEvent

---

### Components Code (`docs/components/code.md`)

## Overview

Code is used to render code with syntax highlighting. `code` is a simple wrapper around [markdown](./markdown.md).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=code_demo"></iframe>

```python
--8<-- "demo/code_demo.py"
```

## API

::: mesop.components.code.code.code

---

### Components Date Picker (`docs/components/date_picker.md`)

## Overview

Date picker allows the user to enter free text or select a date from a calendar widget.
and is based on the [Angular Material datapicker component](https://material.angular.io/components/datepicker/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=date_picker" style="height: 200px"></iframe>

```python
--8<-- "demo/date_picker.py"
```

## API

::: mesop.components.datepicker.datepicker.date_picker
::: mesop.components.datepicker.datepicker.DatePickerChangeEvent

---

### Components Date Range Picker (`docs/components/date_range_picker.md`)

## Overview

Date range picker allows the user to enter free text or select a dates from a calendar widget.
and is based on the [Angular Material datapicker component](https://material.angular.io/components/datepicker/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=date_range_picker" style="height: 200px"></iframe>

```python
--8<-- "demo/date_range_picker.py"
```

## API

::: mesop.components.date_range_picker.date_range_picker.date_range_picker
::: mesop.components.date_range_picker.date_range_picker.DateRangePickerChangeEvent

---

### Components Divider (`docs/components/divider.md`)

## Overview

Divider is used to provide visual separation and is based on the [Angular Material divider component](https://material.angular.io/components/divider/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=divider" style="height: 100px"></iframe>

```python
--8<-- "demo/divider.py"
```

## API

::: mesop.components.divider.divider.divider

---

### Components Embed (`docs/components/embed.md`)

## Overview

Embed allows you to embed/[iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) another web site in your Mesop app.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=embed"></iframe>

```python
--8<-- "demo/embed.py"
```

## API

::: mesop.components.embed.embed.embed

---

### Components Expansion Panel (`docs/components/expansion-panel.md`)

## Overview

Expansion panel and is based on the [Angular Material expansion panel component](https://material.angular.io/components/expansion/overview).

This is a useful component for showing a summary header which can be expanded into a more detailed card/panel.

The expansion panels can also be grouped together to create an accordion.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=expansion_panel"></iframe>

```python
--8<-- "demo/expansion_panel.py"
```

## API

::: mesop.components.accordion.accordion.accordion
::: mesop.components.expansion_panel.expansion_panel.expansion_panel
::: mesop.components.expansion_panel.expansion_panel.ExpansionPanelToggleEvent

---

### Components Html (`docs/components/html.md`)

## Overview

The HTML component allows you to add custom HTML to your Mesop app.

There are two modes for rendering HTML components:

- `sanitized` (default), where the HTML is [sanitized by Angular](https://angular.dev/best-practices/security#sanitization-example) to remove potentially unsafe code like `<script>` and `<style>` for web security reasons.
- `sandboxed`, which allows rendering of `<script>` and `<style>` HTML content by using an iframe sandbox.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=html_demo" style="height: 200px"></iframe>

```python
--8<-- "demo/html_demo.py"
```

## API

::: mesop.components.html.html.html

---

### Components Icon (`docs/components/icon.md`)

## Overview

Icon displays a Material icon/symbol and is based on the [Angular Material icon component](https://material.angular.io/components/icon/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=icon" style="height: 60px"></iframe>

```python
--8<-- "demo/icon.py"
```

## API

::: mesop.components.icon.icon.icon

---

### Components Image (`docs/components/image.md`)

## Overview

Image is the equivalent of an [`<img>` HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=image" style="height: 200px"></iframe>

```python
--8<-- "demo/image.py"
```

## API

::: mesop.components.image.image.image

---

### Components Input (`docs/components/input.md`)

## Overview

Input allows the user to type in a value and is based on the [Angular Material input component](https://material.angular.io/components/input/overview).

For longer text inputs, also see [Textarea](./textarea.md)

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=input" style="height: 120px"></iframe>

```python
--8<-- "demo/input.py"
```

## API

::: mesop.components.input.input.input
::: mesop.components.input.input.InputBlurEvent
::: mesop.components.input.input.InputEnterEvent
::: mesop.events.InputEvent

---

### Components Link (`docs/components/link.md`)

## Overview

Link creates an HTML anchor element (i.e. `<a>`) which links to another page.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=link"></iframe>

```python
--8<-- "demo/link.py"
```

## API

::: mesop.components.link.link.link

---

### Components Markdown (`docs/components/markdown.md`)

## Overview

Markdown is used to render markdown text.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=markdown_demo"></iframe>

```python
--8<-- "demo/markdown_demo.py"
```

## API

::: mesop.components.markdown.markdown.markdown

---

### Components Plot (`docs/components/plot.md`)

## Overview

Plot provides a convenient way to render [Matplotlib](https://matplotlib.org/) figures as an image.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=plot"></iframe>

```python
--8<-- "demo/plot.py"
```

## API

::: mesop.components.plot.plot.plot

---

### Components Progress Bar (`docs/components/progress-bar.md`)

## Overview

Progress Bar is used to indicate something is in progress and is based on the [Angular Material progress bar component](https://material.angular.io/components/progress-bar/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=progress_bar" style="height: 60px"></iframe>

```python
--8<-- "demo/progress_bar.py"
```

## API

::: mesop.components.progress_bar.progress_bar.progress_bar
::: mesop.components.progress_bar.progress_bar.ProgressBarAnimationEndEvent

---

### Components Progress Spinner (`docs/components/progress-spinner.md`)

## Overview

Progress Spinner is used to indicate something is in progress and is based on the [Angular Material progress spinner component](https://material.angular.io/components/progress-spinner/overview).


## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=progress_spinner" style="height: 70px"></iframe>

```python
--8<-- "demo/progress_spinner.py"
```

## API

::: mesop.components.progress_spinner.progress_spinner.progress_spinner

---

### Components Radio (`docs/components/radio.md`)

## Overview

Radio is a single selection form control based on the [Angular Material radio component](https://material.angular.io/components/radio/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=radio" style="height: 100px"></iframe>

```python
--8<-- "demo/radio.py"
```

## API

::: mesop.components.radio.radio.radio

::: mesop.components.radio.radio.RadioOption

::: mesop.components.radio.radio.RadioChangeEvent

---

### Components Select (`docs/components/select.md`)

## Overview

Select allows the user to choose from a list of values and is based on the [Angular Material select component](https://material.angular.io/components/select/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=select_demo" style="height: 200px"></iframe>

```python
--8<-- "demo/select_demo.py"
```

## API

::: mesop.components.select.select.select
::: mesop.components.select.select.SelectOption
::: mesop.components.select.select.SelectSelectionChangeEvent
::: mesop.components.select.select.SelectOpenedChangeEvent

---

### Components Sidenav (`docs/components/sidenav.md`)

## Overview

Sidenav is a sidebar typically used for navigation and is based on the [Angular Material sidenav component](https://material.angular.io/components/sidenav/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=sidenav" style="height: 100px"></iframe>

```python
--8<-- "demo/sidenav.py"
```

## API

::: mesop.components.sidenav.sidenav.sidenav

---

### Components Slide Toggle (`docs/components/slide-toggle.md`)

## Overview

Slide Toggle allows the user to toggle on and off and is based on the [Angular Material slide toggle component](https://material.angular.io/components/slide-toggle/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=slide_toggle" style="height: 100px"></iframe>

```python
--8<-- "demo/slide_toggle.py"
```

## API

::: mesop.components.slide_toggle.slide_toggle.slide_toggle
::: mesop.components.slide_toggle.slide_toggle.content_slide_toggle
::: mesop.components.slide_toggle.slide_toggle.SlideToggleChangeEvent

---

### Components Slider (`docs/components/slider.md`)

## Overview

Slider allows the user to select from a range of values and is based on the [Angular Material slider component](https://material.angular.io/components/slider/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=slider" style="height: 120px"></iframe>

```python
--8<-- "demo/slider.py"
```

## API

::: mesop.components.slider.slider.slider
::: mesop.components.slider.slider.SliderValueChangeEvent

---

### Components Table (`docs/components/table.md`)

## Overview

Table allows the user to render an [Angular Material table component](https://material.angular.io/components/table/overview) from a Pandas data frame.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=table"></iframe>

```python
--8<-- "demo/table.py"
```

## API

::: mesop.components.table.table.table

---

### Components Text To Image (`docs/components/text-to-image.md`)

## Overview

Text To Image component is a quick and simple way of getting started with Mesop. Text To Image is part of [Mesop Labs](../guides/labs.md).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=text_to_image"></iframe>

```python
--8<-- "demo/text_to_image.py"
```
## API

::: mesop.labs.text_to_image.text_to_image

---

### Components Text To Text (`docs/components/text-to-text.md`)

## Overview

Text to text component allows you to take in user inputted text and return a transformed text. This is part of [Mesop Labs](../guides/labs.md).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=text_to_text"></iframe>

```python
--8<-- "demo/text_to_text.py"
```

## API

::: mesop.labs.text_to_text.text_to_text

---

### Components Text (`docs/components/text.md`)

## Overview

Text displays text as-is. If you have markdown text, use the [Markdown](./markdown.md) component.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=text"></iframe>

```python
--8<-- "demo/text.py"
```

## API

::: mesop.components.text.text.text

---

### Components Textarea (`docs/components/textarea.md`)

## Overview

Textarea allows the user to type in a value and is based on the [Angular Material input component](https://material.angular.io/components/input/overview) for `<textarea>`.

This is similar to [Input](./input.md), but Textarea is better suited for long text inputs.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=textarea" style="height: 200px"></iframe>

```python
--8<-- "demo/textarea.py"
```

## API

::: mesop.components.input.input.textarea
::: mesop.components.input.input.native_textarea
::: mesop.components.input.input.InputBlurEvent
::: mesop.events.InputEvent

---

### Components Tooltip (`docs/components/tooltip.md`)

## Overview

Tooltip is based on the [Angular Material tooltip component](https://material.angular.io/components/tooltip/overview).

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=tooltip" style="height: 100px"></iframe>

```python
--8<-- "demo/tooltip.py"
```

## API

::: mesop.components.tooltip.tooltip.tooltip

---

### Components Uploader (`docs/components/uploader.md`)

## Overview

Uploader is the equivalent of an [`<input type="file>` HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) except it uses a custom UI that better
matches the look of Angular Material Components.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=uploader" style="height: 200px"></iframe>

```python
--8<-- "demo/uploader.py"
```

## API

::: mesop.components.uploader.uploader.uploader
::: mesop.components.uploader.uploader.content_uploader
::: mesop.components.uploader.uploader.UploadEvent
::: mesop.components.uploader.uploader.UploadedFile

---

### Components Video (`docs/components/video.md`)

## Overview

Video is the equivalent of an [`<video>` HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video). Video displays the browser's native video controls.

## Examples

<iframe class="component-demo" src="https://mesop-dev.github.io/mesop/demo/?demo=video" style="height: 300px"></iframe>

```python
--8<-- "demo/video.py"
```

## API

::: mesop.components.video.video.video

---

### Getting Started Core Concepts (`docs/getting-started/core-concepts.md`)

# Core Concepts

This doc will explain the core concepts of building a Mesop app.

## Hello World app

Let's start by creating a simple Hello World app in Mesop:

```python
--8<-- "mesop/examples/docs/hello_world.py"
```

This simple example demonstrates a few things:

- Every Mesop app starts with `import mesop as me`. This is the only recommended way to import mesop, otherwise your app may break in the future because you may be relying on internal implementation details.
- `@me.page` is a function decorator which makes a function a _root component_ for a particular path. If you omit the `path` parameter, this is the equivalent of `@me.page(path="/")`.
- `app` is a Python function that we will call a __component__ because it's creating Mesop components in the body.

## Components

Components are the building blocks of a Mesop application. A Mesop application is essentially a tree of components.

Let's explain the different kinds of components in Mesop:

- Mesop comes built-in with __native__ components. These are components implemented using Angular/Javascript. Many of these components wrap [Angular Material components](https://material.angular.io/components/).
- You can also create your own components which are called __user-defined__ components. These are essentially Python functions like `app` in the previous example.

## Counter app

Let's build a more complex app to demonstrate Mesop's interactivity features.

```python
--8<-- "mesop/examples/docs/counter.py"
```

This app allows the user to click on a button and increment a counter, which is shown to the user as "Clicks: #".

Let's walk through this step-by-step.

### State

The `State` class represents the application state for a particular browser session. This means every user session has its own instance of `State`.

`@me.stateclass` is a class decorator which is similar to Python's [dataclass](https://docs.python.org/3/library/dataclasses.html) but also sets default values based on type hints and allows Mesop to inject the class as shown next.

> Note: Everything in a state class must be serializable because it's sent between the server and browser.

### Event handler

The `button_click` function is an event handler. An event handler has a single parameter, `event`, which can contain a value (this will be shown in the next example). An event handler is responsible for updating state based on the incoming event.

`me.state(State)` retrieves the instance of the state class for the current session.

### Component

Like the previous example, `main` is a Mesop component function which is decorated with `page` to mark it as a root component for a path.

Similar to the event handler, we can retrieve the state in a component function by calling `me.state(State)`.

> Note: it's _not_ safe to mutate state inside a component function. All mutations must be done in an event handler.

Rendering dynamic values in Mesop is simple because you can use standard Python string interpolation use f-strings:

```python
me.text(f"Clicks: {state.clicks}")
```

The button component demonstrates connecting an event handler to a component. Whenever a click event is triggered by the component, the registered event handler function is called:

```python
me.button("Increment", on_click=button_click)
```

In summary, you've learned how to define a state class, an event handler and wire them together using interactive components.

## What's next

At this point, you've learned all the basics of building a Mesop app. For a step-by-step guide for building a real-world Mesop application, check out the DuoChat Codelab:

<a href="../../codelab" class="next-step">
    DuoChat Codelab
</a>

---

### Getting Started Installing (`docs/getting-started/installing.md`)

# Installing

If you are familiar with setting up a Python environment, then run the following command in your terminal:

```shell
pip install mesop
```

If you're not familiar with setting up a Python environment, follow one of the options below.

## A. Colab (Recommended for beginners)

Colab is a free hosted Jupyter notebook product provided by Google.

Try Mesop on Colab: [](https://colab.research.google.com/github/mesop-dev/mesop/blob/main/notebooks/mesop_colab_getting_started.ipynb)

## B. Command-line

If you'd like to run Mesop locally on the command-line, follow these steps.

**Pre-requisites:** Make sure you have Python version 3.10 or later installed by running:

```sh
python --version
```

If you don't, please [download Python](https://www.python.org/downloads/).

### Create a venv environment

1. **Open the terminal** and navigate to a directory: `cd foo`

2. **Create a virtual environment** by using [venv](https://docs.python.org/3/library/venv.html), which will avoid Python environment issues. Run:

```sh
python -m venv .venv
```

3. **Activate your virtual environment:**

    === "macOS and Linux"

        ```sh
        source .venv/bin/activate
        ```

    === "Windows command prompt"

        ```sh
        .venv\Scripts\activate.bat
        ```

    === "Windows PowerShell"

        ```sh
        .venv\Scripts\Activate.ps1
        ```

Once you've activated the virtual environment, you will see ".venv" at the start of your terminal prompt.

4. **Install mesop:**

```shell
pip install mesop
```

## Upgrading

To upgrade Mesop, run the following command:

```sh
pip install --upgrade mesop
```

If you are using `requirements.txt` or `pyproject.toml` to manage your dependency versions, then you should update those.


## Next steps

Follow the quickstart guide to learn how to create and run a Mesop app:

<a href="../quickstart" class="next-step">
    Quickstart
</a>

---

### Guides Auth (`docs/guides/auth.md`)

# Auth

Mesop is designed to be auth provider agnostic. You can integrate any auth library you prefer, whether it's on the client-side (JavaScript) or server-side (Python). This guide covers several approaches so you can choose the one that fits your deployment.

| Approach | Best for |
|---|---|
| [Google Cloud IAP](#google-cloud-iap) | Apps already on App Engine or Cloud Run |
| [Firebase Authentication](#firebase-authentication) | Apps that need social sign-in or multi-provider auth |
| [Cookies](#cookies) | Persisting session tokens or structured data across requests; includes a [username/password login example](#username-and-password) (experimental) |
| [HTTP Basic Auth](#http-basic-auth) | Internal tools where a browser pop-up is acceptable and no login UI is needed |

> **Important:** Regardless of which approach you choose, always enforce authorization on the server side in your event handlers. Never rely solely on client-visible state to gate privileged actions. See the [state management security guide](./state-management.md#security-best-practices) for more details.

## Google Cloud IAP

[Google Cloud Identity-Aware Proxy (IAP)](https://cloud.google.com/iap) handles authentication entirely at the infrastructure level before any request reaches your Mesop app. This is the simplest approach if you are already deploying to App Engine or Cloud Run — there is no login UI to build and no tokens to manage in your app.

**How it works:**

1. You enable IAP on your App Engine or Cloud Run service in the Google Cloud Console.
2. Google handles the sign-in flow and only forwards authenticated requests to your app.
3. Each forwarded request includes a signed `X-Goog-IAP-JWT-Assertion` header containing the user's identity.
4. Your app verifies this header and trusts the identity inside it.

**Prerequisites:**

- A Google Cloud project with App Engine or Cloud Run
- IAP enabled on your service (see the [IAP docs](https://cloud.google.com/iap/docs/enabling-cloud-run) for setup)
- `google-auth` Python package (`pip install google-auth`)

**Finding your audience string:**

The audience value for JWT verification depends on your service type:

- **App Engine:** `/projects/PROJECT_NUMBER/apps/PROJECT_ID`
- **Cloud Run:** `/projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID`

You can find `PROJECT_NUMBER` and `PROJECT_ID` in the Google Cloud Console. For Cloud Run, get `SERVICE_ID` from the [IAP settings page](https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers).

**Example:**

```python
# requirements.txt: google-auth

import os
import mesop as me
import google.auth.transport.requests
import google.oauth2.id_token
from flask import request

# Set via environment variable in production.
# Format for App Engine:  /projects/PROJECT_NUMBER/apps/PROJECT_ID
# Format for Cloud Run:   /projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID
IAP_AUDIENCE = os.environ["IAP_AUDIENCE"]

_http_request = google.auth.transport.requests.Request()


def verify_iap_jwt(iap_jwt: str) -> dict:
    return google.oauth2.id_token.verify_token(
        iap_jwt,
        _http_request,
        audience=IAP_AUDIENCE,
        certs_url="https://www.gstatic.com/iap/verify/public_key",
    )


@me.stateclass
class State:
    user_email: str


def on_load(e: me.LoadEvent):
    iap_jwt = request.headers.get("X-Goog-IAP-JWT-Assertion")
    if not iap_jwt:
        # Header is absent — IAP is not in front of this request.
        # This can happen during local development. Gate accordingly.
        me.navigate("/unauthorized")
        return
    try:
        payload = verify_iap_jwt(iap_jwt)
    except Exception:
        me.navigate("/unauthorized")
        return
    me.state(State).user_email = payload["email"]


@me.page(path="/", on_load=on_load)
def page():
    state = me.state(State)
    me.text(f"Hello, {state.user_email}")


@me.page(path="/unauthorized")
def unauthorized_page():
    me.text("Access denied.")
```

**Local development tip:** The IAP header is absent when running locally. You can detect this and either skip the check (during development only) or inject a fake header via a local reverse proxy like [Cloud Run proxy](https://cloud.google.com/run/docs/testing/local).

## Firebase Authentication

This guide will walk you through the process of integrating Firebase Authentication with Mesop using a custom web component.

**Pre-requisites:** You will need to create a [Firebase](https://firebase.google.com/) account and project. It's free to get started with Firebase and use Firebase auth for small projects, but refer to the [pricing page](https://firebase.google.com/pricing) for the most up-to-date information.

We will be using three libraries from Firebase to build an end-to-end auth flow:

- [Firebase Web SDK](https://firebase.google.com/docs/web/learn-more): Allows you to call Firebase services from your client-side JavaScript code.
- [FirebaseUI Web](https://github.com/firebase/firebaseui-web): Provides a simple, customizable auth UI integrated with the Firebase Web SDK.
- [Firebase Admin SDK (Python)](https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_the_firebase_admin_sdk): Provides server-side libraries to integrate Firebase services, including Authentication, into your Python applications.

Let's dive into how we will use each one in our Mesop app.

### Web component

The Firebase Authentication web component is a custom component built for handling the user authentication process. It's implemented using [Lit](https://lit.dev/), a simple library for building lightweight web components.

#### JS code

```javascript title="firebase_auth_component.js"
--8<-- "mesop/examples/web_component/firebase_auth/firebase_auth_component.js"
```

**What you need to do:**

- Replace `firebaseConfig` with your Firebase project's config. Read the [Firebase docs](https://firebase.google.com/docs/web/learn-more#config-object) to learn how to get yours.
- Replace the URLs `signInSuccessUrl` with your Mesop page path and `tosUrl` and `privacyPolicyUrl` to your terms and services and privacy policy page respectively.

**How it works:**

- This creates a simple and configurable auth UI using FirebaseUI Web.
- Once the user has signed in, then a sign out button is shown.
- Whenever the user signs in or out, the web component dispatches an event to the Mesop server with the auth token, or absence of it.
- See our [web component docs](../web-components/quickstart.md#javascript-module) for more details.

#### Python code

```python title="firebase_auth_component.py"
--8<-- "mesop/examples/web_component/firebase_auth/firebase_auth_component.py"
```

**How it works:**

- Implements the Python side of the Mesop web component. See our [web component docs](../web-components/quickstart.md#python-module) for more details.

### Integrating into the app

Let's put it all together:

```python title="firebase_auth_app.py"
--8<-- "mesop/examples/web_component/firebase_auth/firebase_auth_app.py"
```

*Note* You must add `firebase-admin` to your Mesop app's `requirements.txt` file

**How it works:**

- The `firebase_auth_app.py` module integrates the Firebase Auth web component into the Mesop app. It initializes the Firebase app, defines the page where the Firebase Auth web component will be used, and sets up the state to store the user's email.
- The `on_auth_changed` function is triggered whenever the user's authentication state changes. If the user is signed in, it verifies the user's ID token and stores the user's email in the state. If the user is not signed in, it clears the email from the state.

### Next steps

Congrats! You've now built an authenticated app with Mesop from start to finish. Read the [Firebase Auth docs](https://firebase.google.com/docs/auth) to learn how to configure additional sign-in options and much more.

## Cookies

!!! warning "Experimental"
    The cookie API is experimental. The interface may change in future releases.

Mesop provides a first-class cookie API so you can persist small pieces of data (session tokens, user preferences, etc.) in the browser without managing raw `Set-Cookie` headers yourself.

!!! note "MESOP_COOKIE_SECRET_KEY required"
    Set [`MESOP_COOKIE_SECRET_KEY`](../api/config.md#mesop_cookie_secret_key) before starting Mesop:

    ```sh
    MESOP_COOKIE_SECRET_KEY=your-random-secret mesop main.py
    ```

    Generate a strong key:

    ```sh
    python -c "import secrets; print(secrets.token_hex(32))"
    ```

### How it works

Mesop event handlers run inside a streaming response (SSE or WebSockets). HTTP response headers — including `Set-Cookie` — are committed to the client *before* the event-handler body executes, so cookies cannot be set directly on the handler response. Instead, cookies are applied via a lightweight two-step protocol:

1. Your event handler calls `me.set_cookie()`.
2. Mesop encodes the pending cookie operations into a short-lived itsdangerous-signed token and sends an `ApplyCookiesCommand` to the browser.  Any server worker that holds the same [`MESOP_COOKIE_SECRET_KEY`](../api/config.md#mesop_cookie_secret_key) can verify and redeem the token.
3. The Mesop client POSTs the token to `/__apply-cookies`, which verifies the signature, marks the token's nonce as used (single-use within a process), and responds with the `Set-Cookie` headers.

This is transparent to your application code.

### `@me.cookieclass` — structured cookies

The recommended API is `@me.cookieclass`, which works like `@me.stateclass` but for cookies.  Fields are JSON-serialised automatically.

```python
import mesop as me

@me.cookieclass
class SessionCookie:
    username: str = ""
    role: str = "guest"
```

#### Reading a cookie

Call `me.cookie(SessionCookie)` inside `on_load` or any event handler.  If the cookie is absent or unparseable, a fresh default instance is returned — no exception is raised.

```python
def on_load(e: me.LoadEvent):
    session = me.cookie(SessionCookie)
    if session.username:
        state = me.state(State)
        state.logged_in = True
        state.username = session.username
```

#### Writing / updating a cookie

Pass a cookieclass instance to `me.set_cookie()`.  The cookie name is derived from the class, and the fields are JSON-serialised automatically.

```python
def on_login(e: me.ClickEvent):
    me.set_cookie(
        SessionCookie(username="alice", role="admin"),
        max_age=3600,   # seconds; omit for a session cookie
    )
```

When `secure` is omitted it auto-detects HTTPS — so the same code works in local HTTP development and in production HTTPS deployments without any extra configuration.

#### Deleting a cookie

```python
def on_logout(e: me.ClickEvent):
    me.delete_cookie(SessionCookie)   # pass the class, not a string
```

### Signed and encrypted cookies

By default `@me.cookieclass` stores the JSON value in plain text — readable in browser DevTools.  For cookies that carry sensitive data you can add tamper-protection or full encryption by setting `signed=True` or `encrypted=True` on the decorator.

| Option | Protection | Contents visible? | Extra dependency |
|---|---|---|---|
| *(default)* | None | Yes (plain JSON) | — |
| `signed=True` | HMAC — tampering detected on read | Yes (Base64) | — |
| `encrypted=True` | Fernet — contents hidden | No | `pip install cryptography` |

```python
# Tamper-proof — contents still visible in DevTools, but any modification
# is detected and the cookie is silently discarded on the next read.
@me.cookieclass(signed=True)
class SessionCookie:
    username: str = ""

# Fully encrypted — contents hidden; requires pip install cryptography.
@me.cookieclass(encrypted=True)
class SessionCookie:
    username: str = ""
```

The read (`me.cookie()`) and write (`me.set_cookie()`) calls are identical regardless of the protection level — the signing or encryption is applied transparently.

### Low-level API

If you need full control over the cookie name and value format — for example when storing an opaque session token generated by an external library — use `me.set_cookie()` with explicit string arguments.

> **When to use which form:** prefer the cookieclass form for structured data.  Use the low-level form only for raw opaque values.

#### `me.cookie()`

```python
token = me.cookie("session_id")         # low-level: raw string value (or "" if absent)
session = me.cookie(SessionCookie)      # high-level: typed cookieclass instance
```

#### `me.set_cookie()`

```python
# Low-level: explicit name + raw string value.
me.set_cookie(
    "session_id",
    "abc123",
    max_age=3600,        # None → session cookie
    path="/",
    domain=None,         # None → current domain
    secure=None,         # None → auto-detect HTTPS (recommended default)
    httponly=True,       # hidden from JavaScript
    samesite="Lax",      # "Lax" | "Strict" | "None"
)

# High-level: cookieclass instance — name and serialisation handled automatically.
me.set_cookie(SessionCookie(username="alice"), max_age=3600)
```

#### `me.delete_cookie()`

```python
me.delete_cookie("session_id")      # low-level: explicit string name
me.delete_cookie(SessionCookie)     # high-level: class lookup
```

### Username and Password

This example combines a username/password login form with cookie-backed sessions. After a successful login the verified username is written to a signed cookie, so the session persists across page refreshes and new tabs — unlike a state-only approach, which is cleared on any hard navigation.

> **Warning:** Rolling your own auth is error-prone. For production apps with many users, prefer a managed solution like [Google Cloud IAP](#google-cloud-iap) or [Firebase Authentication](#firebase-authentication).

**Prerequisites:**

- `werkzeug` (already installed with Mesop — used for password hashing)

**Step 1 — Store hashed passwords**

Never store plaintext passwords. Use `werkzeug.security` to hash them at setup time and store only the hash.

```python
from werkzeug.security import generate_password_hash

# Run this once to generate the hash, then store it securely.
print(generate_password_hash("my-secret-password"))
```

**Step 2 — Full example**

```
/* Detailed source-code truncated for AI context efficiency. */
```

**Multiple protected pages**

For apps with several protected pages, add `on_load=on_load` to each page and check `state.username` at the top:

```python
def _require_login() -> bool:
    state = me.state(State)
    if not state.username:
        _render_login_form()
        return False
    return True

@me.page(path="/dashboard", on_load=on_load)
def dashboard():
    if not _require_login():
        return
    # Protected dashboard content...

@me.page(path="/settings", on_load=on_load)
def settings():
    if not _require_login():
        return
    # Protected settings content...
```

**Security checklist:**

- Never store plaintext passwords — use `generate_password_hash` / `check_password_hash`.
- Never store password hashes in source code or committed files — use a secret manager (e.g. [GCP Secret Manager](https://cloud.google.com/secret-manager), [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/)) or a `.env` file listed in `.gitignore` for local development.
- Use `@me.cookieclass(signed=True)` (or `encrypted=True`) to prevent cookie tampering or content inspection.
- Validate and sanitize all user inputs (see the [state management guide](./state-management.md#validate-input-before-updating-state)).
- For genuine access control, use [Google Cloud IAP](#google-cloud-iap) or [Firebase Authentication](#firebase-authentication).

### Security notes

- **`httponly=True` (default):** The cookie is not accessible to JavaScript, which mitigates XSS-based session theft.  It is still visible in browser DevTools.
- **`secure=None` (default):** Auto-detects HTTPS — `True` in production, `False` on local HTTP.  Pass `secure=True` to enforce HTTPS-only regardless of environment.
- **`samesite="Lax"` (default):** Protects against most CSRF attacks.  Use `"Strict"` for additional protection at the cost of some UX friction on cross-site navigations.
- **Cookie value size:** Browsers limit individual cookies to ~4 KB.  Use server-side session storage (database, Redis, etc.) for larger payloads and store only an opaque session ID in the cookie.
- **Sensitive data:** Use `@me.cookieclass(encrypted=True)` to hide cookie contents, or store only an opaque token and keep sensitive data server-side.
- **Cookie value visibility during apply:** When `me.set_cookie()` is called, the pending cookie values are embedded in the `ApplyCookiesCommand` token that passes through browser JavaScript before being POSTed to `/__apply-cookies`.  The token is HMAC-signed but not encrypted, so the values are Base64-readable in that brief window.  For cookies that must never be visible to JavaScript (e.g. an `httponly` session token whose raw value is sensitive), use `@me.cookieclass(encrypted=True)` — the value is Fernet-encrypted before it ever leaves the server, so the token carries only ciphertext.

---

## HTTP Basic Auth

HTTP Basic Auth lets the browser display a native username/password pop-up with no login page required. It is a good fit for internal tools where:

- The entire app should be protected (no public pages)
- You want the simplest possible setup with zero UI code
- The app is served over HTTPS (required — Basic Auth sends credentials in plaintext otherwise)

> **Warning:** Do not use HTTP Basic Auth over plain HTTP. Always deploy behind HTTPS.

**How it works:**

A WSGI middleware wrapper intercepts every request before it reaches Mesop. If the `Authorization` header is missing or the credentials are wrong, the middleware returns a `401` response immediately and the browser shows its built-in sign-in dialog. Valid requests are passed through to Mesop as normal.

**Prerequisites:**

- `werkzeug` (already installed with Mesop)

**Example:**

```
/* Detailed source-code truncated for AI context efficiency. */
```

Run with Gunicorn:

```
gunicorn --bind 0.0.0.0:8080 'your_module:app'
```

**Security checklist:**

- Only use HTTP Basic Auth behind HTTPS — credentials are only Base64-encoded, not encrypted.
- Store only hashed passwords, never plaintext.
- Never put credentials in source code or committed files. Use your platform's secret management solution — for example [GCP Secret Manager](https://cloud.google.com/secret-manager), [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/), or a `.env` file that is listed in `.gitignore` for local development.
- Consider combining with an IP allowlist at the network/load-balancer level for extra protection.

---

### Guides Debugging (`docs/guides/debugging.md`)

# Debugging

This guide will show you several ways of debugging your Mesop app:

- [Debugging with server logs](#debugging-with-server-logs)
- [Debugging with Chrome DevTools](#debugging-with-chrome-devtools)
- [Debugging with VS Code](#debugging-with-vs-code)

You can use the first two methods to debug your Mesop app both locally and in production, and the last one to debug your Mesop app locally.

## Debugging with server logs

If your Mesop app is not working properly, we recommend checking the server logs first.

If you're running Mesop locally, you can check the terminal. If you're running Mesop in production, you will need to use your cloud provider's console to check the logs.

## Debugging with Chrome DevTools

[Chrome DevTools](https://developer.chrome.com/docs/devtools) is a powerful set of web developer tools built directly into the Google Chrome browser. It can be incredibly useful for debugging Mesop applications, especially when it comes to inspecting the client-server interactions.

Here's how you can use Chrome DevTools to debug your Mesop app:

1. Open your Mesop app in Google Chrome.

1. Right-click anywhere on the page and select "Inspect" or use the keyboard shortcut to open Chrome DevTools:
    - Windows/Linux: Ctrl + Shift + I
    - macOS: Cmd + Option + I

1. To debug general errors:
    - Go to the Console tab.
    - Look for any console error messages.
    - You can also modify the [log levels](https://developer.chrome.com/docs/devtools/console/reference#level) to display Mesop debug logs by clicking on "Default levels" and selecting "Verbose".

1. To debug network issues:
    - Go to the [Network tab](https://developer.chrome.com/docs/devtools/network/overview).
    - Reload your page to see all network requests.
    - Look for any failed requests (they'll be in red).
    - Click on a request to see detailed information about headers, response, etc.

1. For JavaScript errors:
   - Check the Console tab for any error messages.
   - You can set breakpoints in your JavaScript code using the Sources tab.

Remember, while Mesop abstracts away much of the frontend complexity, using these tools can still be valuable for debugging and optimizing your app's performance.

## Debugging with VS Code

VS Code is recommended for debugging your Mesop app, but you can also debug Mesop apps in other IDEs.

**Pre-requisite:** Ensure VS Code is downloaded.

1. Install the [Python Debugger VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy).

2. Include the following in your `.vscode/launch.json`:

```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: Remote Attach",
      "type": "python",
      "request": "attach",
      "connect": { "host": "localhost", "port": 5678 },
      "pathMappings": [
        { "localRoot": "${workspaceFolder}", "remoteRoot": "." }
      ],
      "justMyCode": true
    }
  ]
}
```

3. At the top of your Mesop app (e.g. main.py), including the following snippet to start the debug server:

```py
import debugpy

debugpy.listen(5678)
```

4. Connect to your debug server by going to the Run & Debug tab in VS Code and selecting "Python: Remote Attach".

Congrats you are now debugging your Mesop app!

To learn more about Python debugging in VS code, read VS Code's [Python debugging guide](https://code.visualstudio.com/docs/python/debugging).

---

### Guides Deployment (`docs/guides/deployment.md`)

# Deployment

> We recommend Google Cloud Run or Hugging Face Spaces, which both have a free tier.

This section describes how to run your Mesop application using the following
platforms:

- [Google Cloud Run](#cloud-run)
- [Google App Engine](#app-engine)
- [Docker](#docker)
- [Hugging Face Spaces](#hugging-face-spaces)

If you can run your Mesop app on [Docker](https://www.docker.com/), you should be able
to run it on many other cloud platforms, such as
[Hugging Face Spaces](https://huggingface.co/spaces).

## Example application

Let's start with an example application which will consist of the following files:

- main.py
- requirements.txt

### main.py

This file contains your Mesop application code:

```py title="main.py"
import mesop as me

@me.page(title="Home")
def home():
  me.text("Hello, world")
```

### requirements.txt

This file specifies the Python dependencies needed. You may need to add additional
dependencies depending on your use case.

```title="requirements.txt"
mesop
gunicorn
```

## Cloud Run

We recommend using [Google Cloud Run](https://cloud.google.com/run) because it's easy to
get started and there's a [free tier](https://cloud.google.com/run#pricing).

### Pre-requisites

You will need to create a [Google Cloud](https://cloud.google.com/) account and install the [`gcloud` CLI](https://cloud.google.com/sdk/docs/install). See the official documentation for [detailed instructions](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service#before-you-begin).

### Procfile

Create `Procfile` to configure `gunicorn` to run Mesop.

```title="Procfile"
web: gunicorn --bind :8080 main:me
```

The `--bind: 8080` will run Mesop on port 8080.

The `main:me` syntax is `$(MODULE_NAME):$(VARIABLE_NAME)`: (see [Gunicorn docs](https://docs.gunicorn.org/en/stable/run.html) for more details):

 - Because the Mesop python file is `main.py`, the module name is `main`.
 - By convention, we do `import mesop as me` so the `me` refers to the main Mesop
   library module which is also a callable (e.g. a function) that conforms to WSGI.

### Deploy to Google Cloud Run

In your terminal, go to the application directory, which has the files listed above.

Run the following command:

```sh
gcloud run deploy
```

Follow the instructions and then you should be able to access your deployed app.

### Session Affinity

If you're running Mesop with [MESOP_STATE_SESSION_BACKEND=memory](../api/config.md#mesop_state_session_backend),
then you will want to enable [session affinity](https://cloud.google.com/run/docs/configuring/session-affinity) in order to utilize the `memory` backend efficiently.

The command should be:

```sh
gcloud run services update $YOUR_SERVICE --session-affinity
```

By default gunicorn allocates one worker, but you should double check that gunicorn is
configured correctly for the `memory` backend.

## App Engine

This section describes deployment to [Google App Engine](https://cloud.google.com/appengine) using
their [flexible environments](https://cloud.google.com/appengine/docs/flexible) feature.

### Pre-requisites

You will need to create a [Google Cloud](https://cloud.google.com/) account and install the [`gcloud` CLI](https://cloud.google.com/sdk/docs/install). See the official documentation for [detailed instructions](https://cloud.google.com/appengine/docs/flexible/python/create-app#before-you-begin).

You will also need to run:

```sh
gcloud app create --project=[YOUR_PROJECT_ID]
gcloud components install app-engine-python
```

### app.yaml

Create `app.yaml` to configure App Engine to run Mesop.

```yaml title="app.yaml"
runtime: python
env: flex
entrypoint: gunicorn -b :$PORT main:me

runtime_config:
  operating_system: ubuntu22
  runtime_version: "3.10"

manual_scaling:
  instances: 1

resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10
```

### Deploy to App Engine

In your terminal, go to the application directory, which has the files listed above.

Run the following command:

```sh
gcloud app deploy
```

Follow the instructions and then you should be able to access your deployed app.

## Docker

If you can run your Mesop app on [Docker](https://www.docker.com/), you should be able
to run it on many other cloud platforms.

### Pre-requisites

Make sure [Docker and Docker Compose are installed](https://docs.docker.com/engine/install/).

### Dockerfile

```Docker title="Dockerfile"
--8<-- "docs/assets/hf/example.Dockerfile"
```

### docker-compose.yaml

```title="docker-compose.yaml"
services:
  mesop-app:
    build: .
    ports:
      - "8080:8080"
```

### Run Docker image

In your terminal, go to the application directory, which has the files listed above.

Run the following command:

```sh
docker-compose up -d
```

Alternatively, if you do not want to use Docker Compose, you can run:

```sh
docker build -t mesop-app . && docker run -d -p 8080:8080 mesop-app
```

You should now be able to view your Mesop app at http://localhost:8080.

### Deploying behind a reverse proxy

If you serve your Mesop app behind a reverse proxy at a custom path, set `MESOP_BASE_URL_PATH` to that path. For example, if the proxy routes requests under `/myapppath` to Mesop, run the server with:

```sh
MESOP_BASE_URL_PATH=/myapppath docker-compose up -d
```

All routes, including the UI endpoint and static assets, will then be served from `/myapppath`.

#### Trusting proxy forwarding headers

When Mesop runs behind a reverse proxy (e.g. nginx, a cloud load balancer), the proxy typically communicates the original client's protocol and host via `X-Forwarded-Proto` and `X-Forwarded-Host` headers. Mesop needs to trust these headers so that its [CSRF/CSWSH origin checks](./web-security.md#cross-site-websocket-hijacking-cswsh) compare against the correct external URL.

Mesop **automatically enables proxy-header trust** on the following managed platforms:

- **Google Cloud Run** (detected via `K_SERVICE`)
- **Google App Engine** (detected via `GAE_APPLICATION`)
- **Kubernetes** (detected via `KUBERNETES_SERVICE_HOST`)

For other reverse-proxy setups (e.g. nginx on a VPS), set the environment variable explicitly:

```sh
MESOP_TRUST_PROXY_HEADERS=true docker-compose up -d
```

> **Security warning:** Only set `MESOP_TRUST_PROXY_HEADERS=true` when Mesop is genuinely behind a trusted reverse proxy. In a direct (non-proxied) deployment, enabling this setting allows callers to spoof `X-Forwarded-*` headers and bypass origin-based security checks.


## Hugging Face Spaces

Hugging Face Spaces has a free tier that gives you 2 vCPU and 16GB RAM, which is plenty
for running Mesop applications that leverage generative AI APIs.

### Pre-requisites

This section assumes you already have a free Hugging Face Space account.

### Create new Space

Go to [https://huggingface.co/spaces](https://huggingface.co/spaces) and click
`Create new Space`.



#### Name your app and use Docker SDK

Name the Space `mesop-hello-world` you want and select the `apache-2.0` license.

Next select the Docker SDK with a blank template.



#### CPU Basic and Create Space

Next make sure that you are using the free `CPU Basic` plan. Then click `Create Space`.



### Clone your Hugging Face Space Git Repository

Example command using Git over SSH:

```sh
git clone git@hf.co:spaces/<user-name>/mesop-hello-world
cd mesop-hello-world
```

> Note: You'll need to have an SSH key configured on Hugging Face. See [https://huggingface.co/docs/hub/en/security-git-ssh](https://huggingface.co/docs/hub/en/security-git-ssh).

### Create main.py

This is the same `main.py` file shown earlier, except we need to allow Hugging Face to
iframe our Mesop app.

```py title="main.py"
import mesop as me

@me.page(
  title="Home",
  security_policy=me.SecurityPolicy(
    allowed_iframe_parents=["https://huggingface.co"]
  ),
)
def home():
  me.text("Hello, world")
```

### Create requirements.txt

This file is the same as the generic Docker setup:

```title="requirements.txt"
mesop
gunicorn
```

### Create Dockerfile

This file is the same as the generic Docker setup:

```Docker title="Dockerfile"
--8<-- "docs/assets/hf/example.Dockerfile"
```

### Add app_port in README.md

Next we will need to open port `8080` which we specified in the Dockerfile. This is
done through a config section in the `README.md`.

```md  title="README.md"
---
title: Mesop Hello World
emoji: 🐠
colorFrom: blue
colorTo: purple
sdk: docker
pinned: false
license: apache-2.0
app_port: 8080
---

Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
```

### Deploy to Hugging Face Spaces

The commands to commit your changes and push to the Hugging Face Spaces git repository
are:

```sh
git add -A
git commit -m "Add hello world Mesop app"
git push origin main
```

### View deployed app

Congratulations! You should now be able to view your app on Hugging Face Spaces.

The URL should be something like this:

```
https://huggingface.co/spaces/<user-name>/mesop-hello-world
```

---

### Guides Event Handlers (`docs/guides/event-handlers.md`)

# Event Handlers

Event handlers are a core part of Mesop and enables you to handle user interactions by writing Python functions which are called by the Mesop framework when a user event is received.

## How it works

Let's take a look at a simple example of an event handler:

```py title="Simple event handler"
def counter():
    me.button("Increment", on_click=on_click)

def on_click(event: me.ClickEvent):
    state = me.state(State)
    state.count += 1

@me.stateclass
class State:
    count: int = 0
```

Although this example looks simple, there's a lot going on under the hood.

When the counter function is called, it creates an instance of the button component and binds the `on_click` event handler to it. Because components (and the entire Mesop UI) is serialized and sent to the client, we need a way of serializing the event handler so that when the button is clicked, the correct event handler is called on the server.

We don't actually need to serialize the entire event handler, rather we just need to compute a unique id for the event handler function.

Because Mesop has a stateless architecture, we need a way of computing an id for the event handler function that's stable across Python runtimes. For example, the initial page may be rendered by one Python server, but another server may be used to respond to the user event. This stateless architecture allows Mesop apps to be fault-tolerant and enables simple scaling.

## Types of event handlers

### Regular functions

These are the simplest and most common type of event handlers used. It's essentially a regular Python function which is called by the Mesop framework when a user event is received.

```py title="Regular function"
def on_click(event: me.ClickEvent):
    state = me.state(State)
    state.count += 1
```

### Generator functions

Python Generator functions are a powerful tool, which allow you to `yield` multiple times in a single event handler. This allows you to render intermediate UI states.

```py title="Generator function"
def on_click(event: me.ClickEvent):
    state = me.state(State)
    state.count += 1
    yield
    time.sleep(1)
    state.count += 1
    yield
```

You can learn more about real-world use cases of the generator functions in the [Interactivity guide](./interactivity.md).

???+ info "Always yield at the end of a generator function"
    If you use a `yield` statement in your event handler, then the event handler will be a generator function. You must have a `yield` statement at the end of the event handler (or each return point), otherwise not all of your code will be executed.

### Async generator functions

Python async generator functions allow you to do concurrent work using Python's `async` and `await` language features. If you are using async Python libraries, you can use these types of event handlers.

```py title="Async generator function"
async def on_click(event: me.ClickEvent):
    state = me.state(State)
    state.count += 1
    yield
    await asyncio.sleep(1)
    state.count += 1
    yield
```

For a more complete example, please refer to the [Async section of the Interactivity guide](./interactivity.md#async).

???+ info "Always yield at the end of an async generator function"
    Similar to a regular generator function, an async generator function must have a `yield` statement at the end of the event handler (or each return point), otherwise not all of your code will be executed.

## Patterns

### Reusing event handler logic

You can share event handler logic by extracting the common logic into a separate function. For example, you will often want to use the same logic for the `on_enter` event handler for an input component and the `on_click` event handler for a "send" button component.

```py title="Reusing event handler logic"
def on_enter(event: me.InputEnterEvent):
    state = me.state(State)
    state.value = event.value
    call_api()

def on_click(event: me.ClickEvent):
    # Assumes that state.value has been set by an on_blur event handler
    call_api()

def call_api():
    # Put your common event handler logic here
    pass
```

If you want to reuse event handler logic between generator functions, you can use the [`yield from`](https://docs.python.org/3/whatsnew/3.3.html#pep-380) syntax. For example, let's say `call_api` in the above example is a generator function. You can use `yield from` to reuse the event handler logic:

```py title="Reusing event handler logic for generator functions"
def on_enter(event: me.InputEnterEvent):
    state = me.state(State)
    state.value = event.value
    yield from call_api()

def on_click(event: me.ClickEvent):
    yield from call_api()

def call_api():
    # Do initial work
    yield
    # Do more work
    yield
```
### Boilerplate-free event handlers

If you're building a form-like UI, it can be tedious to write a separate event handler for each form field. Instead, you can use this pattern which utilizes the `key` attribute that's available in most events and uses Python's built-in `setattr` function to dynamically update the state:

```py title="Boilerplate-free event handlers"
def app():
  me.input(label="Name", key="name", on_blur=update_state)
  me.input(label="Address", key="address", on_blur=update_state)

@me.stateclass
class State:
  name: str
  address: str

def update_state(event: me.InputBlurEvent):
  state = me.state(State)
  setattr(state, event.key, event.value)
```

The downside of this approach is that you lose type safety. Generally, defining a separate event handler, although more verbose, is easier to maintain.

## Troubleshooting

### Avoid using closure variables in event handler

One subtle mistake when building a reusable component is having the event handler use a closure variable, as shown in the following example:

```py title="Bad example of using closure variable"
@me.component
def link_component(url: str):
   def on_click(event: me.ClickEvent):
     me.navigate(url)
  return me.button(url, on_click=on_click)

def app():
    link_component("/1")
    link_component("/2")
```

The problem with this above example is that Mesop only stores the last event handler. This is because each event handler has the same id which means that Mesop cannot differentiate between the two instances of the same event handler.

This means that both instances of the link_component will refer to the last `on_click` instance which references the same `url` closure variable set to `"/2"`. This almost always produces the wrong behavior.

Instead, you will want to use the pattern of relying on the key in the event handler as demonstrated in the following example:

```py title="Good example of using key"
@me.component
def link_component(url: str):
   def on_click(event: me.ClickEvent):
     me.navigate(event.key)
  return me.button(url, key=url, on_click=on_click)
```

For more info on using component keys, please refer to the [Component Key docs](../components/index.md#component-key).

## Next steps

Explore advanced interactivity patterns like streaming and async:

<a href="../interactivity" class="next-step">
    Interactivity
</a>

---

### Guides Interactivity (`docs/guides/interactivity.md`)

# Interactivity

This guide continues from the [event handlers guide](./event-handlers.md) and explains advanced interactivity patterns for dealing with common use cases such as calling a slow blocking API call or a streaming API call.

## Intermediate loading state

If you are calling a slow blocking API (e.g. several seconds) to provide a better user experience, you may want to introduce a custom loading indicator for a specific event.

> Note: Mesop has a built-in loading indicator at the top of the page for all events.

```python
--8<-- "mesop/examples/docs/loading.py"
```

In this example, our event handler is a Python generator function. Each `yield` statement yields control back to the Mesop framework and executes a render loop which results in a UI update.

Before the first yield statement, we set `is_loading` to True on state so we can show a spinner while the user is waiting for the slow API call to complete.

Before the second (and final) yield statement, we set `is_loading` to False, so we can hide the spinner and then we add the result of the API call to state so we can display that to the user.

> Tip: you must have a yield statement as the last line of a generator event handler function. Otherwise, any code after the final yield will not be executed.

## Streaming

This example builds off the previous Loading example and makes our event handler a generator function so we can incrementally update the UI.

```python
--8<-- "mesop/examples/docs/streaming.py"
```

## Async

If you want to do multiple long-running operations concurrently, then we recommend you to use Python's `async` and `await`.

```python
--8<-- "mesop/examples/async_await.py"
```

## Troubleshooting

### User input race condition

If you notice a race condition with user input (e.g. [input](../components/input.md) or [textarea](../components/textarea.md)) where sometimes the last few characters typed by the user is lost, you are probably unnecessarily setting the value of the component.

See the following example using this **anti-pattern** :warning::

```py title="Bad example: setting the value and using on_input"
@me.stateclass
class State:
  input_value: str

def app():
  state = me.state(State)
  me.input(value=state.input_value, on_input=on_input)

def on_input(event: me.InputEvent):
  state = me.state(State)
  state.input_value = event.value
```

The problem is that the input value now has a race condition because it's being set by two sources:

1. The server is setting the input value based on state.
2. The client is setting the input value based on what the user is typing.

There's several ways to fix this which are shown below.

#### Option 1: Use `on_blur` instead of `on_input`

You can use the `on_blur` event instead of `on_input` to only update the input value when the user loses focus on the input field.

This is also more performant because it sends much fewer network requests.

```py title="Good example: setting the value and using on_input"
@me.stateclass
class State:
  input_value: str

def app():
  state = me.state(State)
  me.input(value=state.input_value, on_input=on_input)

def on_input(event: me.InputEvent):
  state = me.state(State)
  state.input_value = event.value
```

#### Option 2: Do not set the input value from the server

If you don't need to set the input value from the server, then you can remove the `value` attribute from the input component.

```py title="Good example: not setting the value" hl_lines="7"
@me.stateclass
class State:
  input_value: str

def app():
  state = me.state(State)
  me.input(on_input=on_input)

def on_input(event: me.InputEvent):
  state = me.state(State)
  state.input_value = event.value
```

#### Option 3: Use two separate variables for initial and current input value

If you need set the input value from the server *and* you need to use `on_input`, then you can use two separate variables for the initial and current input value.

```py title="Good example: using two separate variables for initial and current input value" hl_lines="9"
@me.stateclass
class State:
  initial_input_value: str = "initial_value"
  current_input_value: str

@me.page()
def app():
  state = me.state(State)
  me.input(value=state.initial_input_value, on_input=on_input)

def on_input(event: me.InputEvent):
  state = me.state(State)
  state.current_input_value = event.value
```

## Next steps

Learn about layouts to build a customized UI.

<a href="../layouts" class="next-step">
    Layouts
</a>

---

### Guides Labs (`docs/guides/labs.md`)

# Labs

Mesop Labs is built on top of the core Mesop framework and may evolve in the future.

## Using Labs

You will need to add an import statement to use labs:

```python
import mesop.labs as mel
```

The [code inside Mesop Labs](https://github.com/mesop-dev/mesop/tree/main/mesop/labs) is intended to be simple to understand so you can copy and customize it as needed.

---

### Guides Layouts (`docs/guides/layouts.md`)

# Layouts

Mesop takes an unopinionated approach to layout. It does not impose a specific layout on your app so you can build custom layouts. The crux of doing layouts in Mesop is the [Box component](../components/box.md) and using the [Style API](../api/style.md) which are Pythonic wrappers around the CSS layout model.

For most Mesop apps, you will use some combination of these types of layouts:

- [Rows and Columns](#rows-and-columns)
- [Grids](#grids)

## Common layout examples

To interact with the examples below, open the Mesop Layouts Colab: [](https://colab.research.google.com/github/mesop-dev/mesop/blob/main/notebooks/mesop_layout_colab.ipynb)

### Rows and Columns

#### Basic Row

```python title="Basic Row"
def row():
    with me.box(style=me.Style(display="flex", flex_direction="row")):
        me.text("Left")
        me.text("Right")
```

#### Row with Spacing

```python title="Row with Spacing"
def row():
    with me.box(style=me.Style(display="flex", flex_direction="row", justify_content="space-around")):
        me.text("Left")
        me.text("Right")
```

#### Row with Alignment

```python title="Row with Alignment"
def row():
    with me.box(style=me.Style(display="flex", flex_direction="row", align_items="center")):
        me.box(style=me.Style(background="red", height=50, width="50%"))
        me.box(style=me.Style(background="blue", height=100, width="50%"))
```

#### Rows and Columns

```python title="Rows and Columns"
def app():
    with me.box(style=me.Style(display="flex", flex_direction="row", gap=16, height="100%")):
        column(1)
        column(2)
        column(3)

def column(num: int):
    with me.box(style=me.Style(
        flex_grow=1,
        background="#e0e0e0",
        padding=me.Padding.all(16),
        display="flex",
        flex_direction="column",
    )):
        me.box(style=me.Style(background="red", height=100))
        me.box(style=me.Style(background="blue", flex_grow=1))
```

### Grids

#### Side-by-side Grid

```python title="Side-by-side Grid"
def grid():
    # 1fr means 1 fraction, so each side is the same size.
    # Try changing one of the 1fr to 2fr and see what it looks like
    with me.box(style=me.Style(display="grid", grid_template_columns="1fr 1fr")):
        me.text("A bunch of text")
        me.text("Some more text")
```

#### Header Body Footer Grid

```python title="Header Body Footer Grid"
def app():
    with me.box(style=me.Style(
        display="grid",
        grid_template_rows="auto 1fr auto",
        height="100%"
    )):
        # Header
        with me.box(style=me.Style(
            background="#f0f0f0",
            padding=me.Padding.all(24)
        )):
            me.text("Header")

        # Body
        with me.box(style=me.Style(
            padding=me.Padding.all(24),
            overflow_y="auto"
        )):
            me.text("Body Content")
            # Add more body content here

        # Footer
        with me.box(style=me.Style(
            background="#f0f0f0",
            padding=me.Padding.all(24)
        )):
            me.text("Footer")
```

#### Sidebar Layout

```python title="Sidebar Layout"
def app():
    with me.box(style=me.Style(
        display="grid",
        grid_template_columns="250px 1fr",
        height="100%"
    )):
        # Sidebar
        with me.box(style=me.Style(
            background="#f0f0f0",
            padding=me.Padding.all(24),
            overflow_y="auto"
        )):
            me.text("Sidebar")

        # Main content
        with me.box(style=me.Style(
            padding=me.Padding.all(24),
            overflow_y="auto"
        )):
            me.text("Main Content")
```

#### Responsive UI

This is similar to the Grid Sidebar layout above, except on smaller screens, we will hide the sidebar. Try resizing the browser window and see how the UI changes.

Learn more about responsive UI in the [viewport size docs](../api/viewport-size.md).

```python
def app():
    is_desktop = me.viewport_size().width > 640
    with me.box(style=me.Style(
        display="grid",
        grid_template_columns="250px 1fr" if is_desktop else "1fr",
        height="100%"
    )):
        if is_desktop:
          # Sidebar
          with me.box(style=me.Style(
              background="#f0f0f0",
              padding=me.Padding.all(24),
              overflow_y="auto"
          )):
              me.text("Sidebar")

        # Main content
        with me.box(style=me.Style(
            padding=me.Padding.all(24),
            overflow_y="auto"
        )):
            me.text("Main Content")
```

## Learn more

For a real-world example of using these types of layouts, check out the Mesop Showcase app:

- [App](https://mesop-dev.github.io/mesop/showcase/)
- [Code](https://github.com/mesop-dev/mesop/blob/main/showcase/main.py)

To learn more about flexbox layouts (rows and columns), check out:

- [CSS Tricks Guide to Flexbox Layouts](https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-flexbox-properties)
- [MDN Flexbox guide](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox)

To learn more about grid layouts, check out:

- [CSS Tricks Guide to Grid Layouts](https://css-tricks.com/snippets/css/complete-guide-grid/)
- [MDN Grid guide](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids)

---

### Guides Multi Pages (`docs/guides/multi-pages.md`)

# Multi-Pages

You can define multi-page Mesop applications by using the page feature you learned from [Core Concepts](../getting-started/core-concepts.md).

## Multi-page setup

```python
import mesop as me

@me.page(path="/1")
def page1():
    me.text("page 1")

@me.page(path="/2")
def page2():
    me.text("page 2")
```

Learn more about page configuration in the [page API doc](../api/page.md).

## Navigation

If you have multiple pages, you will typically want to navigate from one page to another when the user clicks a button. You can use `me.navigate("/to/path")` to navigate to another page.

**Example:**

```python
--8<-- "mesop/examples/docs/multi_page_nav.py"
```

> Note: you can re-use state across pages. See how the above example uses the `State#count` value across pages.

---

### Guides Performance (`docs/guides/performance.md`)

# Performance

Occasionally, you may run into performance issues with your Mesop app. Here are some tips to help you improve your app's performance.

## Determine the root cause

The first step in debugging performance issues is to identify the cause of the issue. Follow the [Debugging with DevTools guide](./debugging.md#debugging-with-devtools) and use the Console and Network tab to identify the issue.

## Common performance bottlenecks and solutions

### Optimizing state size

If you notice with Chrome DevTools that you're sending very large network payloads between client and server, it's likely that your state is too large.

Because the state object is serialized and sent back and forth between the client and server, you should try to keep the state object reasonably sized. For example, if you store a very large string (e.g. base64-encoded image) in state, then it will degrade performance of your Mesop app.

The following are recommendations to help you avoid large state payloads:

#### Store state in memory

Mesop has a feature that allows you to store state in memory rather than passing the
full state on every request. This may help improve performance when dealing with large
state objects. The caveat is that, storing state in memory contains its own set of
problems that you must carefully consider. See the [config section](../api/config.md#mesop_state_session_backend)
for details on how to use this feature.

If you are running Mesop on a single replica or you can enable [session affinity](https://cloud.google.com/run/docs/configuring/session-affinity), then this is a good option.

#### Store state externally

You can also store state outside of Mesop using a database or a storage service. This is a good option if you have a large amount of state data. For example, rather than storing images in the state, you can store them in a bucket service like [Google Cloud Storage](https://cloud.google.com/storage) and send [signed URLs](https://cloud.google.com/storage/docs/access-control/signed-urls) to the client so that it can directly fetch the images without going through the Mesop server.

### Handling high user load

If you notice that your Mesop app is running slowly when you have many concurrent users, you can try to scale your Mesop app.

#### Increase the number of replicas

To handle more concurrent users, you can scale your Mesop app horizontally by increasing the number of replicas (instances) running your application. This can be achieved through various cloud services that offer autoscaling features:

1. Use a managed service like Google Cloud Run, which automatically scales your app based on traffic. Follow Mesop's [Cloud Run deployment guide](./deployment.md#deploying-to-cloud-run) for details.

2. Manually adjust the number of replicas to a higher number.

3. Tune gunicorn settings. If you're using [gunicorn](https://docs.gunicorn.org/) to serve your Mesop app, you can adjust gunicorn settings to [increase the number of workers](https://docs.gunicorn.org/en/latest/design.html#how-many-workers). This can help to increase the number of concurrent users your Mesop app can handle.

Whichever platform you choose, make sure to configure the replica settings to match your app's performance requirements and budget constraints.

---

### Guides Server Integration (`docs/guides/server-integration.md`)

# Server integration

Mesop allows you to integrate Mesop with other Python web servers like FastAPI or Flask by mounting the Mesop app which is a [WSGI](https://wsgi.readthedocs.io/en/latest/what.html) app.

This enables you to do things like:

- Serve local files (e.g. images)
- Provide API endpoints (which can be called by the web component, etc.)

## API

The main API for doing this integration is the `create_wsgi_app` function.

::: mesop.server.wsgi_app.create_wsgi_app

## FastAPI example

For a working example of using Mesop with FastAPI, please take a look at this repo:
[https://github.com/wwwillchen/mesop-fastapi](https://github.com/wwwillchen/mesop-fastapi)

> Note: you can apply similar steps to use any other web framework that allows you to mount a WSGI app.

---

### Guides State Management (`docs/guides/state-management.md`)

# State Management

State management is a critical element of building interactive apps because it allows you store information about what the user did in a structured way.

## Basic usage

You can register a class using the class decorator `me.stateclass` which is like a dataclass with special powers:

```python
@me.stateclass
class State:
  val: str
```

You can get an instance of the state class inside any of your Mesop component functions by using `me.state`:

```py
@me.page()
def page():
    state = me.state(State)
    me.text(state.val)
```

## Use immutable default values

Similar to [regular dataclasses which disallow mutable default values](https://docs.python.org/3/library/dataclasses.html#mutable-default-values), you need to avoid mutable default values such as list and dict for state classes. Using mutable default values can result in leaking state across sessions which can be a serious privacy issue.

You **MUST** use immutable default values _or_ use dataclasses `field` initializer _or_ not set a default value.

???+ success "Good: immutable default value"
      Setting a default value to an immutable type like str is OK.

      ```py
      @me.stateclass
      class State:
        a: str = "abc"
      ```

???+ failure "Bad: mutable default value"

    The following will raise an exception because dataclasses prevents you from using mutable collection types like `list` as the default value because this is a common footgun.

    ```py
    @me.stateclass
    class State:
      a: list[str] = ["abc"]
    ```

    If you set a default value to an instance of a custom type, an exception will not be raised, but you will be dangerously sharing the same mutable instance across sessions which could cause a serious privacy issue.

    ```py
    @me.stateclass
    class State:
      a: MutableClass = MutableClass()
    ```

???+ success "Good: default factory"

    If you want to set a field to a mutable default value, use default_factory in the `field`  function from the dataclasses module to create a new instance of the mutable default value for each instance of the state class.

    ```py
    from dataclasses import field

    @me.stateclass
    class State:
      a: list[str] = field(default_factory=lambda: ["abc"])
    ```

???+ success "Good: no default value"

    If you want a default of an empty list, you can just not define a default value and Mesop will automatically define an empty list default value.

    For example, if you write the following:

    ```py
    @me.stateclass
    class State:
      a: list[str]
    ```

    It's the equivalent of:

    ```py
    @me.stateclass
    class State:
      a: list[str] = field(default_factory=list)
    ```

## How State Works

`me.stateclass` is a class decorator which tells Mesop that this class can be retrieved using the `me.state` method, which will return the state instance for the current user session.

> If you are familiar with the dependency injection pattern, Mesop's stateclass and state API is essentially a minimalist dependency injection system which scopes the state object to the lifetime of a user session.

Under the hood, Mesop is sending the state back and forth between the server and browser client so everything in a state class must be serializable.

## Serialization

Understanding what can and cannot be serialized in Mesop state is critical to avoiding runtime errors. This section explains which types are supported and provides guidance on handling complex objects.

### Serializable Types

Mesop supports serialization for the following types:

#### Primitive Types
- `int` - Integer numbers
- `float` - Floating-point numbers
- `str` - Strings
- `bool` - Boolean values

```python
@me.stateclass
class State:
  count: int
  temperature: float
  name: str
  is_enabled: bool
```

#### Collections
- `list[T]` - Lists (including nested lists)
- `dict[K, V]` - Dictionaries (including nested dicts)
- `set[T]` - Sets

```python
@me.stateclass
class State:
  items: list[str]
  scores: dict[str, int]
  unique_ids: set[int]
  nested_list: list[list[str]]
  nested_dict: dict[str, dict[str, bool]]
```

#### Date and Time
- `datetime.datetime` - Date and time objects
- `datetime.date` - Date objects

```python
from datetime import datetime, date

@me.stateclass
class State:
  created_at: datetime
  birthday: date
```

#### Binary Data
- `bytes` - Binary data

```python
@me.stateclass
class State:
  file_content: bytes
```

#### Special Types
- `pandas.DataFrame` - Pandas DataFrames (requires pandas to be installed)
- `pydantic.BaseModel` - Pydantic models and subclasses
- `mesop.components.uploader.UploadedFile` - Uploaded files from the uploader component

```python
import pandas as pd
from pydantic import BaseModel

class UserModel(BaseModel):
  name: str
  age: int

@me.stateclass
class State:
  data_frame: pd.DataFrame
  user: UserModel
```

#### Nested State Classes
You can nest dataclasses within your state class, and they will be automatically serialized:

```python
from dataclasses import dataclass

@dataclass
class Address:
  street: str
  city: str

@me.stateclass
class State:
  address: Address
  addresses: list[Address]
```

### Non-Serializable Types

Most types that are not in the serializable types list above cannot be serialized. This includes (but is not limited to):

- Functions, lambdas, and methods
- File handles, sockets, and I/O objects
- Database connections and cursors
- Thread and lock objects
- Protocol buffers and custom complex objects

**If you get a serialization error**, refer to the [serializable types](#serializable-types) section above for supported types.

### Workarounds for Unsupported Types

If you need to work with types that aren't directly serializable, here are some strategies:

#### 1. Break down into primitives

Extract the data you need into serializable primitive types:

```python
# ❌ Bad: storing complex object
@me.stateclass
class State:
  api_response: requests.Response  # Not serializable

# ✅ Good: extract the data you need
@me.stateclass
class State:
  response_text: str
  status_code: int
  headers: dict[str, str]

def on_click(e: me.ClickEvent):
  state = me.state(State)
  response = requests.get("https://api.example.com")
  state.response_text = response.text
  state.status_code = response.status_code
  state.headers = dict(response.headers)
```

#### 2. Serialize to JSON or bytes

Convert complex objects to JSON strings or bytes:

```python
@me.stateclass
class State:
  config_json: str  # Store as JSON string

def on_load(e: me.LoadEvent):
  state = me.state(State)
  complex_config = load_complex_config()
  state.config_json = json.dumps(complex_config)

def use_config():
  state = me.state(State)
  config = json.loads(state.config_json)
  # Use config...
```

#### 3. Use Pydantic models

Pydantic models are serializable and can handle complex nested structures:

```python
from pydantic import BaseModel

class UserProfile(BaseModel):
  name: str
  email: str
  settings: dict[str, str]
  created_at: datetime

@me.stateclass
class State:
  profile: UserProfile  # Pydantic models are serializable

def on_login(e: me.ClickEvent):
  state = me.state(State)
  # Pydantic can serialize most Python types
  state.profile = UserProfile(
    name="Alice",
    email="alice@example.com",
    settings={"theme": "dark"},
    created_at=datetime.now()
  )
```

> **Note**: When using Pydantic models, you get less granular state diffing compared to using primitives directly. This is fine if you don't store a large amount of state data.

### Known Issues

Be aware of these known serialization limitations:

- [https://github.com/mesop-dev/mesop/issues/565](https://github.com/mesop-dev/mesop/issues/565)
- [https://github.com/mesop-dev/mesop/issues/659](https://github.com/mesop-dev/mesop/issues/659)
- [https://github.com/mesop-dev/mesop/issues/814](https://github.com/mesop-dev/mesop/issues/814)

## Multiple state classes

You can use multiple classes to store state for the current user session.

Using different state classes for different pages or components can help make your app easier to maintain and more modular.

```py
@me.stateclass
class PageAState:
    ...

@me.stateclass
class PageBState:
    ...

@me.page(path="/a")
def page_a():
    state = me.state(PageAState)
    ...

@me.page(path="/b")
def page_b():
    state = me.state(PageBState)
    ...
```

Under the hood, Mesop is managing state classes based on the identity (e.g. module name and class name) of the state class, which means that you could have two state classes named "State", but if they are in different modules, then they will be treated as separate state, which is what you would expect.

## Nested State

You can also have classes inside of a state class as long as everything is serializable:

```python
class NestedState:
  val: str

@me.stateclass
class State:
  nested: NestedState

def app():
  state = me.state(State)
```

> Note: you only need to decorate the top-level state class with `@me.stateclass`. All the nested state classes will automatically be wrapped.

### Nested State and dataclass

Sometimes, you may want to explicitly decorate the nested state class with `dataclass` because in the previous example, you couldn't directly instantiate `NestedState`.

If you wanted to use NestedState as a general dataclass, you can do the following:

```python
@dataclass
class NestedState:
  val: str = ""

@me.stateclass
class State:
  nested: NestedState

def app():
  state = me.state(State)
```

> Reminder: because dataclasses do not have default values, you will need to explicitly set default values, otherwise Mesop will not be able to instantiate an empty version of the class.

Now, if you have an event handler function, you can do the following:

```py
def on_click(e):
    response = call_api()
    state = me.state(State)
    state.nested = NestedState(val=response.text)
```

If you didn't explicitly annotate NestedState as a dataclass, then you would get an error instantiating NestedState because there's no initializer defined.

## Security & Best Practices

### Validate input before updating state

Event handlers receive data directly from the browser. Always validate and sanitize that data before writing it into state. Trusting it blindly can lead to corrupt state or unexpected behaviour.

???+ failure "Bad: storing unvalidated input"

    ```python
    def on_submit(e: me.ClickEvent):
        state = me.state(State)
        # Directly copying user-supplied value with no checks
        state.age = int(state.age_input)   # crashes if input is not a number
        state.email = state.email_input    # no format validation
    ```

???+ success "Good: validate before writing to state"

    ```python
    def on_submit(e: me.ClickEvent):
        state = me.state(State)

        # Validate age
        try:
            age = int(state.age_input)
        except ValueError:
            state.error = "Age must be a number."
            return
        if age < 0 or age > 150:
            state.error = "Age must be between 0 and 150."
            return

        # Validate email (basic check)
        if "@" not in state.email_input or "." not in state.email_input:
            state.error = "Enter a valid email address."
            return

        state.age = age
        state.email = state.email_input
        state.error = ""
    ```

This is especially important for fields that end up in database queries, file paths, or external API calls.

### Validate state before sending to external services

State holds what the user submitted across one or more interactions. Before passing state values to a database, API, or any external service, validate them again at the point of use. Never assume that because data is in state it is already safe.

???+ failure "Bad: using state values directly in a database call"

    ```python
    def on_save(e: me.ClickEvent):
        state = me.state(State)
        # No validation — anything could be in state.username
        db.execute("INSERT INTO users (name) VALUES (?)", (state.username,))
    ```

???+ success "Good: re-validate at the service boundary"

    ```python
    import re

    def on_save(e: me.ClickEvent):
        state = me.state(State)

        username = state.username.strip()
        if not username or not re.match(r"^[a-zA-Z0-9_]{3,30}$", username):
            state.error = "Username must be 3–30 alphanumeric characters."
            return

        db.execute("INSERT INTO users (name) VALUES (?)", (username,))
    ```

Use an ORM or parameterised queries to avoid SQL injection. If you call an external HTTP API, validate that URLs or IDs embedded in state are well-formed before constructing the request.

### Be careful what you store in state

Mesop serializes state and sends it between the server and the browser on every interaction. This has two important consequences:

**State is visible to the client.** The serialized state travels over the network and can be inspected by a determined user. Do not store secrets, private keys, raw passwords, or other sensitive credentials in state.

???+ failure "Bad: storing a secret in state"

    ```python
    @me.stateclass
    class State:
        api_key: str          # ❌ Will be sent to the browser
        password_hash: str    # ❌ Leaks implementation details
    ```

???+ success "Good: keep secrets server-side"

    ```python
    import os

    # Read once at startup and keep in server memory, not in state
    _API_KEY = os.environ["MY_API_KEY"]

    @me.stateclass
    class State:
        is_authenticated: bool   # ✅ Only the outcome goes in state

    def call_api():
        # Use the module-level constant, never state
        return requests.get("https://api.example.com", headers={"Authorization": _API_KEY})
    ```

**State size affects performance.** Everything in state is serialized and sent on every round-trip. Avoid storing large blobs, full API responses, or entire datasets. Store only the minimum data needed to render the UI. See the [performance guide](./performance.md#optimizing-state-size) for details.

???+ failure "Bad: storing a full API response"

    ```python
    @me.stateclass
    class State:
        raw_response: str   # ❌ Could be megabytes of JSON
    ```

???+ success "Good: store only what the UI needs"

    ```python
    @me.stateclass
    class State:
        result_count: int
        result_titles: list[str]   # ✅ Just the fields displayed
    ```

**State is not a substitute for authentication.** State is scoped to a user session, but Mesop itself does not authenticate users. Do not rely on a flag like `state.is_admin = True` as your sole access control mechanism. Enforce authorization on the server side (e.g., check the session token in your event handler) before performing privileged operations.

???+ failure "Bad: trusting state for authorization"

    ```python
    def on_delete(e: me.ClickEvent):
        state = me.state(State)
        if state.is_admin:   # ❌ Client can manipulate this
            db.delete_all()
    ```

???+ success "Good: verify authorization server-side"

    ```python
    def on_delete(e: me.ClickEvent):
        state = me.state(State)
        user = get_current_user(state.session_token)
        if not user or not user.has_permission("delete"):
            state.error = "Permission denied."
            return
        db.delete_all()
    ```

**State does not persist across sessions.** When a user's session ends (e.g., they close the tab or the server restarts), state is lost. Do not use state as durable storage. Persist anything important — user preferences, saved work, etc. — to a database or other backend store before the session ends.

## Tips

### State performance issues

Take a look at the [performance guide](./performance.md#optimizing-state-size) to learn how to identify and fix State-related performance issues.

## Next steps

Event handlers complement state management by providing a way to update your state in response to user interactions.

<a href="../event-handlers" class="next-step">
    Event handlers
</a>

---

### Guides Static Assets (`docs/guides/static-assets.md`)

# Static Assets

Mesop allows you to specify a folder for storing static assets that will be served by
the Mesop server.

This feature provides a simple way to serving images, favicons, CSS stylesheets, and
other files without having to rely on CDNs, external servers, or mounting Mesop onto
FastAPI/Flask.

## Enable a static folder

This feature can be enabled using environment variables.

- [MESOP_STATIC_FOLDER](../api/config.md#mesop_static_folder)
- [MESOP_STATIC_URL_PATH](../api/config.md#mesop_static_url_path)

Full descriptions of these two settings can be found on the [config page](../api/config.md).

### Enabling a static folder named "assets"

This will make the files in the `assets` directory accessible from the Mesop server
at `/static`.

Mesop will look for the `assets` directory relative to your current working directory.
In this case, `/some/path/mesop-app/assets`.

```bash
cd /some/path/mesop-app
MESOP_STATIC_FOLDER=assets mesop main.py
```

Here is another example:

Mesop will look for the `assets` directory relative to your current working directory.
In this case, `/some/path/assets`.

```bash
cd /some/path
MESOP_STATIC_FOLDER=assets mesop mesop-app/main.py
```
### Enabling a static folder named "assets" and URL path of /assets

This will make the files in the `assets` directory accessible from the Mesop server
at `/assets`. For example: `https://example.com/assets`.

```bash
MESOP_STATIC_FOLDER=assets MESOP_STATIC_URL_PATH=/assets mesop main.py
```

### Using a .env file

You can also specify the environment variables in a `.env` file. This file should be
placed in the same directory as the `main.py` file.

``` title=".env"
MESOP_STATIC_FOLDER=assets
MESOP_STATIC_URL_PATH=/assets
```

Then you can run the Mesop command like this:

```bash
mesop main.py
```

## Example use cases

Here are a couple examples that use the static assets feature.

### Add a logo

This example shows you how to load an image to use as a logo for your app.

Let's assume you have a directory like this:

- static/logo.png
- main.py
- requirements.txt

Then you can reference your logo in your Mesop app like this:

```python title="main.py"
import mesop as me

@me.page()
def foo():
  me.image(src="/static/logo.png")
```

### Use a custom favicon

This example shows you how to use a custom favicon in your Mesop app.

Let's assume you have a directory like this:

- static/favicon.ico
- main.py
- requirements.txt

If you have a static folder enabled, Mesop will look for a `favicon.ico` file in your
static folder. If the file exists, Mesop will use your custom favicon instead of the
default Mesop favicon.

### Load a Tailwind stylesheet

This example shows you how to use [Tailwind CSS](https://tailwindcss.com/) with Mesop.

Let's assume you have a directory like this:

- static/tailwind.css
- tailwind_input.css
- tailwind.config.js
- main.py
- requirements.txt


You can import the CSS into your page using the `stylesheets` parameter on `@me.page`.

```python title="main.py"
import mesop as me

@me.page(stylesheets=["/static/tailwind.css"])
def foo():
  with me.box(classes="bg-gray-800"):
    me.text("Mesop with Tailwind CSS.")
```

Tailwind is able to extract the CSS properties from your Mesop main.py file. This does
not work for all cases. If you are dynamically generating CSS properties using string concatenation/formatting, then Tailwind may not be able to determine which properties
to include. In that case, you may need to manually add these classes to the safelist.

```js title="tailwind.config.js"
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["main.py"],
  theme: {
    extend: {},
  },
  plugins: [],
  safelist: [],
};
```

This is just the base Tailwind input file.

```css title="tailwind_input.css"
@tailwind base;
@tailwind components;
@tailwind utilities;
```

The command to generate the output Tailwind CSS is:

```bash
# This assumes you have the tailwindcss CLI installed. If not, see
# https://tailwindcss.com/docs/installation
npx tailwindcss -i ./tailwind_input.css -o ./static/tailwind.css
```

---

### Guides Testing (`docs/guides/testing.md`)

# Testing

This guide covers the recommended approach for testing Mesop applications using Playwright, a popular browser automation and testing framework.

However, you can use any other testing framework and there's no official testing framework for Mesop.

## Testing Philosophy

Because Mesop is a full-stack UI framework we recommend doing integration tests to cover critical app functionality. We also recommend separating core business logic into separate Python modules, which do not depend on Mesop. This way you can easily unit test your business logic as well as reuse the business logic as part of scripts or other binaries besides Mesop.

## Playwright Example

We will walk through [mesop-playwright-testing repo](https://github.com/wwwillchen/mesop-playwright-testing) which contains a simple Mesop counter app and a Playwright test written in TypeScript (node.js). Although [Playwright has a Python flavor](https://playwright.dev/python/), we recommend using the Playwright node.js flavor because it has better testing support. Even if you're not familiar with JavaScript or TypeScript, it's extremely easy to write Playwright tests because you can [generate your tests](https://playwright.dev/docs/codegen-intro) by clicking through the UI.

The testing example repo's [README.md](https://github.com/wwwillchen/mesop-playwright-testing?tab=readme-ov-file#mesop-playwright-testing-example) contains instructions for setting up your environment and running the tests.

### Playwright config

The playwright configuration used is similar to the [default one](https://playwright.dev/docs/intro#installing-playwright), however we change a few configurations specific for Mesop.

For example, in [`playwright.config.ts`](https://github.com/wwwillchen/mesop-playwright-testing/blob/main/playwright.config.ts), we configure Mesop as the local dev server:

```ts
  webServer: {
    command: "mesop app.py",
    url: "http://127.0.0.1:32123",
    reuseExistingServer: !process.env.CI,
  },
```

This will launch the Mesop app server at the start of the tests.

We also added the following configurations to make writing and debugging tests easier:

```ts
  use: {
    /* Base URL to use in actions like `await page.goto('/')`. */
    baseURL: "http://127.0.0.1:32123",

    /* See https://playwright.dev/docs/trace-viewer */
    trace: "retain-on-failure",

    // Capture screenshot after each test failure.
    screenshot: "on",

    // Retain video on test failure.
    video: "retain-on-failure",
  },
```

### Running and debugging a test

Try changing the test so that it fails. For example, in [`app.spec.ts`](https://github.com/wwwillchen/mesop-playwright-testing/blob/main/tests/app.spec.ts) change `"Count=1"` to `"Count=2"` and then run the tests: `npx playwright test`.

The test will fail (as expected) and a new browser page should be opened with the test failure information. You can click on the failing test and view the screenshot, video and trace. This is very helpful in figuring out why a test failed.

### Writing a test

As mentioned above, it's very easy to write Playwright tests because you can [generate your tests](https://playwright.dev/docs/codegen-intro) by clicking through the UI. Even if you're not familiar with JavaScript/TypeScript, you will be able to generate most of the test code by clicking through the UI and copying the generated test code.

???+ tip "Use the [Playwright VS Code extension](https://playwright.dev/docs/getting-started-vscode)"

    You can use the Playwright VS Code extension to directly generate test code in your file. You can also run and debug tests from VS Code as well.

## Learn more

We recommend reading [Playwright's docs](https://playwright.dev) which are easy to follow and contain much more information on writing browser tests.

You can also look at Mesop's [own tests](https://github.com/mesop-dev/mesop/tree/main/mesop/tests/e2e) written with Playwright.

---

### Guides Theming (`docs/guides/theming.md`)

# Theming

Mesop has early-stage support for theming so you can support light theme and dark theme in a Mesop application.

## Dark Theming

For an actual example of using Mesop's theming API to support light theme and dark theme, we will look at the labs [chat component](../components/chat.md) which itself is written all in Python built on top of lower-level Mesop components.

### Theme toggle button

Inside the chat component, we've defined an icon button to toggle the theme so users can switch between light and dark theme:

```py
def toggle_theme(e: me.ClickEvent):
    if me.theme_brightness() == "light":
      me.set_theme_mode("dark")
    else:
      me.set_theme_mode("light")

with me.content_button(
    type="icon",
    style=me.Style(position="absolute", right=0),
    on_click=toggle_theme,
):
    me.icon("light_mode" if me.theme_brightness() == "dark" else "dark_mode")
```

### Using theme colors

You could define custom style logic to explicitly set the color based on the theme:

```py
def container():
  me.box(
    style=me.Style(
      background="white" if me.theme_brightness() == "light" else "black"
    )
  )
```

But this would be pretty tedious, so you can use theme variables like this:


```py
def container():
  me.box(style=me.Style(background=me.theme_var("background")))
```

This will use the appropriate background color for light theme and dark theme.

### Default theme mode

Finally, we want to use the default theme mode to "system" which means we will use the user's preferences for whether they want dark theme or light theme. For many users, their operating systems will automatically switch to dark theme during night time.

> Note: Mesop currently defaults to light theme mode but will eventually default to system theme mode in the future.

On our demo page with the chat component, we have a page [on_load](../api/page.md#on_load) event handler defined like this:

```py
def on_load(e: me.LoadEvent):
  me.set_theme_mode("system")
```

## Theme Density

You can set the visual density of the Material components. By default, Mesop uses the least visually dense setting, i.e.

```py
me.set_theme_density(0) # 0 is the least dense
```

You can configure the density as an integer from 0 (least dense) to -4 (most dense). For example, if you want a medium-dense UI, you can do the following:

```py
def on_load(e: me.LoadEvent):
  me.set_theme_density(-2) # -2 is more dense than the default


@me.page(on_load=on_load)
def page():
  ...
```

## API

::: mesop.features.theme.set_theme_density
::: mesop.features.theme.set_theme_mode
::: mesop.features.theme.theme_brightness
::: mesop.features.theme.theme_var
::: mesop.features.theme.ThemeVar

---

### Guides Web Security (`docs/guides/web-security.md`)

# Web Security

## Static file serving

Mesop allows serving JS and CSS files located within the Mesop app's file subtree to support [web components](../web-components/index.md).

**Security Warning:** Do not place any sensitive or confidential JS and CSS files in your Mesop project directory. These files may be inadvertently exposed and served by the Mesop web server, potentially compromising your application's security.

## JavaScript Security

At a high-level, Mesop is built on top of Angular which provides [built-in security protections](https://angular.io/guide/security) and Mesop configures a strict [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).

Specifics:

- Mesop APIs do not allow arbitrary JavaScript execution in the main execution context. For example, the [markdown](../components/markdown.md) component sanitizes the markdown content and removes active HTML content like JavaScript.
- Mesop's default Content Security Policy prevents arbitrary JavaScript code from executing on the page unless it passes [Angular's Trusted Types](https://angular.io/guide/security#enforcing-trusted-types) polices.

## Iframe Security

To prevent [clickjacking](https://owasp.org/www-community/attacks/Clickjacking), Mesop apps, when running in prod mode (the default mode used when [deployed](../guides/deployment.md)), do not allow sites from any other origins to iframe the Mesop app.

> Note: pages from the same origin as the Mesop app can always iframe the Mesop app.

If you want to allow a trusted site to iframe your Mesop app, you can explicitly allow list the [sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors#sources) which can iframe your app by configuring the security policy for a particular page.

### Example

```py
import mesop as me


@me.page(
  path="/allows_iframed",
  security_policy=me.SecurityPolicy(
    allowed_iframe_parents=["https://google.com"],
  ),
)
def app():
  me.text("Test CSP")
```

You can also use wildcards to allow-list multiple subdomains from the same site, such as: `https://*.example.com`.

## Cross Origin Opener Policy

Mesop sets this value to `unsafe-none`, which is the default value. It is recommended to set this to `same-origin` to ensure process isolation from random domains. In most cases, your Mesop app should run without any issues.

For more information, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy) and [XS Leaks Wiki](https://xsleaks.dev/).

## Cross-site WebSocket Hijacking (CSWSH)

Unlike regular HTTP requests, browsers do **not** enforce same-origin policy for WebSocket connections. This means a malicious page on another origin could open a WebSocket to your Mesop server and interact with it on behalf of a logged-in user.

To prevent this, Mesop validates the `Origin` header on every WebSocket upgrade request and rejects connections whose origin does not match the server's own URL. This check mirrors the existing CSRF protection on the SSE (`POST /__ui__`) endpoint.

The check is skipped in [debug mode](./debugging.md) to support environments like Colab where the UI and the server may run on different origins.

### Deployments behind a reverse proxy

If your app is deployed behind a reverse proxy (Cloud Run, App Engine, Kubernetes, nginx, etc.), the server's apparent URL may differ from the external-facing URL seen by browsers. In that case the origin check could incorrectly reject legitimate connections.

Mesop solves this by reading the `X-Forwarded-Proto` and `X-Forwarded-Host` headers set by the proxy, but only when proxy-header trust is enabled. See [Trusting proxy forwarding headers](./deployment.md#trusting-proxy-forwarding-headers) in the deployment guide for details.

## API

You can configure the security policy at the page level. See [SecurityPolicy on the Page API docs](../api/page.md#mesop.security.security_policy.SecurityPolicy).

---

### Internal Architecture (`docs/internal/architecture.md`)

# Architecture

This doc is meant to provide an overview of how Mesop is structured as a framework. It's not necessary to know this information as a developer using Mesop, but if you're _developing_ Mesop's codebase, then this is helpful in laying out the lay of the land.

At the heart of Mesop is two subsystems:

- A Python server, running on top of [Flask](https://flask.palletsprojects.com/en/).
- A Web client, built on [Angular](https://angular.dev/) framework, which wraps various [Angular components](https://angular.dev/guide/components), particularly [Angular Material components](https://material.angular.io/components/).

## Terminology

- *Downstream* - This refers to the synced version of Mesop inside of Google (["google3 third-party"](https://opensource.google/documentation/reference/thirdparty)). Although almost all the code is shared between the open-source and internal version of Mesop, there's many considerations in maintaining parity between these two versions, particularly with regards to [toolchain](./toolchain.md).
- *Component* vs *component instance* - A component typically refers to the Python factory function that creates a component instance (e.g. `me.box()`). A component instance refers to a specific component created by a component function and is represented as a `Component` proto. Other UI frameworks oftentimes give a different name for an instance (i.e. Element) of a component, but for simplicity and explicitness, I will refer to these instances as component instance or _component tree_ (for the entire tree of component instances) in this doc.

## Life of a Mesop request

### Initial page load

When a user visits a Mesop application, the following happens:

1. The user visits a path on the Mesop application, e.g. "/" (root path), in their browser.
1. The Mesop client-side web application (Angular) is bootstrapped and sends an `InitRequest` to the server.
1. The Mesop server responds with a `RenderEvent` which contains a fully instantiated component tree.
1. The Mesop client renders the component tree. Every Mesop component instance corresponds to 1 or more Angular component instance.

### User interactions

If the user interacts with the Mesop application (e.g. click a button), the following happens:

1. The user triggers a `UserEvent` which is sent to the server. The UserEvent includes: the application state (represented by the `States` proto), the event handler id to trigger, the key of the component interacted with (if any), and the payload value (e.g. for checkbox, it's a bool value which represents the checked state of the checkbox).
1. The server does the following:
    1. Runs a first render loop in tracing mode (i.e. instantiate the component tree from the root component of the requested path). This discovers any event handler functions. In the future, this trace can also be used to calculate the before component tree so we can calculate the diff of the component tree to minimize the network payload.
    1. Updates the state by feeding the user event to the event handler function discovered in the previous step.
    > Note: there's a mapping layer between the UserEvent proto and the granular Python event type. This provides a nicer API for Mesop developers then the internal proto representation.
    1. Runs a second render loop to generate the new component tree given the new state. After the first render loop, each render loop results in a RenderEvent sent to the client.
    1. In the [streaming](../guides/interactivity.md#streaming) case, we may run the render loop and flush it down via [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) many times.
1. The client re-renders the Angular application after receiving each RenderEvent.

## Python Server

Flask is a minimalist Python server framework that conforms to WSGI ([Web Server Gateway Interface](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface)), which is a Python standard that makes it easy for web servers (oftentimes written in other languages like C++) to delegate requests to a Python web framework. This is particularly important in the downstream case because we rely on an internal HTTP server to serve Mesop applications.

For development purposes (i.e. using the CLI), we use [Werkzeug](https://werkzeug.palletsprojects.com/en/), which is a WSGI library included with Flask.

## Web Client

Mesop's Web client consists of three main parts:

- **Core**: Includes the root Angular component and singleton services like `Channel`. This part is fairly small and is the critical glue between the rest of the client layer and the server.
- **Mesop Components**: Every Mesop component has its own directory under `/components`
> Note: this includes both the Python API and the Angular implementation for developer convenience.
- **Dev Tools**: Mesop also comes with a basic set of developer tools, namely the components and log panels. The components panel allows Mesop developers to visualize the component tree. The log panel allows Mesop developers to inspect the application state and component tree values.

## Static assets

- Using the regular CLI, the web client static assets (i.e. JS binary, CSS, images) are served from the Python server. This simplifies deployment of Mesop applications by reducing version skew issues between the client and server.
- In uncompiled mode (using the dev CLI), the web client is served from the web devserver. This is convenient because it builds faster than the regular/compiled mode and it allows live-reloading when developing the client codebase.

## Tooling

Outside of the `mesop/` directory are various tools used to build, test and document the Mesop framework. However, anything needed to actually run a Mesop application should be located within `mesop/`. The three main tools inside the codebase are:

- **Build tooling** - these are in `build_defs/` which contains various Bazel `bzl` files and `tools` which is forked from the Angular codebase. The build toolchain is described in more detail on the [toolchain doc](./toolchain.md).
- **Component generator** - inside `generator/` is a mini-library and CLI tool to generate Mesop components from existing Angular components, specifically Angular Material, although with some modifications it could support more generic Angular components. The generator modifies the codebase so that nothing in `generator/` is actually needed when running a Mesop applications.
- **Docs** - Mesop's doc site is built using [Material for Mkdocs](https://squidfunk.github.io/mkdocs-material/) and is what you are looking at right now.

---

### Internal Ci (`docs/internal/ci.md`)

# CI

We use GitHub actions. For all third-party GitHub actions, we must pin it to a specific hash to comply with internal policies.

---

### Internal Codespaces (`docs/internal/codespaces.md`)

# Development on Github Codespaces

[Github Codespaces](https://github.com/features/codespaces) is a quick way to get
started with internal Mesop development. All you need to do is a click a button and a
fully configured workspace will be created for you. No need to spend time debugging
installation issues.

Github Free and Pro plans also provide a [free tier](https://github.com/features/codespaces),
so Codespaces is useful for writing and testing quick patches.

> If using the free tier, the Codespace setup takes 20-30 minutes due to the limited
CPU available.

## Create Github Codespace

You can create a Github Codespace from the [Mesop Github repository page](https://github.com/mesop-dev/mesop).



## Wait for postCreateCommand to run

The Codespace will not be usable until the `postCreateCommand` has completed. You can
view the CLI output by pressing `Cmd/Ctrl + Shift + P` and then finding the `View
Creation Log` option.



## Set the Python environment for the Codespace

During the `postCreateCommand` step, you'll see a pop up asking if you want to set a new
environment for the Codespace. Select `Yes` here to use the virtual env that is created
as part of the `postCreateCommand` set up.



## Run Mesop for development

Once the `postCreateCommand` has finished, you can now start Mesop in the terminal.

```
./scripts/cli.sh
```

This step takes some time for the first run.

You will see some warning messages, but it is OK to ignore them. You can also ignore the
message shown in the screenshot.




## View Mesop demos

Once `./scripts/cli.sh` has started the Mesop dev server, you can view the demos from
the `PORTS` tab.

---

### Internal Contributing (`docs/internal/contributing.md`)

# How-to Contribute

Thanks for looking into contributing to Mesop. There's many ways to contribute to Mesop:

- [Filing new issues](https://github.com/mesop-dev/mesop/issues/new/choose) and providing feedback on existing issues
- Improving our docs
- Contributing examples
- Contributing code

All types of contributions are welcome and are a key piece to making Mesop work well as a project.

## Before you begin

### Sign our Contributor License Agreement

Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.

If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.

Visit <https://cla.developers.google.com/> to see your current agreements or to
sign a new one.

### Review our community guidelines

This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).

## Contributing to docs

If you want to contribute to our docs, please take a look at our [docs issues](https://github.com/mesop-dev/mesop/labels/docs). If you find any of our existing docs confusing or missing key information, please file an issue and we will see how we can improve things. We regularly spend time improving our docs because we know it's a key part of the developer experience.

## Contributing examples

One of the best way of helping the Mesop project is to share what you've built! You can either add an example to our demo gallery by adding it to the [demo/](https://github.com/mesop-dev/mesop/tree/main/demo) directory or you can send us a link to your app running and we will include it in our docs.

## Contributing code

If you'd like to contribute code, I recommend taking a look at one of our existing ["starter" issues](https://github.com/mesop-dev/mesop/labels/starter). These are issues that are good for first-time contributors as they are well-specified.

- [Setup your dev environment](./development.md)
- If you're creating a new component, you can follow the [new component guide](./new-component.md)

I recommend reading through the various pages in the contributing section as it will give you a sense of our project's goals.

One thing that we focus on is providing an easy-to-understand API with minimal breaking changes so we ask that any API changes are first discussed in an issue. This will help prevent wasted work because we are conservative with changing our APIs.

---

### Internal Development (`docs/internal/development.md`)

# Development

I recommend following (or at least reading) all the steps in this doc if you plan on actively developing Mesop.

## Setup

### Bazel/ibazel

We use [Bazel](https://bazel.build/) as our build system. Use [bazelisk](https://github.com/bazelbuild/bazelisk) which ensures the right version of Bazel is used for this project.

If [ibazel](https://github.com/bazelbuild/bazel-watcher) breaks, but bazel works, try following [these steps](https://github.com/bazelbuild/bazel-watcher/issues/588#issuecomment-1421939371)

> TIP: If your build mysteriously fails due to an npm-related error, try running `bazel clean --expunge && rm -rf node_modules`. Bazel and Yarn have a cache bug when upgrading npm modules.

### uv

We use [uv](https://github.com/astral-sh/uv). Follow the instructions [here](https://docs.astral.sh/uv/#getting-started) to install uv.

### Commit hooks

1. Install [pre-commit](https://pre-commit.com/#installation)
1. Install pre-commit hooks for this repo: `pre-commit install`

## Run local development

We recommend using this for most Mesop framework development.

```sh
./scripts/cli.sh
```

> NOTE: this automatically serves the angular app.

## Python

### Third-party packages (PIP)

If you update `//build_defs/requirements.txt`, run:

```sh
bazel run //build_defs:pip_requirements.update
```

### venv

To support IDE type-checking (Pylance) in VS Code, we use Aspect's [rules_py](https://docs.aspect.build/rulesets/aspect_rules_py/) which generates a venv target.

```sh
bazel run //mesop/cli:cli.venv
```

Then, you can activate the venv:

```sh
source .cli.venv/bin/activate
```

You will need to setup a symlink to have Python IDE support for protos:

```sh
./scripts/setup_proto_py_modules.sh
```

Check that you're using venv's python:

```sh
which python
```

Copy the python interpreter path and paste it into VS Code.

Finally, install third-party dependencies.

```sh
pip install -r build_defs/requirements_lock.txt
```

> NOTE: You may need to run the command with `sudo` if you get a permission denied error, particularly with "\_distutils_hack".

## Commit hooks

We use [pre-commit](https://pre-commit.com/) to automatically format, lint code before committing.

_Setup:_

1. [Install pre-commit](https://pre-commit.com/#installation).
1. Setup git hook: `pre-commit install`

## Docs

We use [Mkdocs Material](https://squidfunk.github.io/mkdocs-material/) to generate our docs site.

1. [Activate venv](#venv)
1. `mkdocs serve`

---

### Internal Hot Reload (`docs/internal/hot-reload.md`)

# Hot Reload

One of Mesop's key benefits is that it provides a fast iteration cycle through hot reload. This means whenever a Mesop developer changes their Mesop app code, their browser window will automaticall reload and execute the new app code while preserving the existing state. This isn't guaranteed to work, for example, if the State class is modified in an incompatible way, but it should work for >90% of the build-edit loops (e.g. tweaking the UI, calling new components).

## How it works

See: [https://github.com/mesop-dev/mesop/pull/211](https://github.com/mesop-dev/mesop/pull/211)

## Design decisions

### What to reload

Right now we reload all the modules loaded by the Mesop application. However, this results in a lot of unnecessary modules being reloaded and can be quite slow if there's a heavy set of transitive dependencies.

Instead, I'm thinking we can use a heuristic where we calculate the existing package based on the file path passed in and *only* reload modules which are in the current package or a sub-package. Effectively this is only reloading modules within the target file's subtree.

This seems like a pretty reasonable heuristic where it reloads all the application modules without reloading the entire dependency graph. Previously I tried reloading *only* the module passed in via `--path`, however this was too limiting as it meant shared code (e.g. a navmenu) would not get hot-reloaded.

### When to reload

With the previous design decision, re-executing a module should be much faster, but we still need to guard against the case where the live reload occurs too quickly in the client side. Options:

- **Wait a fixed timeout** - A simple heuristic could just be to wait 500ms since in theory, all the application code (with the non-application dependnecies cached) should re-execute fairly quickly.
- **Client retry/reload** - Another approach could be to retry a client-side reload N times (e.g. 3) if we get an error. The pattern could be: 1. save state to local storage, 2. trigger reload, 3. if reload results in a successful render, we clear the state _OR_ if reload results in an error, we trigger a reload (and persist in local storage which retry attempt this is).
- **Server loop** - In the common error case where the server is still re-executing the module and the client reloads, it will hit path not found because the path hasn't been registered yet. One way of mitigating this is to simply do a sleep in debug mode. We can even do an exponential backoff for the sleep (e.g. wait 300ms, 900ms, 2700ms).
- **Preferred appproach** - given the trade-offs, I think **Server loop** is the best option as it's relatively simple to implement, robust and doesn't incur a significant delay in the happy case.

### Abstracting ibazel-specific details

Since Google's internal equivalent of ibazel doesn't work exactly the same, we should treat HotReloadService as an abstract base class and then extend it for Ibazel (and the internal variant).

---

### Internal Modes (`docs/internal/modes.md`)

# Modes

There are two modes that you can run Mesop in.

## Development mode (aka debug mode or editor mode)

Recommended for developers using Mesop when they are developing the apps locally. This provides good error messages and hot reloading.

- **How to run:** `ibazel run //mesop/cli -- --path=mesop/mesop/example_index.py`
- Angular should run in dev mode.
- Developer Tools and Visual Editor are available.

## Prod mode

Recommended when developers deploy applications built with Mesop for public serving. This is optimized for performance and provides less-detailed error messages.

- Developer tools aren't available.
- Angular doesn't run in dev mode.
- **How to run:** `bazel run //mesop/cli -- --path=mesop/mesop/example_index.py --prod`

---

### Internal New Component (`docs/internal/new-component.md`)

# New Component

## How-to

```sh
python scripts/scaffold_component.py $component_name
```

## API Guidelines

- Make all arguments keyword only by putting `*` as the initial argument. Keyword argument is more readable, particularly for UI components which will have increasingly more optional arguments over time.
- **Model after existing APIs.** For example, if we are wrapping an existing @angular/material component, we will try to mirror their API (within reason). If we are wrapping a native HTML element, we should try to expose a similar API. In some cases, we will look at other UI frameworks like Flutter for inspiration, even though we are not directly wrapping them.
- **Prefer small components**. We should try to provide small native components that can be composed by content components in Python. This enables a wider range of use cases.

## New events

Try to reuse the existing events when possible, but you may need to sometimes create a new event.

1. Define the event class in `//mesop/events/{event_name}.py`
1. In the same file, define an event mapper and register it: `runtime().register_event(EventClass, event_mapper)`

## Potential exploration areas

- Code-gen `component_renderer` using a shell/Python script. Initially, just run the script as-needed, but eventually can run it as part of a BUILD rule (a la [@angular/components examples](https://github.com/angular/components/tree/13629b0cd814ccc5fa01cf670b8b3001bc0021ff/tools/example-module))

---

### Internal Publishing (`docs/internal/publishing.md`)

# Publishing

Follow these instructions for releasing a new version of Mesop publicly via PyPI (e.g. `pip install mesop`).

## Check main branch

Before, cutting a release, you'll want to check two things:

1. The `main` branch should be healthy (e.g. latest commit is green).
2. Check the [snyk dashboard](https://app.snyk.io/org/wwwillchen/project/756c376f-d2e7-44f8-9c20-e4062fdf543f) to review security issues:

    - It only runs weekly so you need to click "Retest now". If there's any High security issues for a core Mesop file (e.g. anything in `mesop/*`), then you should address it before publishing a release.

## Update version to RC

Update [`mesop/version.py`](https://github.com/mesop-dev/mesop/blob/main/mesop/version.py) by incrementing the version number. We follow semver.

You want to first create an RC (release candidate) to ensure that it works.

For example, if the current version is: `0.7.0`, then you should increment the version to `0.8.0rc1` which will create an RC, which is treated as a [pre-release by PyPI](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases).

Merge the PR that bumps the version and then go to the next step to publish a GitHub release which will in turn publish to PyPI.

## Publish GitHub release

After you've submitted the PR which bumps the version and then [publish a GitHub release](https://github.com/mesop-dev/mesop/releases/new).

1. Click "Choose a tag" and type in the version you just released. This will create a new Git tag.
1. Click "Generate release notes".
1. **If this is a an RC:** Click "Set as a pre-release", **otherwise** leave the "Set as the latest release" checked.
1. If this is a regular (non-RC) release, click "Create a discussion for this release".
1. Click "Publish release".

## Testing locally

**Pre-requisite:** you will need to [install uv](https://docs.astral.sh/uv/getting-started/installation/) before doing the following steps.

### Dev CLI

Run the following command but replace `1.0.0` with the version that you just published:

```sh
cd scripts/smoketest_app/ && uvx --refresh mesop==1.0.0 main.py
```

This will start the Mesop dev server and you can test that hot reload works. Double-check that the version shown is the version that you just released.

### Gunicorn integration

Run the following command but replace `1.0.0` with the version that you just published:

```sh
cd scripts/smoketest_app/ && uvx  --refresh --with mesop==1.0.0 gunicorn@latest main:me
```

This will launch Mesop under the Gunicorn server so you can make sure it works as expected.

## Test on Colab

Because Colab installs from PyPI, you will need to test the RC on Colab after uploading to PyPI.

Open our [Mesop Colab notebook](https://colab.research.google.com/github/mesop-dev/mesop/blob/main/notebooks/mesop_colab_getting_started.ipynb). You will need to explicitly pip install the RC version as pip will _not_ automatically install a pre-release version, even if it's the newest version. So change the first cell to something like:

```sh
 !pip install mesop==0.X.Yrc1
```

> Tip: sometimes it takes a minute for the PyPI registry to be updated after upload, so just try again.

Then, run all the cells and make sure it works. Usually if something breaks in Colab, it's pretty obvious because the output isn't displayed, etc.

## Change the version from RC to regular release

If you find an issue, then redo the above steps and create another RC candidate: `0.8.0rc1` -> `0.8.0rc2`.

If all the testing looks good, then you can update [`mesop/version.py`](https://github.com/mesop-dev/mesop/blob/main/mesop/version.py) and change the version from RC to a regular release, for example:

`0.8.0rc1` -> `0.8.0`

Re-do the steps above to publish and test the release.

---

### Internal Testing (`docs/internal/testing.md`)

# Testing

## Unit tests

You can run our unit tests using Bazel.

### Run tests

```sh
bazel test //mesop/...
```

## E2E tests

We use [Playwright](https://playwright.dev/) as our e2e test framework. Unlike most of the stack, this isn't Bazel-ified although we'd like to eventually do this.

### Run tests

```shell
yarn playwright test
```

### Debug tests

```shell
yarn playwright test --debug
```

---

### Internal Toolchain (`docs/internal/toolchain.md`)

# Build / Toolchain

## Context

Because Mesop is a Google open-source project and we want to provide a good integration with Google's internal codebase, Mesop uses Google's build system Bazel.

Although Bazel is similar to the [internal tool](https://bazel.build/about/faq#did_you_rewrite_your_internal_tool_as_open-source_is_it_a_fork), there's numerous differences, particularly around the ecosystems, which makes it quite a challenge to maintain Mesop for both open-source and internal builds. Nevertheless, it's important that we do this to serve both communities well.

## Differences

We try to isolate as much of the differences between these two environments into the `build_defs/` directory. Different versions of the same files inside `build_defs/` are maintained for each environment. In particular, `build_defs/defaults.bzl` is meant to wrap all external rules/macros used by Mesop so we can swap it between the internal and external variants as needed.

Finally, all external dependencies, e.g. Python's `requirement('$package')` or NPM's `@npm//$package`, are referenced via an indirection to build_defs/defaults.bzl. This is because Google has a special approach to handling third-party dependencies.

### Gotchas

Here's a quick list of gotchas to watch out for:

- Do not use `import * as` when importing protos from TS. This prevents tree-shaking downstream.
- Do not use any external Bazel references (e.g. `@`) within `mesop/`. Instead, reference them indirectly using a wrapper in `build_defs/`.
- Avoid relying on implicit transitive dependencies, particularly for TS/NG modules.
- Do not use raw `JSON.parse`, instead use `jsonParse` in `strict_types.ts`.

## Angular

We rely heavily on Angular's toolchain, particularly around Bazel integration. Many of the Web-related Bazel rules, particularly for Angular/TS code was forked from [github.com/angular/components](https://github.com/angular/components).

---

### Internal Type Checking (`docs/internal/type-checking.md`)

# Type Checking

## Python Type Checking

For our Python code, we use [pyright](https://github.com/microsoft/pyright) as our static type checker because it has excellent IDE support in VS Code via [pylance](https://github.com/microsoft/pylance-release).

To run Python type-checking, run:

```shell
./scripts/run_py_typecheck.sh
```

This will setup the pre-requisites needed for type-checking.

---

### Internal Vs Code Remote Container (`docs/internal/vs-code-remote-container.md`)

# VS Code Remote Container

VS Code Remote Containers is a quick way to get started with internal Mesop
development if you have [VS Code](https://code.visualstudio.com/) and
[Docker Desktop](https://www.docker.com/products/docker-desktop/) installed.

This approach will create a fully configured workspace, saving you time from
debugging installation issues and allowing you to start development right away.

## Pre-requistes: Install VS Code and Docker

In order to use VS Code remote containers, you will need VS Code installed. You will
also need Docker Desktop (which will install Docker Engine and Docker Compose) to run
the remote containers.

- [VS Code](https://code.visualstudio.com/)
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)

## Fork and Clone the Mesop repository

> It is not recommended to develop locally and on remote containers using the same
folder since this may cause unexpected conflicts. Instead you should clone the
repository in a separate directory.

You can follow the instructions here on [how to fork and clone a Github repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo).

## Share Git credentials with your container

The VS Code Dev Containers extension provides a few ways to share your local Git
credentials with your remote container.

If you cloned the Mesop repo using HTTPS, you can use a [Github CLI or Git Credential Manager](https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git).

If you used SSH, then your local ssh agent will automatically be forwarded into your
remote container. All you need do is run the `ssh-add` command to add the ssh key you've
configured for GitHub access.

See the [Sharing Git credentials with your container page](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials) for full details.

## Open folder in container

Open VS Code, press `Cmd/Ctrl + Shift + P`, and select the `Dev Containers: Open Folder in Container...`
option. This will create a new workspace inside a remote container.



## Wait for postCreateCommand to run

The workspace will not be usable until the `postCreateCommand` has completed.



## Run Mesop for development

Once the `postCreateCommand` has finished, you can now start Mesop in the terminal.

```
./scripts/cli.sh
```

You will see some warning messages, but it is OK to ignore them.

You should see this message once the Mesop server is ready.



## View Mesop demos

Once `./scripts/cli.sh` has started the Mesop dev server, you can view the demos at
http://localhost:32123.

---

### Web Components Api (`docs/web-components/api.md`)

# Web Components API

> Note: Web components were previously launched under the labs namespace (i.e. `import mesop.lab as mel`). As part of the v1 release, all of the web component APIs are now exposed in the main mesop namespace (i.e. `import mesop as me`). You can continue to use the labs namespace for the web components API until v2 (which will happen much later).

**Example usage:**

```python
import mesop as me

@me.web_component(...)
def a_web_component():
    me.insert_web_component(...)
```

## API

::: mesop.component_helpers.web_component.web_component

::: mesop.insert_web_component

::: mesop.WebEvent

::: mesop.slot

---

### Web Components Troubleshooting (`docs/web-components/troubleshooting.md`)

# Web Components Troubleshooting

## Security policy

One of the most common issues when using web components is that you will need to relax the stringent security policy Mesop uses by default.

If you use the `mesop` command-line tool to run your app, you will see a detailed error message printed like this that will tell you how to fix the error:

<img src="../../assets/csp-message.webp">

If you are using Colab or another tool to run your Mesop app, and you can't see the terminal messages, then you can use your browser developer tools to view the console error messages.

### Content security policy error messages

In your browser developer tools, you may see the following console error messages (the exact wording may differ):

#### script-src Error

???+ failure "script-src Console Error"
    Refused to load the script 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js' because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-X-_ZR64fycojGBCDQbjpLA'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

If you see this error message, then you will need to update your page's [Security Policy](../api/page.md#mesop.security.security_policy.SecurityPolicy) `allowed_script_srcs` property. In this example, because the "script-src" directive was violated, you will need to add the script's URL to the Security Policy like this:

```py
@me.page(
    security_policy=me.SecurityPolicy(
        allowed_script_srcs=["https://cdn.jsdelivr.net"]
    )
)
```

???+ tip "Allow-listing sites"
     You can allow-list the full URL including the path, but it's usually more convenient
     to allow-list the entire site. This depends on how trustworthy the site is.

#### connect-src Error

???+ failure "connect-src Console Error"
    zone.umd.js:2767 Refused to connect to 'https://identitytoolkit.googleapis.com/v1/projects' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.


If you see this error message, then you will need to update your page's [Security Policy](../api/page.md#mesop.security.security_policy.SecurityPolicy) `allowed_connect_srcs` property. In this example, because the "connect-src" directive was violated, you will need to add the URL you are trying to connect to (e.g. XHR, fetch) to the Security Policy like this:

```py
@me.page(
    security_policy=me.SecurityPolicy(
        allowed_connect_srcs=["https://*.googleapis.com"]
    )
)
```

???+ tip "Allow-listing domains using wildcard"
     You can wildcard all the subdomains for a site by using the wildcard character `*`.

#### Trusted Types Error

Trusted Types errors can come in various forms. If you see a console error message that contains TrustedHTML, TrustedScriptURL or some other variation, then you are likely hitting a trusted types error. Trusted Types is a powerful web security feature which prevents untrusted code from using sensitive browser APIs.

Unfortunately, many third-party libraries are incompatible with trusted types which means you need to disable this web security defense protection for the Mesop page which uses these libraries via web components.

???+ failure "TrustedHTML Console Error"

    TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.

You can fix this Trusted Types error by disabling Trusted Types in the security policy like this:

```py
@me.page(
    security_policy=me.SecurityPolicy(
        dangerously_disable_trusted_types=True
    )
)
```

## Colab

### Site level user permissions

Some APIs like [`navigator.mediaDevices.getUserMedia()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) require that users grant permission through a browser prompt. Colab attempts to detect if a code cell requires user permission, but this detection does not work for Mesop apps running in Colab using `me.colab_run()`.

As a workaround, use the IPython `%%javascript` cell magic to trigger a user permission prompt. Once permission is granted, it applies to all cells in the notebook. For example, to request the `microphone` permission, create a new code cell and run the following code:

```
%%javascript
navigator.mediaDevices.getUserMedia({audio: true, video: false});
```

---

### Api Commands Focus Component (`docs/api/commands/focus-component.md`)

# Focus component

If you want to focus on a component, you can use `me.focus_component` which focuses the component with the specified key if it is focusable.

## Example

```python
--8<-- "mesop/examples/focus_component.py"
```

## API

::: mesop.commands.focus_component.focus_component

---

### Api Commands Navigate (`docs/api/commands/navigate.md`)

# Navigate

To navigate to another page, you can use `me.navigate`. This is particularly useful for navigating across a [multi-page](../../guides/multi-pages.md) app.

## Example

```python
--8<-- "mesop/examples/navigate.py"
```

## API

::: mesop.commands.navigate.navigate

---

### Api Commands Scroll Into View (`docs/api/commands/scroll-into-view.md`)

# Scroll into view

If you want to scroll a component into the viewport, you can use `me.scroll_into_view` which scrolls the component with the specified key into the viewport.

## Example

```python
--8<-- "mesop/examples/scroll_into_view.py"
```

## API

::: mesop.commands.scroll_into_view.scroll_into_view

---

### Api Commands Set Page Title (`docs/api/commands/set-page-title.md`)

# Set page title

If you want to set the page title, you can use `me.set_page_title` which will
set the page title displayed on the browser tab.

This change does not persist if you navigate to a new page. The title will be
reset to the title configured in `me.page`.

## Example

```python
--8<-- "mesop/examples/set_page_title.py"
```

## API

::: mesop.commands.set_page_title.set_page_title

---

### Blog Posts Hello Mesop (`docs/blog/posts/hello-mesop.md`)

---
date: 2023-12-25
---

# Hello, Mesop

After working on Mesop for the last two months, I'm excited to finally announce the first version of Mesop, v0.1. This is still early days for Mesop, but it's an important milestone because it represents a minimum viable tool for building UIs in Python. In case you haven't read Mesop's [home page](https://mesop-dev.github.io/mesop/), Mesop is a Python-based UI framework that allows you to rapidly build web demos. Engineers without frontend experience can build web UIs by writing idiomatic Python code.

## Why Mesop?

Mesop is in many ways a remix of many existing ideas packaged into a single cohesive UI framework, designed for Python developers. I've documented some of these [goals](../../goals.md) previously, but I'll quickly recap the benefits of Mesop here:

- Allows non-frontend engineers to rapidly build UIs for internal use cases like demos.
- Provides a fast build-edit-refresh loop through [hot reload](../../internal/hot-reload.md).
- Enables developers to benefit from the mature [Angular](https://angular.dev/) web framework and [Angular Material](https://material.angular.io/) components.
- Provides a flexible and composable components API that's idiomatic to Python.
- Easy to deploy by using standard HTTP technologies like [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).


## What's next for Mesop?

I see a few broad themes of work in the coming year or so.

### Expand Mesop's component library

Mesop's current [component library](../../components/box.md) is a solid start but there's still gaps to support common use cases.

**Areas of work:**

- **Complete [Angular Material](https://material.angular.io/components/categories) component coverage.** We support 13+ Angular Material components today, however there's many more that we don't support. Some of it is because I haven't had time to wrap their components, but in other cases (e.g. [sidenav](https://github.com/mesop-dev/mesop/issues/30)), I'd like to spend more time exploring the design space as it will probably require supporting some kind of multi-slot component API. Getting this API designed correctly, for not just this component but also future components, is important in the long run.

- **Support more native HTML elements/browser APIs.** Right now, only [Box](../../components/box.md) and [Text](../../components/text.md) are thin wrappers around native HTML elements. However, there are other HTML elements like `<img>`, `<audio>`
 and `<video>` that I'd like to also support. The flip side of supporting these components is enabling a way to allow Mesop end-users to upload these media contents, which there are also native browser APIs for.

- **Custom components.** Some components won't belong in the standard Mesop package because it's either too experimental or too use-case specific. It would be nice to have a complete story for supporting custom components. Today, all of the components use the [component helper](https://github.com/mesop-dev/mesop/blob/main/mesop/component_helpers/helper.py) API which wraps internal framework details like runtime. However, there still isn't a very good story for loading custom components in the Angular frontend (e.g. ComponentRenderer's [type to component map](https://github.com/mesop-dev/mesop/blob/main/mesop/web/src/component_renderer/type_to_component.ts)) and testing them.


### Make it easy to get started with Mesop

Using Mesop today requires following our [internal development setup](../../internal/development.md) which requires dependencies like Bazel/iBazel which makes it easy to interoperate with our downstream sync, but these dependencies aren't commonly used in the Python ecosystem. Eventually, I'd like make using Mesop as simple as `pip install mesop` and then using Mesop's built-in CLI: `mesop serve` for local development and `mesop deploy` to deploy on a Cloud service.

**Areas of work:**

- **Find a suitable ibazel replacement for Hot Reload.** Instead of requiring Mesop developers to sync the entire repo and building the project with Bazel and iBazel, we should distribute a ready-to-use pip package of Mesop. However, this leaves an open question of how we support [hot reload](../../internal/hot-reload.md) without iBazel which provides: 1) a filesystem watching mechanism and 2) live reload. We'll need to investigate good open-source equivalents for each of these capabilities.

- **Provide web-based interactive demos.** Many JavaScript UI frameworks provide a playground (e.g. [Angular](https://angular.dev/playground)) or interactive tutorial (e.g. [Solid](https://www.solidjs.com/tutorial/introduction_basics)) so that prospective developers can use the framework before going through the hassle of setting up their own local dev environment. This would also be very helpful to provide for each component as it's a lot easier to understand a component by tinkering with a live example.

### Explore power use cases

Today Mesop is good for internal apps with relatively un-stringent demands in terms of UI customizability and performance. For production-grade external apps, there's several areas that Mesop would need to advance in, before it's ready.

**Areas of work:**

- **Optimize network payload.** Right now the client sends the entire state to the server, and the server responds with the entire state and component tree. For large UIs/apps, this can result in sizable network payloads. We can optimize this by sending deltas as much as possible. For example, the server can send a delta of the state and component tree to the client. In addition, if we use [POST instead of GET](https://github.com/mesop-dev/mesop/issues/26), we can stop using base-64 encoding which adds a significant overhead on top of Protobuf binary serialization.

- **Stateful server.** Even with the above optimizations, we'd essentially preserve the current architecture, but there's some limitations in how much improvements we can make as long as we assume servers are stateless. However, if we allow stateful servers (i.e. long-lived connections between the client and server), we can use things like WebSockets and *always* send deltas bi-directionally, in particular from client to server which isn't possible with a stateless server. The problem with this direction, though, is that it makes deployment more complex as scaling a WebSocket-based server can be hard depending on the cloud infrastructure used. In addition, we'll need to handle new edge cases like authentication and broken WebSockets connections.

- **Optimistic UI.** One of the drawbacks for server-driven UI frameworks like Mesop is that it introduces significant latency to simple user interactions. For example, if you click a button, it requires a network roundtrip before the UI is (meaningfully) updated. One way of dealing with this shortcoming is by pre-fetching the next UI state based on a user hint. For example, if a user is hovering over a button, we could optimistically calculate the state change and component tree change ahead of time before the actual click. The obvious downside to this is that optimistically executing an action is inappropriate in many cases, for example, a non-reversible action (e.g. delete) should never be optimistically done. To safely introduce this concept, we could provide an (optional) annotation for event handlers like `@me.optimistic(events=[me.HoverEvent])` so develpers could opt-in.

Some of these directions are potentially mutually exclusive. For example, having a **stateful server** may make **optimistic UI** practically more difficult because a stateful server means that non-serializable state could start to creep in to Mesop applications which makes undoing optimistic UI updates tricky

There's, of course, even more directions than what I've listed here. For example, it's technically possible to compile Python into WebAssembly and run it in the browser and this could be another way of tackling latency to user interactions. However, this seems like a longer-term exploration, which is why I've left it out for now.

## Interested in contributing?

If any of this excites you, please reach out. The easiest way is to raise a [GitHub issue](https://github.com/mesop-dev/mesop/issues) and let me know if there's something specific you'd like to contribute.

---

### Blog Posts V1 (`docs/blog/posts/v1.md`)

---
date: 2025-03-19
---

# Mesop v1

After developing Mesop for nearly a year and a half and publicly launching it on [Hacker News](https://news.ycombinator.com/item?id=40567327) 9 months ago, we are excited to release Mesop v1.

It's been an amazing journey working on Mesop, especially with [richard-to@](http://github.com/richard-to) who's been my 20% collaborator at Google. Despite being an extremely small core team (working on this as a 20% project!), we've been able to foster a thriving community both within Google where hundreds of Googlers have used it to build internal apps and outside of Google where it's been downloaded [more than 180k times](https://pepy.tech/projects/mesop) on PyPI.

## What v1 means

Mesop v1 signifies our commitment to prioritizing stability and avoiding backwards-incompatible changes unless absolutely necessary—such as important security fixes or critical bug fixes.

Since we publicly launched 9 months ago, we have taken a conservative approach to making API changes, avoiding backwards-incompatible changes as much as possible. This has been key to Mesop's successful adoption both within Google and externally. In addition, Google's [reputation for churn](https://abseil.io/resources/swe-book/html/ch15.html#:~:text=There%E2%80%99s%20an%20old,and%20fast%2Dpaced.) has made us careful in designing our APIs.

Almost all Mesop apps should be able to seamlessly upgrade to v1. The only backwards-incompatible change we made from v0.14 is to drop `mel.text_io` which has been deprecated for many months and can be replaced with [`mel.text_to_text`](https://google.github.io/mesop/components/text-to-text/) as a simple one-line change.

## Future of Mesop

You might have also noticed that Mesop has graduated from the Google GitHub org into its own GitHub org. Becoming a community-driven project is beneficial for the longevity of Mesop. Rest assured, Mesop continues to have the same core maintainers and the roadmap remains the same.

v1 doesn't mean the end of Mesop's development. We plan to add new components over time, maintain our dependencies, provide more examples, and generally improve the overall developer experience.

While we can’t make firm commitments, our goal is to remain on v1 for as long as possible, avoiding a jump to v2.

— [wwwillchen@](https://github.com/wwwillchen/)

---

### Blog Posts Visual Editor (`docs/blog/posts/visual-editor.md`)

---
date: 2024-01-12
---

# Visual Editor

## Why?

As I began discussing Mesop with friends and colleagues, one thing that has come up is the difficulty of teaching and persuading non-frontend engineers to build UIs, even simple ones. CSS, particularly the rules around layout, can be quite challenging and off-putting.

I've developed a new [visual editor](https://github.com/mesop-dev/mesop/issues/31) for Mesop that aims to make UI building more approachable for beginners and more productive for experts.

## What?

Let's take a look at the visual editor:



With the visual editor, you can:

- Add new components into your app
- Modify existing components
- Visualize the component tree hierarchy
- You can inspect existing components on the page by hovering over them and then change them in the editor panel
- **B**ring **Y**our **O**wn components. By decorating a Python function with `me.component`, you've turned it into a Mesop component and you can now add it with the visual editor.

What's exciting about the visual editor is that you aren't locked into it - everytime you change a component with the visual editor, it's modifying the source code directly so you can seamlessly go back forth between a regular text editor and the visual editor to build your Mesop app.

## Prior Art

Visual editors (aka WYSIWYG builders) have been around for a long time. [Puck](https://github.com/measuredco/puck) is one of the most interesting ones because of a few reasons: 1) it's open-source, 2) it's flexible (e.g. bring your own components) and 3) it's intuitive and easy-to-use.

The main issues I saw with Puck, particularly for Mesop's use case, is that it [currently only supports React](https://github.com/measuredco/puck/issues/302) (and Mesop uses Angular) and Puck saves data whereas I would like Mesop's Visual Editor to directly emit/update code, which I'll explain next.

## Principles

### Hybrid code (not low-code)

One of the reasons why WYSIWYG builders have not gotten much traction with engineers is that they're often good for simple applications, but then you [hit a wall](https://www.reddit.com/r/FlutterDev/comments/165d804/what_do_you_think_about_flutter_flow/) building more complex applications.

To avoid this issue, I'm focusing on making the Visual Editor actually emit __code__ and _not_ just __data__. Essentially, the UI code that you produce from the Visual Editor should be the same as the code that you would write by hand.

### Unobtrustive UI

I want Mesop app developers to do most of their work (except for the final finetuning for deployment) in the Visual Editior which means that it's important the Editor UI is un-obtrusive. Chrome DevTools is a great example of a low-key tool that many web developers keep open throughout their development - it's helpful for debugging, but then it's out of your way as you're interacting with the application.

Concretely, this means:

- Editor UI should be collapsible
- You should be able to "disable" the editor mode and interact with the application as a normal user.

### Contextual

The visual editor should provide only the information that you need when you need it.

For example, rather than showing all the style properties in the editor panel, which would be quite overwhelming, we only show the style properties that you're using for the selected component.

### Local-only

Because the Visual Editor relies on editing files in your local filesystem, I want to avoid any accidental usages out in the wild. Concretely, this means that you can only use the Visual Editor in localhost, otherwise the Mesop server will reject the editor edit requests.

## What's next

There's still a lot of improvements and polishes I would like to make to the visual editor, but a few high-level ideas that I have are:

1. Build example applications using the visual editor with a video walkthrough.
1. Create more high-level components in Mesop Labs, which I'll introduce in an upcoming blog post, to make it even easier to build apps with the visual editor.
1. Drag and drop components onto the page and within the page. This will provide an intuitive experience for building the UI, literally block by block.

---

### Blog Posts Web Components (`docs/blog/posts/web-components.md`)

---
date: 2024-07-12
---

# Is Mesop + Web Components the cure to Front-end fatigue?

I saw this tweet the other day and couldn't help but chuckle:

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">is this the thing that will finally save me from ever learning front end?<a href="https://t.co/eDgY0AfG6U">https://t.co/eDgY0AfG6U</a></p>&mdash; xlr8harder (@xlr8harder) <a href="https://twitter.com/xlr8harder/status/1798673386425786724?ref_src=twsrc%5Etfw">June 6, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

At first, I thought of it as joke, but now that Mesop has launched experimental support for [Web Components](https://mesop-dev.github.io/mesop/web-components/), I think it's plausible that Mesop with Web Components can save you from front-end fatigue.

## What is Mesop?

Before we dive in, let me explain what Mesop is. Mesop is a Python UI framework focused on rapidly building AI apps. You can write a lot of kinds of apps all in Python as you can see from the examples on our [home page](https://mesop-dev.github.io/mesop/), but when you need to, Mesop provides the flexibility of dropping down into web components so you can  have fine-grained UI control and use existing JS libraries.

## Avoid the builds



DHH, creator of Rails, recently gave an [interview](https://youtu.be/rEZNbM4MUdo?si=e0uk-2DsCvwwFVHO&t=1485) saying how he's "done with bundling" and the overall complexity of modern front-end build toolchains.

As someone who's done front-end for almost a decade, I can attest to the sentiment of feeling the pain of compiling JavaScript options. Setting up compiler configs and options can easily take hours. I want to be clear, I think a lot of these tools like TypeScript are fantastic, and the core Mesop framework itself is compiled using TypeScript and Angular's compilers.

But when it comes to rapid prototyping, I want to avoid that overhead. In our [design proposal](https://docs.google.com/document/d/1Nc7Ub8DMNSxAmFuPRdyrlZXh_AoxVjZM-YEeWF8dAyI/edit#heading=h.36b20xkar02d), we intentionally designed a lightweight model where you don't _need_ to set up a complex build chain to start writing JavaScript.

Sometimes a build step is unavoidable, e.g. you're writing TypeScript, and you can still compile your JavaScript as needed.

## Framework churn

The front-end ecosystem is infamous for its steady and constant churn. The good thing about building on top of web components is that it's based on [web standards](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) supported by all modern browsers. This means, that given browser makers' focus on "not breaking the web", this will be there for many years, if not decades to come.

For years, web components had a reputation of being an immature technology due to inconsistent support across browsers, but fast forward to 2024, and web components are well-supported in modern browsers and libraries built on web components like [Lit](https://lit.dev/), which is downloaded millions of times a week.

## Minimizing front-end fatigue in Mesop

FE developers are so used to the pain and complexity of front-end development that they can forget how steep the learning curve is until someone from another domain tries to build a simple web app, and struggles with just getting the web app up and started.

Mesop app developers are mostly _not_ front-end developers which means that reducing the complexity, especially learning curve, of building custom components is very important. In Mesop, we've designed a smooth pathway where you can [get started](https://mesop-dev.github.io/mesop/web-components/quickstart/) with a little front-end knowledge and build simple custom components without learning a complex front-end framework.

## What's next

Follow [our X/Twitter account, @mesop_dev](https://x.com/mesop_dev) for more updates. We're working on improving our web component support, in particular by:

- Creating guides for wrapping React components into Mesop web components
- Fostering an ecosystem of open-source Mesop web components by making it easy to discover and reuse web components that other people have built.

We're excited about the potential of Mesop and Web Components to simplify front-end development. Whether it's the cure for front-end fatigue remains to be seen, but I think it offers a promising alternative to the complexity of traditional front-end development.

---

### Blog Posts Why Mesop (`docs/blog/posts/why-mesop.md`)

---
date: 2024-05-13
hide:
  - toc
---

# Why Mesop?

Mesop is a new UI framework that enables Python developers to quickly build delightful web apps in a scalable way.

Many Python UI frameworks are easy to get started with, but customizing beyond the defaults often requires diving into JavaScript, CSS, and HTML — a steep learning curve for many developers.

Mesop provides a different approach, offering a framework that's both easy to learn and enables flexible UI building, all within Python.

I want to share a couple concrete ways in which Mesop achieves this.

## Build UIs with Functions (i.e. Components)

Mesop embraces a component-based philosophy where the entire UI is composed of reusable, building blocks which are called components. Using a component is as simple as calling a Python function. This approach offers several benefits:

- **Simplicity:** You can use your existing Python knowledge to build UIs quickly and intuitively since components are just functions.
- **Maintainability:** Complex UIs become easier to manage and understand by breaking them down into smaller, focused components.
- **Modularity:** Components are self-contained, enabling easy reuse within a project or across different projects.

Here's an example of a reusable icon button component:

```python
def icon_button(*, icon: str, label: str, tooltip: str, on_click: Callable):
  """Icon button with text and tooltip."""
  with me.content_button(on_click=on_click):
    with me.tooltip(message=tooltip):
      with me.box(style=me.Style(display="flex")):
        me.icon(icon=icon)
        me.text(
          label, style=me.Style(line_height="24px", margin=me.Margin(left=5))
        )

```

## Flexibility through Layered Building Blocks

Mesop provides a range of UI building blocks, from low-level [native components](https://mesop-dev.github.io/mesop/components/#native-components) to high-level components.

- Low-level components: like [box](../../components/box.md), offer granular control over layout and styling. They empower you to create custom UI elements through flexible layouts like flexbox and grid.
- High-level components: like [chat](../../components/chat.md), are built from low-level components and provide ready-to-use elements for common use cases, enabling rapid development.

This layered approach makes deep customization possible. This means that if you want to customize the chat component, you can fork the [chat implementation](https://github.com/mesop-dev/mesop/blob/main/mesop/labs/chat.py) because it's written entirely in Python using Mesop's public APIs.

## See Mesop in Action

To demonstrate the range of UIs possible with Mesop, we built a demo gallery to showcase the types of applications you can build and the components that are available:

<iframe class="immersive-demo" src="https://mesop-dev.github.io/mesop/demo/"></iframe>

 The [demo gallery](https://mesop-y677hytkra-uc.a.run.app/) itself is a Mesop app and [implemented](https://github.com/mesop-dev/mesop/blob/d0b3e286d0dd9de49eb1d5e3bbc1ab84e96a6d08/demo/main.py) in a few hundred lines of Python code. It demonstrates how Mesop can be used to create polished, custom UIs in a maintainable way.

## Try Mesop

If this sounds intriguing, read the [Getting Started guide](../../getting-started/installing.md) and try building your own Mesop app. [Share your feedback and contribute](https://github.com/mesop-dev/mesop/issues) as we continue developing Mesop.

---

--- METRICS ---
- Files Extracted: 108
- Estimated Token Budget: ~64042 tokens
- Canonical Reference: https://codewiki.google/github.com/mesop-dev/mesop
