Contributing#
Thanks for contributing to JupyterLite!
We follow Project Jupyter’s Code of Conduct for a friendly and welcoming collaborative environment.
Setup#
Prerequisites#
You’ll need:
gitnodejs >=24,<25python >=3.10
Tip: You can use any Python package manager you prefer (pip, conda, etc.) for
installing Python dependencies.
Quick Start#
Install all dependencies and set up the dev environment:
# 1. Install build dependencies (includes JupyterLab which provides `jlpm`)
pip install --group build
# 2. Install Node.js dependencies and Python packages
jlpm install
jlpm install:py
The jlpm install:py command installs remaining Python dependencies and packages in
editable mode.
You can also install dependencies manually:
pip install --group dev # Core dev tools
pip install --group all-dev # All dev dependencies (dev + docs + lint + test)
# Python package in editable mode
pip install -e './py/jupyterlite-core[all,test]'
Available dependency groups:
Group |
Description |
|---|---|
|
Minimal build dependencies (hatch, jupyterlab) |
|
Core development (includes |
|
Documentation building (Sphinx, themes) |
|
Linting tools (ruff, pre-commit) |
|
Testing (pytest and plugins) |
|
Demo site extensions (widgets, libraries) |
|
UI/Playwright tests (includes |
|
Release process (includes |
|
All dev dependencies (dev + docs + lint + test) |
Note: Dependency groups (PEP 735) require pip 24.1+ or uv.
Then run the build command:
jlpm build
Development Workflow#
Local Development#
Command |
Description |
|---|---|
|
Build and watch for changes (creates |
|
Serve |
|
One-off build (no watching) |
The recommended workflow uses two terminal windows:
# Terminal 1: Watch and rebuild on changes
jlpm dev:watch
# Terminal 2: Serve the site (run after Terminal 1 creates _site/)
jlpm dev:serve
Refresh your browser after changes. For Python package changes or new extensions,
restart dev:watch.
Directory Structure#
Directory |
Purpose |
|---|---|
|
The JupyterLite application shell (built from |
|
TypeScript source packages (core libraries, plugins, extensions) |
|
Python packages (CLI) ( |
|
Demo content: notebooks, |
|
Build output directory (contains |
|
Playwright end-to-end and visual regression tests |
|
Sphinx documentation source |
Adding Kernels and Extensions#
Install kernels and extensions with pip:
pip install jupyterlite-pyodide-kernel
jlpm dev:build # Rebuilds site with the new kernel
Repository Integrity#
The repository has integrity checks to ensure consistency across package files.
App Resolutions#
Each app (app/lab, app/notebooks, etc.) has a resolutions field in its
package.json that pins dependency versions. Resolutions must stay in sync with
dependencies - if you update a dependency version, the corresponding resolution must
also be updated.
jlpm integrity
Run jlpm integrity after updating dependencies (e.g., bumping JupyterLab versions) to
sync the resolution entries.
Yarn Lock Deduplication#
The yarn.lock file should be deduplicated to minimize dependency duplication:
jlpm deduplicate
Updating Package Dependencies#
To update a dependency across the monorepo:
jlpm up "<package-pattern>"
For example, to update all @jupyterlab/* packages to the latest version:
jlpm up "@jupyterlab/*"
After updating dependencies, run jlpm integrity to sync app resolutions.
Upgrading JupyterLab and Notebook Versions#
When a new JupyterLab or Notebook release is available, use the upgrade script to update
dependencies across the repository. At least one of --jupyterlab-version or
--notebook-version must be specified:
# Update JupyterLab to latest stable
python scripts/upgrade-dependencies.py --jupyterlab-version latest
# Update Notebook to latest stable
python scripts/upgrade-dependencies.py --notebook-version latest
# Update both to latest stable
python scripts/upgrade-dependencies.py --jupyterlab-version latest --notebook-version latest
This script:
Fetches the specified releases from GitHub
Updates version constraints in
pyproject.tomlUpdates all
@jupyterlab/*,@lumino/*,@jupyter/*, and@jupyter-notebook/*dependencies inpackage.jsonfiles to match upstream versionsOnly updates packages for which a version argument is provided
Common Usage Patterns#
# Preview changes without modifying files
python scripts/upgrade-dependencies.py --jupyterlab-version latest --dry-run
# Update to the latest pre-release versions
python scripts/upgrade-dependencies.py --jupyterlab-version next --notebook-version next
# Update to specific versions
python scripts/upgrade-dependencies.py --jupyterlab-version 4.4.0 --notebook-version 7.4.0
After Running the Script#
jlpm install # Update yarn.lock
jlpm deduplicate # Clean up duplicate dependencies
jlpm integrity # Sync app resolutions
jlpm build # Verify the build succeeds
GitHub Token for API Rate Limiting#
Set the GITHUB_TOKEN environment variable to avoid GitHub API rate limiting
(unauthenticated requests are limited to 60/hour, authenticated to 5,000/hour). For
local development, create a Personal Access Token:
Classic PAT: No scopes required (public repo read access is implicit)
Fine-grained PAT: Select “Public Repositories (read-only)”
In GitHub Actions, the CI workflow uses PERSONAL_GITHUB_TOKEN (a PAT stored as a
repository secret) to create PRs. This is required because PRs created with the
automatic GITHUB_TOKEN won’t trigger CI checks on the PR itself.
Automated Upgrade via GitHub Actions#
You can trigger the upgrade workflow directly from GitHub Actions from a fork, which automates the entire process (running the script, updating lock files, and creating a PR).
Setting Up the Token in Your Fork#
Before running the workflow from a fork, you need to create a PERSONAL_GITHUB_TOKEN
repository secret:
Create a Personal Access Token with these permissions:
Classic PAT:
reposcope (to push branches and create PRs)Fine-grained PAT: Select the target repository with “Contents” (read/write) and “Pull requests” (read/write) permissions
Go to your fork’s Settings → Secrets and variables → Actions
Click New repository secret
Name it
PERSONAL_GITHUB_TOKENand paste your token
Running the Workflow#
Go to Actions → Upgrade JupyterLab/Notebook dependencies in the GitHub repository
Click “Run workflow”
Fill in the parameters:
JupyterLab version: Version number or
latest(default:latest)Notebook version: Version number or
latest(default:latest)Branch: Target branch for the PR (default:
main)Target repository: Where to create the PR (default:
jupyterlite/jupyterlite)
Click “Run workflow”
The workflow will create a PR with all necessary changes if updates are available.
Demo Site Dependencies#
The demo site extensions are defined in the demo dependency group in pyproject.toml.
File |
Purpose |
|---|---|
|
Source of truth for demo extensions |
|
Lock file with pinned versions (generated) |
|
Additional packages for piplite bundling |
|
Contains generated |
Updating Demo Dependencies#
When you change the demo dependency group in pyproject.toml, regenerate the lock
file and piplite URLs:
python scripts/compile-lock-files.py
This will update both examples/requirements-demo.txt and the piplite_urls in
examples/jupyter_lite_config.json. Commit these generated files.
Note: This script uses uv if available, otherwise falls back to pip-compile
(from pip-tools).
Documentation#
Install the docs dependency group first:
pip install --group docs
Command |
Description |
|---|---|
|
Build Sphinx documentation |
|
Serve docs on http://localhost:8000 |
|
Watch mode with auto-rebuild |
Testing#
Python Tests#
jlpm test:py
UI Tests (Playwright)#
JupyterLite uses Galata for end-to-end and visual regression testing.
Note: Complete the Quick Start setup first - UI tests require
jupyterlite-coreto be installed.
# Install the ui-tests dependencies
pip install --group ui-tests
cd ui-tests
# Install dependencies
jlpm
# Install Playwright browsers
jlpm playwright install
# Build the JupyterLite app used in the tests
jlpm build
# Run the tests
jlpm test
To run in headed mode:
jlpm test --headed
To update snapshots:
jlpm test:update
See Playwright Command Line Reference for more options.