Jupyter Notebook Markdown Image: Keep the Attachment With Its Cell

An image can disappear when only Markdown text is copied into another cell. Keep the attachment reference and its cell’s image bytes together when preparing a portable notebook.

The example includes 1×1 RGB PNG bundles and complete notebook JSON text. Inspect each cell’s own dot.png reference, then remove one bundle to see why another cell’s bytes cannot repair it.

Jupyter Notebook Markdown Image: Keep the Attachment With Its Cell
Jupyter Notebook Markdown Image: Keep the Attachment With Its Cell
Open the full example ↗

Understand the cell-local reference

Use an original two-cell specification to make the scope visible:

Cell ID Markdown source Its own attachment Expected decoded pixel
image-a ![Original fixture](attachment:dot.png) dot.png, image/png bundle Opaque RGB (255, 0, 0)
image-b ![Original fixture](attachment:dot.png) dot.png, image/png bundle Opaque RGB (0, 0, 255)

Both cells use the filename dot.png. They intentionally carry different original image bytes. In this fixture, each attachment is a 1×1 opaque RGB PNG: red for image-a and blue for image-b.

The reference in image-a belongs with image-a's bundle. It is not an instruction to take the other cell's image simply because the filenames match. Keep each source and attachment pair identifiable by cell ID.

Create real bytes before writing the manifest

Record the decoded PNG byte hash, dimensions, mode, and pixel value for each cell. Identical decoded image properties do not require every encoder to produce identical PNG bytes, so keep the chosen original bytes and their hashes as the fixture evidence.

Test portability by moving only the notebook

The positive test should use a new isolated folder containing only a copy of the completed notebook. Add no external dot.png file and no helper image directory. Reopen it in the confirmed native editor and render its Markdown cells without executing code cells or running a kernel for this test.

Expected result: image-a uses its red bundle and image-b uses its blue bundle. Verify that expectation against the actual reopened display, not just the source text. A one-pixel image may be visually small; pair native rendering inspection with the decoded-pixel checks rather than silently enlarging or changing the underlying fixture.

Keep an acceptance ledger with the notebook revision, editor version, destination-folder inventory, cell ID, requested reference, attachment filename, MIME type, decoded-byte hash, expected pixel, and actual rendering observation.

The absence of external images is part of the test evidence. Seeing a correct image in the original working folder does not by itself prove that the notebook is portable.

Preserve missing-reference failures

In a separate negative fixture, remove image-a's dot.png bundle while leaving its Markdown source unchanged. The expected result is an unresolved attachment reference in that cell. Do not repair it by borrowing image-b's bundle or searching the destination directory for a matching filename.

A second negative case copies only the Markdown source into another cell without the attachment bundle. That is not a complete portable copy. If the destination schema requires a cell ID, assign a new valid unique ID; copying an existing ID would introduce another independent structural problem.

Keep expected and actual outcomes separate. The native editor may represent an unresolved reference differently, so record what it actually displays instead of inventing a universal error message. If a source reference or bundle changes, invalidate its old acceptance record until the saved artifact has been checked again.

Keep three kinds of evidence apart

Schema validation answers whether the notebook follows the selected format. Decoding the bundle answers whether it contains the expected PNG bytes and pixel. Reopening and rendering answers how the chosen editor resolves that cell's reference. Passing one check does not silently pass the other two.

This exercise makes no promise about drag-and-drop insertion, cleanup of unused attachments when saving, or preservation through HTML export. Those are separate editor or exporter behaviors to verify if your workflow needs them.

Try the worked example

Inspect image-a to find its red bundle, then image-b for its separate blue bundle. Remove A’s bundle and inspect image-a again: its reference stays unresolved even though B has dot.png. Restore before carrying the JSON into a native notebook test. A filename does not make an attachment notebook-global.

This inspects JSON and displays embedded PNGs in HTML, not a native notebook editor. Schema validation and moving/reopening the notebook remain separate tests.

Recreate this example

Build one complete self-contained HTML page for this task: inspect two original cell-local attachment bundles and provide complete notebook JSON text.

Use these exact original practice data and worked expectations: Use notebook format 4.5 with unique cell IDs image-a and image-b. Both cells contain ![Original fixture](attachment:dot.png). Cell A owns a red 1×1 opaque RGB PNG; cell B owns a blue 1×1 PNG. Removing A’s bundle leaves A unresolved; it cannot borrow B’s bundle. Native schema, reopening, and rendering checks remain separate.

Implement the following useful local interactions: Cell-id input, Inspect, Remove A bundle, Restore. Show both original tiny images magnified with labels and complete JSON text. Construct genuine PNG bytes, not guessed base64. Inspect only the selected cell’s filename/MIME bundle; update JSON when bundle changes. No native notebook render claim. Preserve the supplied source text and distinguish an uncomputed state, a successful local result, and an invalid or incomplete input. Update output only from the current input snapshot. An input edit must retire stale feedback until the reader runs the relevant action again. Do not silently invent missing values or replace a failing case with a success.

Design the page as a compact source-and-result developer workbench with readable code, labeled inputs, and a separate explanation area. Put a compact task heading, actual sample data, the main result, and usable controls early enough to be useful on a narrow screen. Use readable text of at least 16px, generous paragraph spacing, strong contrast, native labels, accessible buttons, and visible keyboard focus. Stack long records at 360px and wrap code without page-level horizontal overflow.

Use type=button and explicit querySelector-based click handlers rather than relying on form submission. If Enter is useful, handle it only on the relevant input and ignore isComposing. Keep effects within this open page; no remote dependencies, network calls, persistence, fake downloads, or automated sending. This inspects JSON and displays embedded PNGs in HTML, not a native notebook editor. Schema validation and moving/reopening the notebook remain separate tests. Place worked explanations beside their data and identify teacher or synthetic examples clearly. Output only the complete HTML document, with inline styles and script and LF line endings.

Review the two-cell attachment reference inspector in NOS

Move only the completed notebook to an isolated folder and inspect both rendered Markdown cells in your native editor. Use the manifest explanation in NOS to review which dot.png belongs to each cell, and revise eligible static portability instructions in visual editing mode if a copy step leaves out the bundle.

Keep reading