Package version: 0.4.0
Build steps for JupyterLite sites — turn a Python project and its notebooks into version-matched, pruned and fully offline xeus-python or Pyodide sites, sized for GitHub or GitLab Pages.
Quick Start
jl-forge runs on the build machine (macOS or Linux), from a project's own build script. Pick a kernel, and install jl-forge in the build environment, next to jupyterlite-core and the kernel, not with pipx or uv tool: pyodide-dist reads the Pyodide version from the installed kernel, and the xeus commands import jupyterlite-xeus and empack. In a uv project, uv add --dev does it.
Pyodide site: project wheel via piplite, bundled wheels, self-hosted and pruned Pyodide.
pip install jl-forge jupyterlite-core jupyterlite-pyodide-kernel
jl-forge clean workspace # rm -rf, refused outside the cwd
jl-forge notebooks workspace/content --strip # clear code cell outputs
jl-forge pyodide-install --pyproject pyproject.toml --package-name mypkg --wheel-dir workspace/content
jl-forge notebooks workspace/content --pyodide-install
jl-forge pyodide-wheels --pyproject pyproject.toml --out-dir workspace/lite/pypi
jupyter lite build --contents workspace/content --output-dir workspace/public --lite-dir workspace/lite \
--pyodide "$(jl-forge pyodide-dist --pyproject pyproject.toml --out workspace/pyodide.tar.bz2)"
xeus-python site: cached conda environment, packed without build-only files, project mounted from source. Also needs micromamba.
pip install 'jl-forge[xeus]' jupyterlite-core # the extra adds jupyterlite-xeus
jl-forge clean workspace
jl-forge notebooks workspace/content --strip
PREFIX=$(jl-forge xeus-env --micromamba --cache-dir ~/.cache/jl-forge/xeus --pyproject pyproject.toml)
jupyter lite build --contents workspace/content --output-dir workspace/public \
--XeusAddon.prefix="$PREFIX" \
--XeusAddon.empack_config="$(jl-forge xeus-pack-config --out workspace/empack.yml)" \
--XeusAddon.mounts="$(jl-forge xeus-mount src/mypkg --prefix "$PREFIX")"
After either build: no request to other hosts, shared files stored once, within Pages limits.
jl-forge self-host workspace/public
jl-forge dedup workspace/public
jl-forge size workspace/public --max 1G
jl-forge urls workspace/public # serve command and notebook URLs
Building both sites needs one environment per kernel, as examples/demo/build.sh does: jupyter lite build copies every installed labextension.
Features
- Either kernel: xeus-python (conda prefix, source mount) or Pyodide (wheel + piplite
install.py) - One dependency list: kernel packages come from
[project.dependencies]and extras inpyproject.toml - Correct dependency handling: names normalized, duplicate constraints intersected, markers evaluated for emscripten
- Notebook preparation: strip outputs of code cells only; idempotent install line for Pyodide
- No hard-coded Python version: xeus mount paths follow the Python installed in the prefix
- Voici dashboards: demo and docs for either kernel;
jl-forge urlslists the dashboard pages - Self-hosting: Pyodide pruned to the packages the site uses (
pyodide-dist, 23 MB instead of 340 MB), the wheels piplite installs (pyodide-wheels), and the files xeus and Voici fetch from other hosts (self-host): no request leaves the site - Pages size limits:
deduphardlinks the files sites share,sizefails the build above a limit - Smaller, faster xeus builds: environments packed without build-only files (half the size) and reused between builds
- Safe workspace reset:
cleanrefuses directories outside the project or that look like a project
What is jl-forge?
jl-forge is a small library and CLI extracted from the JupyterLite builds of several projects. It automates the steps around jupyter lite build that JupyterLite's offline guide leaves manual, and that each project used to script by hand:
- Version matching: the self-hosted Pyodide is the release the installed kernel loads.
- Pruning: Pyodide's 340 MB distribution cut to the packages the site uses, with their dependencies; xeus environments packed without build-only files.
- A complete wheel set: the PyPI packages piplite would fetch, with their dependencies, bundled into the site.
- No external requests: also the files xeus and Voici load from other hosts, which the guide doesn't mention; the demo's browser smoke test shows how to verify it.
- Either kernel, with cached xeus environments and size checks for GitHub or GitLab Pages.
A Python API (jl_forge.dependencies, notebooks, xeus, pyodide, wheels, selfhost, artifact) sits behind the CLI.
jl-forge is not a replacement for JupyterLite's build system. Your build script still calls jupyter lite build, chooses the apps, and handles application-specific assets.
Related projects
| Project | Relation |
|---|---|
| jupyterlite-core, jupyterlite-pyodide-kernel, jupyterlite-xeus | The build system and kernels jl-forge drives |
| jupyterlite-pyodide-kernel's lock addon (0.8+) | Solves a custom pyodide-lock.json with uv; resolves better than pyodide-wheels, but doesn't prune or make the site offline, and Voici needs kernel 0.7.0; see COMPATIBILITY.md |
| Voici | Dashboard app jl-forge's sites include |
| jupyterlite-sphinx | JupyterLite embedded in Sphinx documentation, for docs rather than standalone sites |
| Shinylive, marimo | Static WebAssembly exports of their own app and notebook formats; Shinylive also prunes Pyodide |
| pyodide-pack | Shrinks Pyodide apps by detecting the modules they use at run time, a finer cut than pruning whole packages |
Best suited for: projects publishing a Python package with notebooks as a static JupyterLite site.
Note: jl-forge runs on the build machine (macOS or Linux). It installs nothing in the browser itself; see docs/RUNTIME.md.
Feedback and Contributions
jl-forge is an early-stage project. Feedback is particularly welcome on:
- Build steps worth sharing that are still scripted per project.
- JupyterLite, jupyterlite-xeus or Pyodide kernel changes that break the build.
- Support for other apps (Voici, Notebook) and hosting targets.
Please share suggestions, bug reports, and use cases through GitHub Issues.
AI-Assisted Development
Parts of this project were developed with the assistance of generative AI tools. The maintainer is responsible for the architecture, review, testing, and published code.
AI-assisted contributions are welcome, provided contributors understand, review, and test their changes and take responsibility for their submissions.
Documentation
- Getting Started - Development setup, testing, demo build and release
- Usage Guide - CLI reference, Python API, compatibility guarantees and a complete build script
- Apps, Kernels & Version Constraints - Lab, Notebook and Voici on xeus-python and Pyodide: what works, pinned versions and why
- Runtime & Integration - What runs where, and integration boundaries
Example Output
The demo site below is not the tool: it is what jl-forge builds. CI runs examples/demo/build.sh on one notebook and deploys both sites to GitHub Pages. They load nothing from other hosts, the Pyodide site ships a 23 MB pruned distribution, and the notebook reports which kernel runs it.
- xeus-python site —
jl_forge_demomounted from source into the conda prefix. - Pyodide site —
jl_forge_demoinstalled from its wheel by the generatedinstall.py. - Voici dashboards — the same notebook as an app: xeus-python, Pyodide.
How It Works
┌───────────────────────────────────────────┐
│ pyproject.toml notebooks/*.ipynb │
└──────┬──────────────────────────┬─────────┘
│ jl-forge │ jl-forge notebooks
┌──────▼────────────────┐ ┌───────▼──────────────────┐
│ xeus-env / xeus-mount │ │ pyodide-install │
│ conda prefix (wasm) │ │ wheel + install.py │
└──────┬────────────────┘ └───────┬──────────────────┘
│ jupyter lite build │ jupyter lite build
┌──────▼────────────────┐ ┌───────▼──────────────────┐
│ public/xeus/ │ │ public/pyodide/ │
└───────────────────────┘ └──────────────────────────┘
static site → GitHub Pages
License
Apache License 2.0. See LICENSE and NOTICE for details.
Citation
@software{jlforge2026,
author = {Decitre, Emmanuel},
title = {jl-forge: Build tooling for JupyterLite sites},
year = {2026},
url = {https://github.com/decitre/jl-forge}
}
Acknowledgements
jl-forge builds on several open-source projects:
- JupyterLite — the browser-based Jupyter environment.
- xeus-python, jupyterlite-xeus and emscripten-forge — Python kernels and packages for the browser.
- Pyodide and jupyterlite-pyodide-kernel.
Metadata
Release files for jl-forge 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| jl_forge-0.4.0.tar.gz | 29.5 kB | With provenance | Details for jl_forge-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| jl_forge-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for jl_forge-0.4.0-py3-none-any.whl |
Total release size: 57.2 kB
Release files / jl_forge-0.4.0.tar.gz
| Download URL | jl_forge-0.4.0.tar.gz |
|---|---|
| Size | 29.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
78ef97a11532588115f7c729150c6b3e33e1f45fb1240527e3e8e0020d26937c
|
|
BLAKE2b-256 checksum How to use checksums |
15728c1db96b1beadf64ce2cc79dd6e39db5c0293c36da05498d53114e668f38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / jl_forge-0.4.0-py3-none-any.whl
| Download URL | jl_forge-0.4.0-py3-none-any.whl |
|---|---|
| Size | 27.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d2964d1f4424550a933cb9e93aaddddac25be8518a9796da4192d310860d00b7
|
|
BLAKE2b-256 checksum How to use checksums |
d4662880f097181649a442805b70f79121155cb98e746602efe8da9ffeadaba8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for jl-forge 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jl_forge-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.