Solara: one Python component that runs in a notebook and as a standalone server
A Pure Python, React-style Framework for Scaling Your Jupyter and Web Apps
At a glance
- What is it?
- A pure Python reimplementation of React called Reacton, rendered through ipywidgets so the same component file works in JupyterLab, Colab, Databricks and behind FastAPI. What the monorepo actually contains.
- Who is it for?
- Solara's bet is that the reason Python web apps become unmaintainable is not the absence of a framework but the absence of components, and that fixing that at the widget layer rather than the DOM layer is what lets the same file survive the move from a notebook to production.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 8, 2026, and from our analysis. They are not legal advice.
Editorial analysis
React in Python, delivered through ipywidgets
The pitch is one sentence in the README: Solara is a pure Python implementation of React, called Reacton, creating ipywidget-based applications. Those apps work both inside a Jupyter notebook and as standalone web apps with frameworks like FastAPI.
The routing through ipywidgets is the design decision everything else follows from. Rather than emitting HTML or talking to a virtual DOM, a Solara component is a tree of Jupyter widgets. That buys a very specific kind of portability, and the README names the platforms it gets you: JupyterLab, Jupyter Notebook, Voilà, Google Colab, Databricks and JetBrains Datalore.
The argument the README makes against other Python web frameworks is worth quoting in spirit because it is unusually blunt. Most frameworks out there are designed for small data apps or use paradigms unproven at larger scale, and code organization, reusability and state suffer as apps grow, resulting in either spaghetti code or offloading the real work to a React application. Solara's answer is to import React's component model wholesale rather than invent a Python-shaped one.
GitHub reports the project as MIT licensed with 2,178 stars and 202 forks, the homepage is solara.dev, and the last push was 2026-09-24, so this is active work.
The eleven line example that explains the reactivity model
The README's first script is small enough to read in full and it teaches the whole model. Two reactive variables are declared at the top level, outside the component:
import solara
# Declare reactive variables at the top level. Components using these variables
# will be re-executed when their values change.
sentence = solara.reactive("Solara makes our team more productive.")
word_limit = solara.reactive(10)Then a component, decorated with `@solara.component`, reads those values and renders widgets:
@solara.component
def Page():
# Calculate word_count within the component to ensure re-execution when reactive variables change.
word_count = len(sentence.value.split())Two things are being demonstrated and both are easy to get wrong in a hand-rolled version. First, reactivity is created by `solara.reactive(...)` rather than by a decorator on the component, and the variables live at module scope so that anything can reach them. Second, and this is the comment doing real work, `word_count` is computed inside the component body rather than outside it, which is what causes the component to re-execute when the reactive variables change. Hoisting that calculation out would be more efficient in a normal Python program and would silently break the update.
The widget call takes the reactive object directly as its value:
solara.SliderInt("Word limit", value=word_limit, min=2, max=20)
solara.InputText(label="Your sentence", value=sentence, continuous_update=True)`continuous_update` on the text input means the component re-renders on every keystroke rather than on blur, which is the setting most demos omit.
One file, two runtimes
Installation is a single pip command, and the README links to a longer installation page for anything more complicated:
pip install solaraRunning it is also a single command, and the output tells you where to look:
$ solara run sol.py
Solara server is starting at http://localhost:8765The same code runs in a notebook, and the mechanism is one line. The README notes that the `Page()` expression at the end of the snippet is required only when running in a Jupyter notebook, because calling the component function in a cell is what triggers rendering there. Under `solara run`, the file is executed by the server instead.
The two runtime paths are documented separately under their own headings. The solara server is built on top of Starlette and FastAPI and runs standalone, which the README calls ideal for production use. The Jupyter path works because the components are ipywidgets underneath.
There is a pleasant bit of dogfooding at the end of the README: the solara.dev website itself is built with Solara. And the demo app it links, a scatter plot with filtering and dataset download, is a single file at `solara/website/pages/apps/scatter.py`, so you can read a real application rather than a tutorial fragment.
The manifest names the package solara-ui, the README calls it solara
Here is a real discrepancy, and you should know about it before you file an issue or try to reproduce something. The README says to install with `pip install solara`. The build manifest says something different about what is being built:
[project]
name = "solara-ui"
readme = "README.md"
authors = [{name = "Maarten A. Breddels", email = "[email protected]"}]
license = {file = "LICENSE"}
requires-python = ">=3.8"So the distribution name in this file is `solara-ui`, while the import name is `solara` and the install command in the documentation is `pip install solara`. Both facts are visible and neither is wrong about its own layer: `solara` is the importable package inside the wheel, as the `packages = [ { include = "solara" } ]` line shows, and `solara-ui` is the distribution built from it.
Whether that is intentional or a leftover from a rename is not something this repository settles. What you can do about it is concrete: if you depend on Solara, pin against the name that actually appears in your environment, and if you are reading a bug report about installation failure, check which distribution name the reporter used.
The manifest also pins the build backend hard, requiring hatchling 1.26.3 exactly, and declares a floor of Python 3.8, which is generous given the tooling around it.
A monorepo with a server, an enterprise package and a widget manager
The requirements file at the root reveals that the `pip install solara` package is only the core, and that development happens across several distributions in a `packages/` directory:
.[all]
./packages/solara-server[all]
./packages/solara-enterprise[all]
./packages/solara-meta[all]So there is a separate server package, an enterprise package, a metadata package, and the manifest references a fifth, `packages/solara-widget-manager`, with a bundled Voilà license file.
The core dependency list is short and tells you what Solara considers essential:
dependencies = [
"reacton>=1.9",
"ipywidgets>=7.7",
"ipyvuetify>=1.6.10",
"ipyvue>=1.9.0",
"requests",
"humanize",
"executing",
]Reacton is the React implementation, ipywidgets is the widget layer everything renders through, and ipyvuetify plus ipyvue are the component libraries supplying the actual widgets. `executing` is there to find the source of a call site, which is how Solara knows which frame to re-execute. Optional extras are defined for markdown, for cache via cachetools, and for extra, with an `all` extra that pulls in all three.
State persistence is mentioned in a comment in the manifest describing a redis state backend for opt-in reactive state persistence, imported lazily so it costs nothing unless you turn it on. The repository carries a matching example directory named `examples/state-persistence-failover`, which suggests the failover behaviour of that backend is something the project actively expects people to run into.
Deployment targets, project metadata, and the issue count
A few root level files tell you how this is meant to reach production. There is a `Procfile`, which is a Heroku style declaration, and a `runtime.txt` for pinning a Python version on platforms that want one. There is a `pyinstaller/` directory, so frozen single file builds are a supported output, and a `release.sh` alongside `release.md` and a `.bumpversion.cfg`, which is an older versioning workflow kept alongside a CHANGELOG.
There is also a `SERVER.md` distinct from the README, which is where the standalone server story is presumably documented in more depth than the README's one paragraph gives. And the project ships `AGENTS.md` and `CLAUDE.md` at the root, so contributor guidance is written for current tooling.
The number worth sitting with is 326 open issues against 2,178 stars and 202 forks. That is a high absolute count for a project of this size, and it is the sort of figure that tells you the project is used heavily and that triage is the bottleneck rather than adoption. It does not by itself mean anything is broken, but for anyone evaluating it as a dependency, it is a better signal of where the sharp edges are than the star count is.
The README's stated developer experience commitments are hot code reloading and type hints, and the presence of `mypy.ini`, `mypy-v3.ini`, a `tsconfigbase.json` and a `.pre-commit-config.yaml` is consistent with those claims being taken seriously.
Editorial conclusion
Solara's bet is that the reason Python web apps become unmaintainable is not the absence of a framework but the absence of components, and that fixing that at the widget layer rather than the DOM layer is what lets the same file survive the move from a notebook to production. The repository backs that bet with a proper monorepo: a core package, a separate server package, an enterprise package, a meta package and a Jupyter widget manager, with a Procfile and a runtime file for deployment targets. Two things to check before committing. The distribution name in the manifest is `solara-ui` while the import name and the install command in the README are both `solara`, which is worth understanding before you file anything. And the 326 open issues against 2,178 stars is a real number to factor in. Start with the word limit example, it is eleven lines, and run it both ways with `solara run sol.py` and in a notebook cell.
Frequently asked questions
Is Solara Python or JavaScript?
Pure Python, with no JavaScript build step for your own code. The README calls it a pure Python implementation of React, called Reacton, and components are ordinary Python functions decorated with @solara.component. The frameworks the name Solara itself describes as React-style are about the component model and state, not about the language.
What pip package name should I install for Solara?
The README says `pip install solara`. The build manifest in the repository declares the distribution name as `solara-ui` while the importable package inside it is `solara`, and the development requirements file lists the core package plus separate server, enterprise and meta packages under a `packages/` directory. Check which name your environment actually has before assuming a failed install is your setup.
Can the same Solara app run in Jupyter and as a web app?
That is the central claim, and the mechanism is that components are ipywidgets rather than emitted HTML. The README lists JupyterLab, Jupyter Notebook, Voilà, Google Colab, Databricks and JetBrains Datalore as platforms it runs on, and the same component runs standalone behind a solara server built on Starlette and FastAPI. Running a notebook cell needs the trailing `Page()` call so the component renders.
How does Solara handle state and re-execution?
With `solara.reactive(...)` values declared at module level, and components that re-execute when the reactive variables they read change. The README's own example makes the subtle point: the derived word count is calculated inside the component body specifically so that changing the reactive values causes re-execution, with an inline comment saying so. A redis backend for opt-in reactive state persistence also exists, imported lazily so it costs nothing when unused.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/widgetti-solara)