Runtime Configuration Files

The behavior JupyterLite in the browser can be controlled by creating specially-named files at any level of the file tree. It is expected each file conforms to the schema. For an example, see the demo configuration.


Config Location






integrates into existing Jupyter workflows


whole file


good for simple/automated configuration




configuration of last resort, not recommended

Each can be omitted from the file tree, and will result in a harmless (though noisy) 404 response.


Configuration cascades down, such that the closest, most-user-editable file to the index.html being served takes highest precedence. Like-named keys will be replaced by higher-priority files, with the notable exceptions of:

  • the federated_extensions and disabledExtensions lists are appended and deduplicated

  • the settingsOverrides dictionary will be merged at the top level of each plugin



The current schema version is 0, and as such is subject to change. Once it has stabilized, we hope to provide similar backwards-compatibility guarantees as the Jupyter Notebook Format.

As the schema provides many options, please see the dedicated pages below.

Adding Content

Content with the CLI

With the CLI installed, run:

jupyter lite build

Any contents found in:

  • {lite-dir}/files/

  • any content roots added via:

    • the CLI flag --contents

    • the #/LiteBuildConfig/contents in jupyter_lite_config.json

Will be:

  • copied to the built site under {output-dir}/files/

    • may have timestamps changed if --source-date-epoch is provided.

  • indexed to provide {output-dir}/api/contents/{subdir?}/all.json

Server Contents and Local Contents

When a user changes a server-hosted file, a copy will be made to the browser’s IndexedDB. A user’s locally-modified copy will take precedence over any server contents, even if the server contents are newer.

Customizing Content Storage

By default, all of a user’s contents on the same domain will be available to all JupyterLite instances hosted there. To create separate content stores, change the jupyter-lite.json#jupyter-config-data/contentsStorageName from the default of JupyterLite Storage.

Adding Extensions

