# streamlit-folium: putting interactive Folium maps inside a Streamlit app

> streamlit-folium bridges two Python projects that do not otherwise meet, giving Streamlit apps an interactive Folium map whose clicks and viewport come back to Python as a dictionary. The read-only folium_static function exists so you can check your Folium syntax before debugging the interactive one.

**randyzwitch/streamlit-folium** — Streamlit Component for rendering Folium maps

- Repository: https://github.com/randyzwitch/streamlit-folium
- Website: https://folium.streamlit.app/
- Stars: 587 · Forks: 223
- Language: Python
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/randyzwitch-streamlit-folium

## Two projects that do not otherwise talk to each other

Streamlit and Folium are both widely used, and they have almost nothing in common. Streamlit is a framework for building data applications where a script reruns on interaction. Folium is a library for building interactive web maps, and it renders to HTML and JavaScript rather than to a Streamlit widget.

streamlit-folium integrates the two, and the project description calls the result geospatial made easy in Streamlit. The mechanism is that a Folium or Branca object, which is a Python description of a map that Folium turns into HTML and JavaScript, is handed to Streamlit as a component, mounted in the page, and the interaction with it comes back to Python.

That round trip is the whole value. A static image of a map cannot tell your application that a user clicked a particular marker, and it cannot tell your application where the user has panned. A Streamlit app that filters a dataframe by the selected region needs exactly that information, and getting it out of a Folium map requires the component mechanism that this project provides.

The audience is Python developers who already use Folium and want it in a Streamlit app, and the homepage is the live demo at https://folium.streamlit.app/. The licence is MIT, the primary language is Python, and the topics list geospatial, folium-maps, visualization and streamlit-component among others.

## Two functions, one interactive and one deliberately static

The usage section says there are currently two functions defined, and the split between them is the most useful thing to understand about the library.

The first is st_folium, described as a bi-directional Component. It takes a Folium or Branca object and plots to the Streamlit app, and upon mount or interaction with the Streamlit app it returns a Dict with selected information including the bounding box and the items clicked on. Bi-directional is the key word. Data flows one way into the map, and the other way out of it, and what comes out is a plain Python dictionary rather than a JavaScript object the application has to unpick.

The second is folium_static. It takes a folium.Map, a folium.Figure or a branca.element.Figure object and displays it in a Streamlit app using the _repr_html() representation created in Folium.

And here is the design decision that makes this worth reading. The documentation says folium_static should be a strict subset of the functionality of the newer st_folium function, but is great for testing to ensure you have the correct Folium syntax.

That is a debugging tool disguised as a rendering function. If your map is blank or broken, the first question is whether your Folium code is wrong at all. Rendering the same object through folium_static answers that without involving the component machinery, the browser and the Streamlit rerun loop. If the static version renders and the interactive one does not, the problem is in the bridge, not in your Folium syntax.

## Installing, and the version constraint that catches people

Installation is the standard pip or conda install.

```python
pip install streamlit-folium

or

conda install -c conda-forge streamlit-folium
```

The runtime dependencies are short: streamlit at 1.13.0 or newer, folium at 0.13 or newer with 0.15.0 explicitly excluded, jinja2 and branca. The folium constraint deserves a second look, because the exclusion of a single patch release in the middle of the range is not an accident of a dependency resolver.

The same exclusion appears in the test dependency group, which also pins streamlit to 1.13.0 or newer with 1.34.0 excluded, alongside pyproj, pyogrio, shapely, pyarrow and geopandas, plus pytest, pytest-playwright and pytest-rerunfailures.

That test stack is the most informative thing in the project metadata. pytest-playwright means the test suite drives a real browser, which is the only way to test a component that returns data from JavaScript back into Python. pytest-rerunfailures means those browser tests are expected to be flaky and are retried. And geopandas, shapely and pyogrio in the test group mean the test fixtures include real geospatial data rather than trivial shapes.

The runtime dependencies themselves are deliberately minimal. If you are using Folium, you already have folium, and branca comes with it as Folium's own element base. jinja2 is a Folium dependency. So adding this to a Streamlit app that already uses Folium is close to a one-line change.

For running a first example, the repository has an examples/ directory with streamlit_app.py, park_app.py, a pages/ subdirectory and a requirements.txt, and the README links a hosted example you can open and edit in Py.Cafe.

## What comes back from the map and into your script

The data flow deserves to be spelled out, because it determines how you write the rest of the application.

You build a Folium or Branca object in Python. You pass it to st_folium, along with whatever else the call takes. The object is serialised and rendered inside the Streamlit page as an interactive map, and the user interacts with it. On mount or on interaction, Streamlit reruns your script, the component is remounted, and the function returns a Dict containing the selected information, which the documentation says includes the bounding box and the items clicked on.

The consequence for application design is that state flows through the return value, not through the map object. A typical pattern is: read the dictionary, look at the last clicked item, and use it to filter or to update something else on the page. Since the return value is a plain dictionary, ordinary Python works on it, no JavaScript interop required.

The bounding box is the other half of the return value, and it enables a different pattern: read the current viewport and use it to query or select the data in view, which is a map-driven filtering approach rather than a click-driven one.

Because every interaction causes a rerun, the Folium object is rebuilt each time. That is a Streamlit property rather than a property of this library, but it applies here, and it is the thing to be aware of when a Folium object is expensive to build or when you see repeated work on each interaction.

## How it compares with building the map yourself

There are two realistic alternatives, and they differ in approach rather than in features.

The first is to skip the component and render the map statically. Folium objects have a _repr_html(), and Streamlit can display raw HTML, so a Folium map can appear in a Streamlit app without this library. You lose click handling and the viewport, and you gain a much smaller dependency surface. If your map is decorative, that is the right trade.

The second is to build the bridge yourself using Streamlit's component API, which is what this project does. That is a substantial amount of work: you need a frontend bundle that talks to the Folium object, a protocol for sending selection and viewport data back, and a rendering of the returned values into Python. Streamlit's own component development documentation requires a build toolchain and a dev server setup. The repository layout shows the shape of the result, with a streamlit_folium/frontend/ directory holding a node_modules tree and a src/ directory, and the project metadata explicitly excluding both from the built distribution while shipping the compiled frontend.

The difference in approach is that this project has already solved the hard part, the bidirectional protocol, and solved it in a way that returns a plain dictionary. The price is a dependency on folium's version constraints and on Streamlit's rerun model.

There is a third option for teams that need map interaction with more control: building the frontend component directly against the Folium JavaScript API rather than going through Folium's HTML output. That gives complete control over the interaction model, at the cost of maintaining a frontend build and a protocol yourself. For a dashboard where a click needs to trigger a complex selection workflow, that is sometimes the honest answer, and for a map where a click selects a row, this library is enough.

## The limits and the upgrade cost

There are three things to know before you commit.

The first is the version pinning, and it is not decorative. The runtime requires folium>=0.13,!=0.15.0 and streamlit>=1.13.0, and the test group excludes streamlit 1.34.0 as well. Any project that has already pinned a different Streamlit major line, or that is on folium 0.15.0, has to resolve that before installing. Excluded versions in a lower bound are the kind of thing that turns into an afternoon of dependency resolution when another package wants the excluded release, and it is worth checking what your environment already has rather than discovering it at install time.

The second is the rerun cost. Every interaction with the map triggers a Streamlit rerun, and the map object is rebuilt each time. For a large Folium object with many markers, that rebuilding cost is paid on every click. A map with tens of thousands of features is where you would feel this, and the fix, if you need it, is to keep the payload small and filter server-side rather than rendering everything.

The third is the test stack cost. pytest-playwright and pytest-rerunfailures mean the project's own test suite needs a browser and tolerates flakiness, and a fork that maintains this component inherits that. The tests/ directory also contains a visual_baseline tree, with a baseline.png for a basic test, which is the usual approach of screenshot comparison, and that is another dependency and another class of environment-dependent failure.

On maintenance, the repository is not archived, the last push was on 2026-10-05, and the recent releases are v0.27.4 on 2026-08-03, v0.27.3 on 2026-07-21 and v0.27.2 on 2026-04-29. The build backend is uv_build with a pinned range, Python 3.11 or newer is required, and the tree carries a Taskfile.yml, a pre-commit configuration and ruff plus mypy configuration, which is a well-equipped repository for a component of this size.

## Conclusion

Adopt streamlit-folium if you are building a Streamlit app that needs an interactive map with click handling or a viewport returned to Python, since st_folium gives you that without writing a custom component. Do not adopt it if a static map image would do, because folium_static is the simpler path. Before you start, check your folium version against the constraint folium>=0.13,!=0.15.0, and note the last push was on 2026-10-05 with release v0.27.4 on 2026-08-03.

## FAQ

### How do I install streamlit-folium?

With pip install streamlit-folium, or with conda install -c conda-forge streamlit-folium. The runtime dependencies are streamlit at 1.13.0 or newer, folium at 0.13 or newer with 0.15.0 excluded, jinja2 and branca, and Python 3.11 or newer is required.

### What does st_folium return to my Streamlit script?

It returns a Dict with selected information, including the bounding box and the items clicked on. The return value is a plain Python dictionary, so the interaction data can be used directly to filter or update the rest of the app.

### What is folium_static for and how is it different from st_folium?

folium_static takes a folium.Map, folium.Figure or branca.element.Figure and displays it using the _repr_html() representation created in Folium, with no interaction. The documentation describes it as a strict subset of st_folium that is great for testing to ensure you have the correct Folium syntax.

### Does streamlit-folium work with any Folium object?

The documentation says st_folium takes a Folium or Branca object, and folium_static takes a folium.Map, a folium.Figure or a branca.element.Figure. Folium versions 0.13 and above are supported, with 0.15.0 excluded by the dependency constraint.

### How is streamlit-folium tested?

The test dependency group includes pytest-playwright, which drives a real browser to test a component that returns data from JavaScript, and pytest-rerunfailures to absorb flakiness in those tests. It also pulls in geopandas, shapely, pyogrio, pyproj and pyarrow for geospatial fixtures.

## Sources

- [License: MIT](https://github.com/randyzwitch/streamlit-folium/blob/master/LICENSE)
- [Project website](https://folium.streamlit.app/)
- [randyzwitch/streamlit-folium on GitHub](https://github.com/randyzwitch/streamlit-folium)
- [README](https://github.com/randyzwitch/streamlit-folium/blob/master/README.md)
- [Releases](https://github.com/randyzwitch/streamlit-folium/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/randyzwitch-streamlit-folium
