Last modified: Jul 08, 2026
Python-docx Bookmarks & Cross-References Guide
Bookmarks and cross-references make Word documents dynamic. They help readers jump to specific sections. With python-docx, you can automate this process. This guide shows you how.
Bookmarks are invisible markers in a document. Cross-references link to those markers. Together, they improve navigation. You can use them in reports, manuals, or ebooks.
Why Use Bookmarks and Cross-References?
Long documents need structure. Bookmarks let you mark key points. Cross-references let you refer to those points. For example, "See Section 2.1 for details."
This saves time for readers. It also keeps your document organized. Automated scripts can create these links in bulk.
For more advanced document structure, see our Python docx Numbered Headings Guide.
Adding a Bookmark with Python-docx
You can add a bookmark to any run or paragraph. Use the add_bookmark method on a run. The bookmark needs a unique name.
Here is a simple example:
from docx import Document
doc = Document()
p = doc.add_paragraph("This is a ")
run = p.add_run("bookmarked text")
run.add_bookmark("MyBookmark1")
doc.save("bookmark_example.docx")
This creates a bookmark named "MyBookmark1". The bookmark wraps the text "bookmarked text". You can now reference it in Word.
Important: Bookmark names must be unique. Use descriptive names like "Section2" or "Figure3". Avoid spaces and special characters.
Finding Existing Bookmarks
You may need to find bookmarks in an existing document. Python-docx does not have a direct method for this. But you can inspect the XML.
Here is how to find all bookmark names:
from docx import Document
from docx.oxml.ns import qn
doc = Document("bookmark_example.docx")
bookmarks = []
for p in doc.paragraphs:
for run in p.runs:
for bookmark in run._element.findall(qn('w:bookmarkStart')):
bookmarks.append(bookmark.get(qn('w:name')))
print("Found bookmarks:", bookmarks)
Output:
Found bookmarks: ['MyBookmark1']
This code searches all runs. It finds bookmarkStart elements. Then it extracts the name attribute.
Creating Cross-References
Cross-references in Word are complex. Python-docx does not fully support them. But you can insert a field code that Word recognizes.
A cross-reference field looks like this: { REF BookmarkName \h }. The \h makes it a hyperlink.
Here is an example:
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
def add_cross_reference(paragraph, bookmark_name):
run = paragraph.add_run()
fldChar1 = OxmlElement('w:fldChar')
fldChar1.set(qn('w:fldCharType'), 'begin')
run._element.append(fldChar1)
instrText = OxmlElement('w:instrText')
instrText.set(qn('xml:space'), 'preserve')
instrText.text = f' REF {bookmark_name} \\h '
run._element.append(instrText)
fldChar2 = OxmlElement('w:fldChar')
fldChar2.set(qn('w:fldCharType'), 'end')
run._element.append(fldChar2)
doc = Document()
p = doc.add_paragraph("Go to ")
add_cross_reference(p, "MyBookmark1")
p.add_run(" for more info.")
# Also add the bookmark target
p2 = doc.add_paragraph()
run2 = p2.add_run("This is the target section")
run2.add_bookmark("MyBookmark1")
doc.save("cross_reference_example.docx")
When you open the document in Word, the cross-reference appears as a clickable link. It jumps to the bookmark.
Note: The field code may not render in all viewers. Word desktop and Word Online support it. LibreOffice may need an update.
Practical Use Case: Table of Contents
You can build a simple table of contents with bookmarks. Add a bookmark for each heading. Then add cross-references in a summary section.
This is useful for automated report generation. For example, a monthly sales report with links to each product section.
For cleaner document generation, check our Python docx Best Practices for Clean Generation.
Handling Multiple Bookmarks
When you have many bookmarks, loop through them. Store names in a list. Then create cross-references dynamically.
Here is a script that adds bookmarks to all headings:
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
doc = Document()
headings = ["Introduction", "Methods", "Results", "Conclusion"]
for i, heading in enumerate(headings):
p = doc.add_paragraph()
run = p.add_run(heading)
run.bold = True
run.add_bookmark(f"Section{i+1}")
doc.save("headings_with_bookmarks.docx")
Now you can reference each section by its bookmark name like "Section1", "Section2", etc.
Best Practices
Follow these tips for reliable bookmarks:
- Use unique, descriptive names without spaces.
- Add bookmarks to runs, not paragraphs directly.
- Test cross-references in Word after generation.
- Keep bookmark names short but meaningful.
For more on document automation, see our Python docx vs win32com: Automate Word Docs guide.
Limitations of Python-docx
Python-docx has limits. It cannot update cross-references automatically. Word does that when you open the file. Also, complex field codes may break.
If you need full cross-reference support, consider using win32com on Windows. It controls Word directly.
But for simple bookmarks, python-docx works well. It is cross-platform and easy to use.
Conclusion
Bookmarks and cross-references add value to Word documents. Python-docx lets you create bookmarks easily. Cross-references require a bit of XML work, but it is doable.
Start with simple bookmarks. Then expand to cross-references for navigation. This makes your automated documents more professional.
Practice with the examples above. Modify them for your own projects. You will soon master document automation with python-docx.