Skip to main content

jl-forge — build steps for JupyterLite sites

Tests Build demo Coverage

Package version: 0.3.6

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 in pyproject.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 urls lists 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: dedup hardlinks the files sites share, size fails 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: clean refuses 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.

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

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_demo mounted from source into the conda prefix.
  • Pyodide site — jl_forge_demo installed from its wheel by the generated install.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:


Metadata

Release files for jl-forge 0.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jl-forge 0.3.6
File Size Uploaded Provenance Actions
jl_forge-0.3.6.tar.gz 28.9 kB With provenance Details for jl_forge-0.3.6.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jl-forge 0.3.6
File Interpreter ABI Platform Actions
jl_forge-0.3.6-py3-none-any.whl Python 3 none any Details for jl_forge-0.3.6-py3-none-any.whl

Total release size: 56.3 kB

Release files / jl_forge-0.3.6.tar.gz

Download URL jl_forge-0.3.6.tar.gz
Size 28.9 kB
Tags Source
SHA-256 checksum
How to use checksums
588b376efaff9aa593840ba02427ca2fa5695f6b20e0fb8718501bec576f79fc
BLAKE2b-256 checksum
How to use checksums
40698610f613508fd392306930d03c30cf8b1dba1d9d786b39daa08b1e711a79
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 log

Release files / jl_forge-0.3.6-py3-none-any.whl

Download URL jl_forge-0.3.6-py3-none-any.whl
Size 27.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4a6755fd09fe18700afc72ef1864d1dac4425f59cfdf62460b17b99b75d11fc4
BLAKE2b-256 checksum
How to use checksums
b1d074d222272ca851ca8d91237874dce55c0841f749a192fb49d0420644dc8a
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 log

Security for jl-forge 0.3.6

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.

Select from 2 files

Attestation for jl_forge-0.3.6.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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page