Open-source project
mljar/mercury avatar
mljar/mercury

Mercury: Turn a Jupyter Notebook into a Web App Without a Frontend

Impress your boss and turn a Jupyter notebook into a beautiful, shareable web app — no callbacks, no frontend, no rewrite.

4,355 stars292 forksPythonApache-2.0

At a glance

What is it?
Mercury is an Apache-2.0 Python framework from MLJAR that serves notebooks as interactive web apps, re-running cells when widgets change instead of wiring callbacks. Here is how the mechanism works, how to install it, and where it stops being the right tool.
Who is it for?
Mercury fits Python teams that already have working notebooks and want them reachable in a browser without writing a frontend: install with pip install mercury, start the server with the mercury command, and the notebook becomes an app.
Can I use it commercially?
Yes. Apache-2.0 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 17 days 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 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap Mercury fills between a notebook and a deployed app

A notebook is a fine place to develop an analysis and a poor place to hand one to someone else. The reader needs a Python environment, needs to run cells in order, and needs to ignore the ones that were exploratory. Mercury targets that last step: the notebook stays the source of truth, and the framework presents it as a web application with a layout it supplies.

The README describes the audience indirectly through its examples. It names chats, AI agents, dashboards and reports as the kinds of data-rich applications you can build, and the worked example in the README is an echo chat bot built from a mr.Chat widget, a mr.ChatInput prompt, and mr.Message objects. So the intended user is a Python or data person who wants an interactive surface, not a web developer who wants a framework. The pitch is explicit about what you give up: no callbacks, no frontend, no rewrite.

Reactive notebooks: widget interactions fire cell re-execution

The central design decision is stated plainly in the README: there are no callbacks, and widget interactions fire cell re-execution. That is a different execution model from the usual Python web stack. In a callback model, a widget event maps to a handler function, and only that handler runs. In Mercury, changing a widget causes cells to run again, so the notebook's own top-to-bottom structure is the program.

This is why the layout can be predefined and why there is no UI to write. The framework is not rendering a component tree you assemble; it is executing a document and displaying the results. The README also documents an escape hatch for people who do not want that reactivity: you can uncheck auto re-run in the app preview toolbar, and the web app gets a Run button in the sidebar that triggers cells re-execution on click. That gives you a batch model, run all cells from top to bottom on demand, at the cost of the immediate feedback.

The repository layout matches this split. There is a Python package (mercury), a server package (mercury_app) with a console entry point mercury mapped to mercury_app.__main__:main, and a separate TypeScript side with app/, packages/ and a JupyterLab extension under the @mljar/mercury-extension name. The Python dependencies in pyproject.toml include anywidget, jupyterlab, jupyter_server, jupyterlab_server, toml, markdown, nh3, pandas and questionary. The presence of nh3, an HTML sanitizer, alongside markdown is worth noting: notebook content is rendered as HTML, and the project ships a sanitizer rather than trusting the input.

Installing Mercury and serving your first notebook

Installation is a single pip command. The README gives no virtual environment step, so the packaging is ordinary: the package is published as mercury on PyPI.

bash
pip install mercury

Start the server with the mercury command and no arguments. According to the README, it detects all notebooks in the current directory and serves them as web apps, so the home page becomes a notebook listing rather than a single app.

bash
mercury

To serve notebooks from elsewhere, pass a working directory. The README states this also makes relative paths resolve from that directory, which matters when a notebook reads a CSV by a relative path.

bash
mercury --working-dir /path/to/notebooks

The README shows that you can combine it with a specific notebook argument, so a single file can be served while the working directory still anchors relative paths.

bash
mercury app.ipynb --working-dir /path/to/notebooks

If you want the app reachable from outside the machine, the Dockerfile in the repository is the reference. It uses python:3.12-slim, installs Mercury with pip, sets WORKDIR /workspace, exposes port 8888 and starts the server with the flags below. Note that the README's own Dockerfile example and the repository Dockerfile agree on the port and the flags.

dockerfile
FROM python:3.12-slim
RUN pip install mercury
WORKDIR /workspace
EXPOSE 8888
CMD ["mercury", "--ip=0.0.0.0", "--no-browser", "--allow-root"]

Mount your notebooks at /workspace and publish port 8888. The README's Docker comments show the run pattern with a volume and a port mapping. For a first real use, the README's echo chat bot is the smallest complete app: import mercury as mr, create mr.Chat() and mr.ChatInput(), then append a user message and an assistant message when prompt.value is set.

Customization lives in config.toml, not in your code

Mercury keeps appearance out of the notebook. The README says to create a file called config.toml in the active notebooks directory, which is the current directory by default and the --working-dir value when that flag is used. The repository ships an example.config.toml at the top level, so the shape is discoverable without reading the docs site.

The [main] table carries the app title, footer text, a favicon_emoji, the notebooks_button_label, a starting_message and starting_icon, search filter settings, and thumbnail styling. The README lists the accepted starting_icon values as coffee, spinner, or none. There is also a [welcome] table with header and message. Thumbnails can still be overridden per notebook through notebook metadata, with the [main] values acting as defaults for the whole directory.

toml
[main]
title = "Mercury"
favicon_emoji = "🎉"
starting_icon = "spinner"
show_search_filter = true

[welcome]
header = ""
message = ""

