Why SuperDoc Shows Only the Last Image: A DOCX Internals Investigation
Source: Dev.to
The Symptom
The generated report contained multiple property images arranged in a grid.
| Expected inside editor | Actual result inside SuperDoc |
|---|---|
| Image 1 → slot 1 | Slot 1 → last image |
| Image 2 → slot 2 | Slot 2 → last image |
| Image 3 → slot 3 | Slot 3 → last image |
Strangely:
- Microsoft Word → correct
- LibreOffice → correct
- Google Docs → correct
- SuperDoc editor → wrong
So the DOCX itself was valid; the issue appeared editor‑specific.
Environment
| Tool | Version |
|---|---|
| SuperDoc | 1.16.x |
docx-templates | 4.15.0 |
| Node | 20.x |
Images were inserted using docx-templates loops and loaded into the editor via docxUrl.
First Check: Is the DOCX Corrupt?
Initial assumption: the DOCX generation might be broken.
Verification steps
- Opened in Word – ✅
- Opened in LibreOffice – ✅
- Converted to PDF – ✅
- Uploaded to Google Docs – ✅
Everything rendered correctly everywhere except inside the editor.
Conclusion: basic DOCX corruption ruled out.
Deep‑Debugging Approach
Since DOCX files are just ZIP archives, I extracted and inspected the raw XML:
word/document.xml
word/_rels/document.xml.rels
word/media/
The goal was to see how images were referenced internally.
Finding 1: Non‑Standard Relationship IDs
Normally DOCX uses relationship IDs like rId1, rId2, rId3.
The generated DOCX contained hash‑based IDs such as:
Test performed – converted all relationship IDs to standard rId1, rId2, … and updated references.
Result: No change in editor behaviour; all image slots still showed the last image.
Conclusion: relationship IDs were not the main cause.
Finding 2: Media Filenames
Generated filenames looked like:
template_document.xml_img2073076884.jpg
instead of the usual image1.jpg, image2.jpg.
Test performed – renamed them to a standard format and updated references.
Result: Still broken in the editor.
Conclusion: filename convention was not the cause.
Finding 3: Images Inside Tables?
Suspected that images inside table cells were being flattened or merged.
Check: raw XML structure – images were already in standalone paragraphs, not nested inside complex table drawing structures.
Result: No structural issue found.
Finding 4: Base64 External References
Converted images to base64‑encoded external references to rule out any media‑file‑resolution issue inside the editor.
Result: Still broken – all slots continued showing the last image.
Conclusion: The problem had to be in how the editor was interpreting drawing‑node identity, not how images were stored or referenced.
The Strongest Lead So Far (Unverified)
While inspecting the drawing XML for each image, a pattern emerged:
Every image had id="0".
According to the OOXML spec, this attribute must be unique per drawing object within a document.
docx-templates appears to assign id="0" to all generated images.
Working theory: SuperDoc uses ProseMirror (or a similar engine) for rendering. If the parser relies on pic:cNvPr/@id to distinguish drawing nodes, duplicate IDs could cause it to treat all images as the same node, with the last image loaded “winning” every slot.
Note: This is a hypothesis, not a confirmed fix. The next step is to patch docx-templates output to inject unique sequential IDs, perform a clean server restart, and verify the result. A failed test earlier turned out to be against old compiled code because the server hadn’t been restarted after a rebuild.
What Has Been Tried So Far
| Attempt | Result |
|---|---|
| Extract images from table | No change |
Rename relationship IDs to rId1/rId2 | No change |
Rename media filenames to image1.jpg | No change |
| Convert images to base64 external refs | No change |
| Inspect drawing XML | Found duplicate id="0" across all images |
| Patch to unique IDs | Not fully verified yet |
Current Status
Issue is still under investigation.
Leading hypothesis: duplicate pic:cNvPr/@id="0" across all images causes the editor to collapse them into a single node.
Next steps
- Inject unique IDs into the drawing XML.
- Perform a full server restart.
- Run a clean verification (open the DOCX in SuperDoc, Word, LibreOffice, Google Docs).
Lessons From This Debugging Session
-
If it works in Word, it can still be wrong
Word tolerates many OOXML violations silently. Editors and converters are stricter. Never assume Word rendering equals correctness. -
Always inspect DOCX internally
Treat a DOCX as a ZIP file. Unzip it, inspect the XML, search for patterns, verify structure – debugging becomes much faster. -
Rendering engines behave differently
| Tool | Strictness |
|---|---|
| Word | Very forgiving |
| Google Docs | Moderate |
| LibreOffice | Strict |
| Editor engines | Very strict |
Editor‑specific bugs often stem from structural assumptions the spec makes but Word quietly ignores.
Why I’m Documenting This
The bug is still unresolved, but the debugging process uncovered a plausible root cause (duplicate pic:cNvPr/@id). Sharing the journey may help others facing similar editor‑specific rendering quirks, and it serves as a reminder to validate DOCX output beyond “it looks fine in Word”.
If you’ve encountered similar behaviour with DOCX image rendering, I’d genuinely like to hear what you found.