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.
- 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.
- 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
Install the base package:
pip install nb2wbInstall 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 pipelineFor development:
git clone https://github.com/the-palindrome/nb2wb.git
cd nb2wb
pip install -e ".[dev]"Convert a notebook to the default preview wrapper:
nb2wb notebook.ipynbTarget a platform profile:
nb2wb notebook.ipynb -t medium
nb2wb notebook.ipynb -t x
nb2wb notebook.ipynb -t linkedinUse 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 --verboseReverse 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 --verboseOpenAIOCRPipeline and GeminiOCRPipeline fetch public remote http/https image URLs.
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"),
)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
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.
- 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
Run the test suite:
pytestBuild docs locally:
pip install -e ".[docs]"
sphinx-build -b html docs docs/_build/htmlThe detailed test guide lives in tests/README.md.
MIT