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:

  • git

  • nodejs >=24,<25

  • python >=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

build

Minimal build dependencies (hatch, jupyterlab)

dev

Core development (includes build)

docs

Documentation building (Sphinx, themes)

lint

Linting tools (ruff, pre-commit)

test

Testing (pytest and plugins)

demo

Demo site extensions (widgets, libraries)

ui-tests

UI/Playwright tests (includes build)

release

Release process (includes build + lint)

all-dev

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

jlpm dev:watch

Build and watch for changes (creates _site/ directory)

jlpm dev:serve

Serve _site/ on http://localhost:8000

jlpm dev:build

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

app/

The JupyterLite application shell (built from packages/)

packages/

TypeScript source packages (core libraries, plugins, extensions)

py/

Python packages (CLI) (jupyterlite-core, jupyterlite)

examples/

Demo content: notebooks, jupyter_lite_config.json, and requirements

_site/

Build output directory (contains build/ symlink to app/build/)

ui-tests/

Playwright end-to-end and visual regression tests

docs/

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

  • Updates all @jupyterlab/*, @lumino/*, @jupyter/*, and @jupyter-notebook/* dependencies in package.json files to match upstream versions

  • Only 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:

  1. Create a Personal Access Token with these permissions:

    • Classic PAT: repo scope (to push branches and create PRs)

    • Fine-grained PAT: Select the target repository with “Contents” (read/write) and “Pull requests” (read/write) permissions

  2. Go to your fork’s Settings → Secrets and variables → Actions

  3. Click New repository secret

  4. Name it PERSONAL_GITHUB_TOKEN and paste your token

Running the Workflow#
  1. Go to Actions → Upgrade JupyterLab/Notebook dependencies in the GitHub repository

  2. Click “Run workflow”

  3. 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)

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

pyproject.toml (demo group)

Source of truth for demo extensions

examples/requirements-demo.txt

Lock file with pinned versions (generated)

examples/requirements-piplite.txt

Additional packages for piplite bundling

examples/jupyter_lite_config.json

Contains generated piplite_urls

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

jlpm docs:build

Build Sphinx documentation

jlpm docs:serve

Serve docs on http://localhost:8000

jlpm docs:watch

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-core to 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.