The README ends the customization section by saying the team is actively working on adding more customization options and inviting users to ask for what is missing. Treat that as a signal about the current ceiling: the exposed keys are presentation-level, and there is no documented mechanism in the README for injecting your own CSS or component structure. The repository does contain an example_styles/ directory, which suggests styling is possible, but the README does not document how to use it. If deep visual control is a requirement, that gap is the thing to investigate before adopting.

Authentication is a password flag, and user accounts are not free

There are two levels of access control in the README, and the difference matters for anyone planning an internal deployment. The first is a single shared password passed on the command line.

bash
mercury --pass=your-secret-here

Before opening a notebook, the user is asked for that password. One secret for everyone is adequate for a demo or a small trusted group, and it is not adequate when you need to know who viewed which report, or when someone leaves and you need to revoke exactly their access.

The second level is user-based authentication, and the README states directly that it is a paid option, directing readers to contact mljar.com. This is the sharpest limitation in the project's own documentation. The code is Apache-2.0, but a feature you may consider part of the baseline for a shared deployment is not in the open source package. If your requirements include per-user identity, budget for that conversation or plan to put an authenticating proxy in front of Mercury yourself. The README does not describe such a proxy setup.

The live preview extension and where it does not run

Mercury ships a JupyterLab extension for live app preview during development, opened by clicking an icon in the top notebook toolbar. The README is unusually blunt about its reach: the live app preview extension is available only for JupyterLab and MLJAR Studio, and will not work in Google Colab and VS Code.

That single sentence decides the tool for a lot of people. If your team works in VS Code notebooks, the development loop the README advertises is not available to you, and you are left editing a notebook and restarting or reloading the served app to see the result. The standalone server still works, and the notebook still becomes a web app, but the fast feedback loop that makes the framework pleasant is tied to a specific editor. The packaging reflects this: the wheel installs a JupyterLab extension into share/jupyter/labextensions/@mljar/mercury-extension along with a Jupyter server config, so the integration is with the Jupyter server, not with a generic notebook runtime.

The same toolbar also carries the notebook's presentation settings. The README lists what you can set there: notebook title and description for the home page, emoji and colors of the home page icon, whether to display the notebook code (the README suggests this is useful for teachers), and whether the app should go full width.

Mercury against Streamlit and Gradio, and who should not adopt it

The obvious alternatives are Streamlit and Gradio, and the difference is architectural rather than cosmetic. Both of those are Python-first frameworks where you write a script that constructs widgets and the framework reruns the script on interaction. Mercury inverts the relationship: the notebook is the artifact, and the framework serves it. You do not rewrite the analysis into an app file, and you do not maintain two versions of the same logic.

That inversion is the whole value proposition and also the constraint. In Streamlit and Gradio, the app file is ordinary Python you can structure however you like, with functions, classes and explicit state. In Mercury, the notebook's cell order is the control flow, and the README says widget interactions fire cell re-execution. Cells that are expensive run again whenever a widget changes unless you disable auto re-run and accept the Run button model. Cell-level caching is not described in the README.

So the wrong-tool cases are concrete. If your app needs fine-grained state that survives a browser reload, the README documents no mechanism for it. If you need per-user accounts on your own infrastructure, the paid option is the documented path. If you develop in VS Code or Colab, you lose the live preview. And if the notebook contains steps that take minutes, the reactive model will make the app feel slow in a way that a callback-based framework would not, because only the handler would rerun. Mercury is for teams whose notebooks are already the deliverable and whose interactions are cheap.

Editorial conclusion

Mercury fits Python teams that already have working notebooks and want them reachable in a browser without writing a frontend: install with pip install mercury, start the server with the mercury command, and the notebook becomes an app. It is the wrong choice when you need per-user accounts on a self-hosted install, since the README states user-based authentication is a paid option, and it is the wrong choice for apps whose state must survive a page reload, because widget interactions re-execute cells. Before committing, verify the extension in your actual editor, since the README says the live preview works only in JupyterLab and MLJAR Studio and not in Google Colab or VS Code, and confirm the default port 8888 is free on the host you plan to expose.

Frequently asked questions

What is Mercury (mljar/mercury)?

Mercury is a Python framework that builds interactive web applications directly from Jupyter notebooks. The README describes it as including widgets, a standalone server to serve notebooks as web apps, and a JupyterLab extension for live app preview during development.

How do I install Mercury and start the server?

Install the package with pip install mercury, then run the mercury command. By default it detects all notebooks in the current directory and serves them as web apps, and you can point it elsewhere with mercury --working-dir /path/to/notebooks.

Does the Mercury live app preview work in VS Code or Google Colab?

No. The README states the live app preview extension is available only for JupyterLab and MLJAR Studio and will not work in Google Colab and VS Code. The standalone server still serves the notebook as a web app in those environments.

How do I add a password to a Mercury app?

Start the server with the pass flag, for example mercury --pass=your-secret-here. The README says the user must provide that password before opening the notebook. User-based authentication is described in the README as a paid option.

How do I customize the Mercury app title and appearance?

Create a config.toml in the active notebooks directory (the current directory, or the --working-dir value). The [main] table accepts keys such as title, footer, favicon_emoji, starting_icon, show_search_filter and thumbnail styling.

Official sources

  1. License: Apache-2.0
  2. mljar/mercury on GitHub
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mljar-mercury.svg)](https://hysenlabs.com/projects/mljar-mercury)