JupyterLab 3 pre-built extensions allow for adding new capabilities to JupyterLite without rebuilding the entire web application. A good starting point for extensions that might work is the JupyterLab issue Extension Compatibility with 3.0 (#9461). Additionally, this site demonstrates a few extensions.

Extensions with the CLI

Environment Extensions

When you run jupyter lite build, all pre-built extensions in your JupyterLab environment, e.g. {sys.prefix}/share/jupyter/labextensions will be:

  • copied to {output-dir}/lab/extensions

  • have its theme information copied to {output-dir}/{app/?}theme/

This discovery behavior can be disabled with the CLI flag --ignore-sys-prefix or LiteBuildConfig/ignore_sys_prefix.

Extensions for a Specific App

Similar to the above, by updating $YOUR_JUPYTERLITE/{app}/jupyter-lite.json, the pre-built extensions will only be available for pages within that file tree.

Custom Extensions

By placing extensions under {lite-dir}/lab/extensions/{org/?}{package}/, these will also be copied into the output-dir after any environment extensions, and all will be added to {output-dir}/jupyter-lite.json#jupyter-config-data/federated_extensions.


For example, after building a lab extension, you can copy the contents of packages.json#/jupyterlab/outputDir right into the lite-dir to preview your extension.

Finally, the --federated-extensions CLI flag and the LiteBuildConfig/federated_extensions config entry allow for adding additional federated extensions, as packaged in Python .whl or conda .tar.bz2 packages.

Customizing Settings

With the CLI, if you create an overrides.json in either the root, or a specific app directory, these will be:

  • merged into {output-dir}/{app?}/jupyter-lite.json#/jupyter-config-data/settingsOverrides

Settings Storage

By default, all of a user’s settings on the same domain will be available to all JupyterLite instances hosted there. To create separate settings stores, change the jupyter-lite.json#jupyter-config-data/settingsStorageName from the default of JupyterLite Storage.

Adding pyolite wheels

The pyolite kernel itself consists of a bit of JavaScript and customized python wheels, which in turn require other wheels and pre-built WASM modules and other JavaScript.

Extra wheels that can be installed via piplite in a running kernel can be added via the --piplite-urls CLI flag or LiteBuildConfig/piplite_urls config value, or simply left in-place in lite_dir/pypi.

These will be:

  • downloaded to the local cache

  • copied into {output-dir}/pypi

  • indexed into an all.json with data similar to the [PyPI Warehouse API]

  • added to pipliteUrls in jupyter-lite.json

If a package is not found in one of these URLs, it will be sought on the main Python Package Index (PyPI). This behavior can be disabled via jupyter-lite.json:

"jupyter-config-data": {
  "litePluginSettings": {
    "@jupyterlite/pyolite-kernel-extension:kernel": {
      "disablePyPIFallback": true


Beneath custom wheels are the raw JS and WebAssembly parts of pyolite provided by pyodide. As the full distribution is very large, and self-hosting of all its assets brings their own challenges, this use of CDN is the default for JupyterLite.

A custom pyodide.js, along with its packages.json and the rest of its assets, such as might be downloaded via the --pyodide CLI option, can also be configured. This can be either relative to the lite_dir, or as a full URL.

"jupyter-config-data": {
  "litePluginSettings": {
    "@jupyterlite/pyolite-kernel-extension:kernel": {
      "pyodideUrl": "./path/to/custom/pyodide/pyodide.js"


Rendering \(\LaTeX\) is generally handled in a special way when compared with most other renderers in JupyterLab. For this reason, it is not presently covered by a pre-built extension, but rather by adding MathJax 2 directly to the page. As it changes very slowly, and is relatively benign if missing for most use cases, this use of a CDN is the default for JupyterLite.

Configuring fullMathjaxUrl and mathjaxConfig in jupyter-lite.json allows you to specify a relative or remote location, replacing (or avoiding) the CDN. If jupyter-server-mathjax is installed, the default configuration TeX-AMS-MML_HTMLorMML-full,Safe will be copied into the output folder.

About the Demo

This documentation site contains the JupyterLite Demo (the Try buttons on the top of the screen) and uses a number of techniques described on this page.

Demo Configuration

The following generated configuration powers the Demo, and is generated prior to building the docs site, copied in during the build, and fetched by browsers from /_static/jupyter-lite.json.

{ “jupyter-config-data”: { “appName”: “JupyterLite Examples”, “appUrl”: “./lab”, “appVersion”: “0.1.0-alpha.20”, “baseUrl”: “./”, “collaborative”: true, “disabledExtensions”: [ “@jupyterlab/server-proxy”, “jupyterlab-server-proxy”, “nbdime-jupyterlab” ], “exposeAppInBrowser”: true, “faviconUrl”: “./lab/favicon.ico”, “federated_extensions”: [ { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.f6574b55a223d23e7a04.js”, “name”: “@jupyter-widgets/jupyterlab-manager” }, { “liteExtension”: false, “load”: “static/remoteEntry.cdf08c015a8192358158.js”, “mimeExtension”: “./mimeExtension”, “name”: “@jupyterlab/fasta-extension”, “style”: “./style” }, { “liteExtension”: false, “load”: “static/remoteEntry.a6d92e11d5e8c375cfe6.js”, “mimeExtension”: “./mimeExtension”, “name”: “@jupyterlab/geojson-extension”, “style”: “./style” }, { “extension”: “./extension”, “liteExtension”: true, “load”: “static/remoteEntry.4ba93726087006d2f61b.js”, “name”: “@jupyterlite/p5-kernel-extension”, “style”: “./style” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.c7d5ca84bbdfef35f278.js”, “name”: “@telamonian/theme-darcula” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.145e469029155b53b350.js”, “name”: “@timkpaine/jupyterlab_miami_nights” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.e9a229a6deef8e3c895b.js”, “name”: “bqplot” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.51b89f201a0af1cc6ef5.js”, “name”: “ipycanvas” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.422e89a36ce1df74544d.js”, “name”: “jupyter-cytoscape” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.8c5efb3516d04e6a5bb7.js”, “name”: “jupyter-leaflet” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.a458e80b4361f068e82d.js”, “name”: “jupyter-matplotlib” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.8b2d16b6f8ed831ddea6.js”, “name”: “jupyter-vue” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.fcff606ae435a57ff541.js”, “name”: “jupyter-vuetify” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.8f896d6ad92823566c46.js”, “name”: “jupyterlab-drawio”, “style”: “./style” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.c6e727a2dea5913e60d7.js”, “name”: “jupyterlab-kernelspy”, “style”: “./style” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.7b0dfd91a418a1e7eb37.js”, “mimeExtension”: “./mimeExtension”, “name”: “jupyterlab-plotly” }, { “extension”: “./extension”, “liteExtension”: false, “load”: “static/remoteEntry.5a1869d4304cb9223cfc.js”, “name”: “jupyterlab-tour”, “style”: “./style” } ], “fullLabextensionsUrl”: “./lab/extensions”, “fullMathjaxUrl”: “./static/jupyter_server_mathjax/MathJax.js”, “fullStaticUrl”: “./lab/build”, “litePluginSettings”: { “@jupyterlite/pyolite-kernel-extension:kernel”: { “pipliteUrls”: [ “./pypi/all.json?sha256=c8bc6665cd9025d3a02556e639cc3fd2ef99e2a6b82484204d8ab746acdbaa93” ] } }, “mathjaxConfig”: “TeX-AMS-MML_HTMLorMML-full,Safe”, “settingsOverrides”: { “jupyterlab-tour:user-tours”: { “tours”: [ { “id”: “jupyter-lite”, “label”: “Welcome to JupyterLite”, “options”: {}, “steps”: [ { “content”: “This is JupyterLite”, “target”: “#jp-MainLogo” } ] } ] } }, “settingsUrl”: “./lab/build/schemas”, “themesUrl”: “./lab/build/themes” }, “jupyter-lite-schema-version”: 0 }

Demo Extension Notes

The federated_extensions above are copied from the documentation environment prior to building this site with Sphinx, and are meant to exercise different kinds of extensions, including themes, MIME renderers, and Widgets. Some transient dependencies also include labextensions, but don’t work entirely correctly.



working issue


needs Jupyter Kernel Comms



needs server extension


needs server extension

The Hard Way

Content, The Hard Way


  • you have a running JupyterLab 3

  • you want to add all of the files in the root folder of the current JupyterLab to your JupyterLite.

Open a browser:

  • view the Contents API, e.g. http://localhost:8888/api/contents, which should look something like:

  "name": "",
  "path": "",
  "last_modified": "2021-05-15T20:16:17.753908Z",
  "created": "2021-05-15T20:16:17.753908Z",
  "format": "json",
  "mimetype": null,
  "size": null,
  "writable": true,
  "type": "directory",
  "content": [
      "name": "",
      "path": "",
      "last_modified": "2021-05-15T20:12:22.261076Z",
      "created": "2021-05-15T20:12:22.261076Z",
      "content": null,
      "format": null,
      "mimetype": "text/markdown",
      "size": 3735,
      "writable": true,
      "type": "file"
  • Paste this JSON in $YOUR_JUPYTERLITE/api/contents/all.json

  • Copy your files in $YOUR_JUPYTERLITE/files

  • Repeat this for every subfolder :(

Now, when the app reloads, these files will appear in the File Browser if there isn’t an existing file of that name in browser storage. If a user has created such a file, and is deleted, the original server-backed file will become visible.

Extensions, The Hard Way


This is a heavily work-in-progress procedure, and will hopefully soon be improved with convenience tools in (at least) python and JavaScript.

Get the extension assets

Assuming you have a working JupyterLab 3 installation, look in your {sys.prefix}/share/jupyter/labextensions. Each folder contains either:

  • if it begins with @, a collection of packages

  • otherwise, a single pre-built extension

mkdir -p lab/extensions
cd lab/extensions
cp -r $PREFIX/share/jupyter/labextensions/@jupyter-widgets/jupyterlab-manager .


Some extensions will require other extensions to be available. This can be determined by looking in package.json for the extension, specifically #/jupyterlab/sharedPackages.

Handle theme assets

The Theme Manager expects to be able to load theme CSS/font assets from {:app}/build/themes/({:org}/){:package}, where app is usually lab.

Continuing the example above:

cd $YOUR_JUPYTERLITE/lab/extensions
mkdir -p ../build/themes
cp -r @*/*/themes/* ../build/themes/
cp -r @*/themes/* ../build/themes/
# To also ensure these are available for JupyterLite Retro:
mkdir -p ../../retro/build/themes
cp -r @*/*/themes/* ../../retro/build/themes/
cp -r @*/themes/* ../../retro/build/themes/

Fill Out federated_extensions

Again, assuming you have a working JupyterLab, click Inspect Element in your Lab and inspect the <script id="jupyter-config-data"> in the <head>. The entry you need will be contained there.

Update your /app/jupyter-lite.json like so:

  "federated_extensions": [
      "extension": "./extension",
      "load": "static/remoteEntry.ca1efc27dc965162ca86.js",
      "name": "@jupyter-widgets/jupyterlab-manager"


Some extensions also include a style key, and may look off if omitted.