Write in Jupyter Notebooks. Publish anywhere.
Project description
nb2wb
Write in notebooks. Publish anywhere.
nb2wb turns notebook-native writing into paste-ready HTML for editors that do not understand Jupyter, Quarto, or LaTeX. Start from a notebook, a Markdown article, or a Quarto document, then target a neutral preview wrapper or a profile tuned for Substack, Medium, X Articles, LinkedIn, Dev.to, Hashnode, Ghost, or WordPress.
The project also ships a reverse scaffold. Use wb2nb or nb2wb.revert() to turn published HTML back into a Jupyter notebook when you need to recover prose, code blocks, and image-derived content.
Why People Reach for nb2wb
- Render code cells as syntax-highlighted images so theme and spacing survive copy and paste.
- Render display math as images and inline math as readable Unicode text.
- Keep tables as native HTML or convert them to images when a platform editor is unreliable.
- Wrap output for different publishing targets without rewriting article content.
- Serve extracted images through
--servewhen editors reject embedded data URIs. - Reverse HTML posts back into notebook scaffolds, with OCR as an opt-in upgrade.
- Apply safety limits and HTML/SVG sanitization by default for server-side use.
Supported Inputs
- Jupyter notebooks:
.ipynb - Quarto documents:
.qmd - Markdown documents:
.md - In-memory notebook payloads:
dict/nbformat.NotebookNode - In-memory text payloads: raw strings or
{"format": "...", "content": "..."} - Reverse conversion inputs:
.html,.htm, and in-memory HTML payloads
Installation
Install the base package:
pip install nb2wb
Install extras only when you need them:
pip install "nb2wb[ocr]" # local OCR for reverse conversion
pip install "nb2wb[openai]" # OpenAI-backed OCR pipeline
pip install "nb2wb[gemini]" # Google Gemini-backed OCR pipeline
For development:
git clone https://github.com/the-palindrome/nb2wb.git
cd nb2wb
pip install -e ".[dev]"
Quick Start
Convert a notebook to the default preview wrapper:
nb2wb notebook.ipynb
Target a platform profile:
nb2wb notebook.ipynb -t medium
nb2wb notebook.ipynb -t x
nb2wb notebook.ipynb -t linkedin
Use execution, raw mode, and wrapper overrides when you need them:
nb2wb report.qmd --execute
nb2wb report.ipynb --warnings
nb2wb report.ipynb --raw -o article_raw.html
nb2wb report.ipynb -t ghost --image-strategy embed --article-width 900
nb2wb report.ipynb --serve
nb2wb report.ipynb --verbose
Reverse an HTML article back into a notebook scaffold:
wb2nb article.html
wb2nb article.html -o recovered.ipynb
wb2nb article.html --ocr-pipeline local
OPENAI_API_KEY=... wb2nb article.html --ocr-pipeline openai --model your-model-name
GEMINI_API_KEY=... wb2nb article.html --ocr-pipeline gemini --model gemini-2.0-flash
GEMINI_API_KEY=... wb2nb article.html --ocr-pipeline gemini --model gemini-2.5-flash --verbose
OpenAIOCRPipeline and GeminiOCRPipeline fetch public remote http/https image URLs.
Python API
nb2wb.convert() is content-only. Load files with helpers first, then pass the in-memory payload into the converter.
import nb2wb
payload = nb2wb.load_input_payload("notebook.ipynb")
html = nb2wb.convert(
payload,
target="substack",
config={"latex": {"try_usetex": True}},
verbose=True,
)
You can also enable package logging explicitly:
import nb2wb
nb2wb.configure_logging(verbose=True)
You can also pass text or notebook payloads directly:
import nb2wb
html = nb2wb.convert(
{
"format": "md",
"content": "# Shipping Notes\n\n`nb2wb` handles this in memory.",
},
target="medium",
raw_mode=True,
)
Reverse conversion follows the same pattern:
import nb2wb
payload = nb2wb.load_html_payload("article.html")
notebook = nb2wb.revert(payload)
Add OCR only when you want image-derived notebook cells:
from nb2wb.ocr.openai import OpenAIOCRPipeline
ocr_notebook = nb2wb.revert(
payload,
ocr_pipeline=OpenAIOCRPipeline(model="your-model-name"),
)
Examples
The examples/ directory now covers forward conversion, reverse conversion, API usage, Markdown directives, Quarto {output} chunks, visibility tags, rich HTML/SVG outputs, and target-specific publishing flows.
Useful entry points:
examples/notebook.ipynbexamples/markdown.mdexamples/quarto.qmdexamples/reverse_article.htmlexamples/convert_notebook_api.pyexamples/revert_html_api.py
Security Model
nb2wb keeps the safe path on by default:
- HTML and SVG fragments are sanitized.
- CSS URLs are filtered.
- Remote image fetching is SSRF-safe.
- Local image handling blocks traversal and escape paths.
- Notebook payloads are constrained by configurable size and workload limits.
Execution is different. If you enable --execute or execute=True, treat the notebook as untrusted code and isolate that runtime yourself.
Documentation
- Read the full docs:
https://nb2wb.readthedocs.io/ - Start in the repo:
docs/index.md - Follow the quick path:
docs/getting-started.md - Explore all features:
docs/feature-tour.md - Integrate the API:
docs/python-api.md - Recover notebooks from HTML:
docs/reverse-conversion.md
Development
Run the test suite:
pytest
Build docs locally:
pip install -e ".[docs]"
sphinx-build -b html docs docs/_build/html
The detailed test guide lives in tests/README.md.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nb2wb-0.3.3.tar.gz.
File metadata
- Download URL: nb2wb-0.3.3.tar.gz
- Upload date:
- Size: 95.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19e9ac8018467d1da655816f68a258c93fc2804a92e08184b037c5a3d0509b84
|
|
| MD5 |
4620714e6cae3ce8da1f5e22cdda15f5
|
|
| BLAKE2b-256 |
6b47f30795707e6712d430e303cbe0b8536f4d9029d85c0d53052a630bc97f01
|
Provenance
The following attestation bundles were made for nb2wb-0.3.3.tar.gz:
Publisher:
publish.yml on the-palindrome/nb2wb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nb2wb-0.3.3.tar.gz -
Subject digest:
19e9ac8018467d1da655816f68a258c93fc2804a92e08184b037c5a3d0509b84 - Sigstore transparency entry: 1142128898
- Sigstore integration time:
-
Permalink:
the-palindrome/nb2wb@c325ae29cbc7046fc4bfaedc3ad54e8873f3dbc5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the-palindrome
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c325ae29cbc7046fc4bfaedc3ad54e8873f3dbc5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file nb2wb-0.3.3-py3-none-any.whl.
File metadata
- Download URL: nb2wb-0.3.3-py3-none-any.whl
- Upload date:
- Size: 108.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2f8b44c83dd20ff864858148cd2b4b0de134dc28bb3f968440711c1fe114488
|
|
| MD5 |
56b490e50756b0a5e161e25b99a6f8c8
|
|
| BLAKE2b-256 |
7388047a0f634496e110469cf2778a59224d4720de39b800d7c9c0388dd65548
|
Provenance
The following attestation bundles were made for nb2wb-0.3.3-py3-none-any.whl:
Publisher:
publish.yml on the-palindrome/nb2wb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nb2wb-0.3.3-py3-none-any.whl -
Subject digest:
d2f8b44c83dd20ff864858148cd2b4b0de134dc28bb3f968440711c1fe114488 - Sigstore transparency entry: 1142128955
- Sigstore integration time:
-
Permalink:
the-palindrome/nb2wb@c325ae29cbc7046fc4bfaedc3ad54e8873f3dbc5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the-palindrome
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c325ae29cbc7046fc4bfaedc3ad54e8873f3dbc5 -
Trigger Event:
push
-
Statement type: