Skip to main content

Publishing a static website with Pyodide

Flet app can be published as a standalone static website (SPA) and run entirely in the browser with Pyodide.

Pyodide is a port of CPython to WebAssembly (WASM) which is an emerging technology with some limitations. Pyodide comes with a big list of built-in packages. However, to use a Python package from PyPI it must be a pure Python package or provide a wheel with binaries built for Emscripten.

Flet static vs server-side

Flet static pros:

  • Zero latency between user-generated events (clicks, text field changes, drags) and page updates. There is no Fletd server, no WebSockets - Python program communicates with Flutter web client directly via JavaScript.
  • Cheap hosting - Flet static app does not require any code to run on the server and thus can be hosted anywhere: GitHub Pages, Cloudflare Pages, Replit, Vercel, a shared hosting or your own VPS.
  • Higher scalability - Flet static app runs entirely in the browser and if it doesn't use any server-side API it could be scaled to any number of users with just CDN.

Flet static cons:

  • Slower loading time - it requires additional time to download Python engine (Pyodide), built-in and flet-pyodide packages, and a user program. Besides, initialization of Pyodide engine itself takes around 2-4 seconds which the team is looking to improve in the future.
  • Limited Python compatibility - not every program that works with native Python can be run with Pyodide.
  • Lower performance - Pyodide is currently 3x-5x slower than native Python, so Flet apps with heavy processing would be better deployed as a server-side.
  • Users can access program code as it's downloaded by a browser in the form of tar.gz archive.

Async or not async?

Both asyncio and "regular" sync Flet apps can be published as a static website. In terms of concurrency, the website will have only one thread with a single user only - you. If your app has CPU-intensive logic it may affect UI responsivness no matter the app is async or not.

However, if your app contains an I/O logic (like fetch wrapper for Pyodide) which is async in browser by definition then your app must be async.

Publish app as a static website

Run the following command to publish Flet app to a standalone website:

flet publish <>

A static website is published into ./dist directory.

Command optional arguments:

  • --pre - allow micropip to install pre-release Python packages.
  • -a ASSETS_DIR, --assets ASSETS_DIR - path to an assets directory.
  • --app-title APP_TITLE - application title.
  • --app-description APP_DESCRIPTION - application description.
  • --base-url BASE_URL - base URL for the app.
  • --web-renderer {canvaskit,html} - web renderer to use.
  • --route-url-strategy {path,hash} - URL routing strategy.

Testing website

You can test a published Flet app using Python's built-in http.server module:

python -m http.server --directory dist

Open http://localhost:8000 in your browser to check the published app.

Loading packages

You can load custom packages from PyPI during app start by listing them in requirements.txt. requirements.txt must be created in the same directory with <>.

Each line of requirements.txt contains a package name followed by an optional version specifier.


To install custom packages Pyodide uses micropip - a lightweight version of pip that works in a browser.

You can use Micropip API directly in your Flet app:

import sys

if sys.platform == "emscripten": # check if run in Pyodide environment
import micropip
await micropip.install("regex")

Pre-release Python packages

You can allow loading pre-release versions of PyPI packages, by adding --pre option to flet publish command:

flet publish <> --pre


If your app requires assets (images, fonts, etc.) you can copy them into website directory by using --assets <directory> option with flet publish command:

flet publish <> --assets assets

If you have assets directory in your app's directory and don't specify --assets option then the contents of assets will be packaged along with a Python application rather than copied to dist.

URL strategy

Flet apps support two ways of configuring URL-based routing:

  • Path (default) - paths are read and written without a hash. For example,
  • Hash - paths are read and written to the hash fragment. For example,

If a hosting provider supports Single-page application (SPA) rendering you can leave default "path" URL strategy as it gives pretty URLs.

However, if a hosting provider (like GitHub Pages) doesn't support SPA mode then you need to publish your app with "hash" URL strategy.

To specify "hash" URL strategy during static app publishing use --route-url-strategy option:

flet publish <> --route-url-strategy hash

Web renderer

You can change default "canvaskit" web renderer (more about renderers here) to "html" with --web-renderer option:

flet publish <> --web-renderer html

Hosting website in a sub-directory

Multiple Flet apps can be hosted on a single domain - each app in it's own sub-directory.

To make a published Flet app work in a sub-directory you have to publish it with --base-url option:

flet publish <> --base-url <sub-directory>

For example, if app's URL is then it must be published with --base-url myapp.

Deploying website

Deploy a static website to any free hosting such as GitHub Pages, Cloudflare Pages or Vercel!

Cloudflare Pages

Before we get to the deployment, you will need an account. Get one from here, or simply login if you already have one. After signing up, you will have to verify your email address by clicking the link you will receive in your email. Check the spams too, if you don’t get it in your inbox.

In your account, from the side menu, select "Pages" as shown below:

And from there, select the “Create a project” button:

Cloudflare proposes three ways to create a project. Only the first two will be exposed here:

  • Connect to a git provider
  • Direct upload

Connecting to a Git provider

For this, you will need to have a GitHub or GitLab account. In this account should be the repository you plan to use. An example could be found in this repo.

Click on the “Connect to Git” blue button:

From there, select the tab with the service containing your repository. Then, connect your account. Select one of the suggested options, then click on “Install & Authorize”.

Choose the repository to be used, and press on the “Begin setup” button.

Before moving on, add a runtime.txt file in your repo. It should contain the python version to be used. In the file enter 3.7 which is the latest python version Cloudflare uses at time of writing.

Here is an example from the repo above.

Having this done, we can now move to the next step which will be to configure some build and deployment settings for your site.

Set the name of your project, and the production branch to be used. The production branch is simply the branch of your repo to which any push of a commit will automatically trigger a deployment to your production environment. Pushes to your other branches will trigger deployment instead to your preview environment.

After setting these two, jump down to the “Build settings” section where we will be setting up the build instructions.

Skip the “Framework preset” (allow None) because Flet is neither in the list nor a JavaScript framework :)

The “Build command” depends on your application's structure. Follow the guide in the sections above to come up with your custom build command.

When the build command is ran by flet, a folder named ‘dist’ is created which will contain all the web files required by Cloudflare pages. Set it as your output directory. Note that this file will not be added to your repository, because Cloudflare only has read access to your code.

You could optionally specify advanced parameters: the root directory (the directory in which Cloudflare runs the build command), and Environment variables (variables to be used during build time). Now, click on the “Save and Deploy” button and let Cloudflare do the remaining job for you.

Click on the URL that will be shown to move to your deployed site.

If when opening the site you see a Cloudflare error, it means they haven’t completely finished the setup. So, simply wait for a minute then refresh the page, and you will see your application running. Test the above deployed site here.

Direct Upload

Click on “Upload assets”. In step one, give your project a name. Remember that the name you will give will be used to determine the link to which your project will be deployed to.

The second step requires you to upload your project’s assets, either as folder or a zip file (with all the assets inside). If you already have one of them, then use the drag-and-drop or select them using the folder picker.

If you don’t yet have these assets but already have an app you've built, use the flet publish command in the directory containing your app files, and a dist folder will be created which you will then upload to Cloudflare pages as mentioned above.

After the upload press on “Deploy site” button at the bottom.

You will then see a success message with a link to your deployed website. Test an example of a deployed site here.

If when opening the site you see a Cloudflare error, it means they haven’t completely finished the setup. So, simply wait for a minute then refresh the page, and you will see your application running. You can now click on the “Continue to project” button to monitor your deployments, or create new ones following the same steps above.


When Flet app is running in a web browser all its print() statements are displayed in "Console" tab of Developer Tools in a browser. print() can be used as a simple debugging tool.

You can also use logging module and output messages to Console with different severity.

To enable detailed Flet logging add this to your program:

import logging