Last modified: Jul 08, 2026

Python docx Multi-Column Layouts Guide

Multi-column layouts are essential for newsletters, brochures, and reports. With python-docx, you can create them programmatically. This guide shows you how.

Many developers struggle with column formatting. The python-docx library doesn't have a direct method. But you can use XML manipulation to achieve it.

We'll cover section-based columns, custom widths, and spacing. You'll learn to build professional documents with ease.

Understanding Sections and Columns

In Word, columns belong to sections. A document can have multiple sections. Each section can have its own column layout.

To add columns, you modify the section's XML. The python-docx library provides access to the underlying XML. This is your key to multi-column layouts.

First, install the library:


pip install python-docx

Creating a Basic Two-Column Layout

Let's start with a simple two-column layout. We'll create a document with a single section.


from docx import Document
from docx.shared import Inches, Pt, Emu
from docx.enum.text import WD_ALIGN_PARAGRAPH
import lxml.etree as etree

doc = Document()

# Add a section (default section exists)
section = doc.sections[0]

# Access the section's XML element
sect_pr = section._sectPr

# Create column element
cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', '2')

# Add content
doc.add_paragraph('First column content').alignment = WD_ALIGN_PARAGRAPH.LEFT
doc.add_paragraph('Second column content').alignment = WD_ALIGN_PARAGRAPH.LEFT

doc.save('two_columns.docx')
print("Document saved successfully.")

Document saved successfully.

This creates a simple two-column layout. The content flows from left to right. Remember that columns affect the entire section.

Customizing Column Widths and Spacing

You can control column widths and spacing. Use the w and space attributes. Values are in EMUs (English Metric Units).


from docx import Document
from docx.shared import Inches
import lxml.etree as etree

doc = Document()
section = doc.sections[0]
sect_pr = section._sectPr

# Create columns with custom widths and spacing
cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', '3')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}space', '360')  # 0.25 inch spacing

# Add individual column definitions for custom widths
col1 = etree.SubElement(cols, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}col')
col1.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w', '2500')  # 2.5 inches

col2 = etree.SubElement(cols, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}col')
col2.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w', '3000')  # 3 inches

col3 = etree.SubElement(cols, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}col')
col3.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}w', '2000')  # 2 inches

doc.add_paragraph('Column 1 - Narrow')
doc.add_paragraph('Column 2 - Wide')
doc.add_paragraph('Column 3 - Medium')

doc.save('custom_columns.docx')
print("Custom columns created.")

Custom columns created.

You can mix different widths. This creates a balanced layout. The total width should match the page width minus margins.

Adding Columns to Specific Sections

You can add multiple sections with different column layouts. This is useful for title pages or appendices.


from docx import Document
from docx.shared import Inches
import lxml.etree as etree

doc = Document()

# First section - no columns (title page)
section1 = doc.sections[0]
doc.add_paragraph('Title Page').add_run().bold = True
doc.add_paragraph('This is a single column section.')

# Add a new section for multi-column layout
new_section = doc.add_section()
sect_pr = new_section._sectPr

# Add two columns to the new section
cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', '2')

doc.add_paragraph('This content appears in two columns.')
doc.add_paragraph('Second column content here.')

doc.save('multi_section_columns.docx')
print("Multi-section document created.")

Multi-section document created.

This approach gives you flexibility. You can mix single and multi-column layouts in one document.

Handling Column Breaks

To force content to the next column, use column breaks. Add them as XML elements in the paragraph properties.


from docx import Document
from docx.oxml.ns import qn
import lxml.etree as etree

doc = Document()
section = doc.sections[0]
sect_pr = section._sectPr

# Add two columns
cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', '2')

# Add content with column break
doc.add_paragraph('This is in the first column.')

# Insert a column break
para = doc.add_paragraph()
run = para.add_run()
br = etree.SubElement(run._r, qn('w:br'))
br.set(qn('w:type'), 'column')

doc.add_paragraph('This is forced to the second column.')
doc.add_paragraph('More content in second column.')

doc.save('column_break.docx')
print("Column break added.")

Column break added.

Column breaks are powerful for precise layout control. Use them for tables, images, or headings.

Working with Line Between Columns

Add a vertical line between columns for readability. Use the sep attribute on the columns element.


from docx import Document
import lxml.etree as etree

doc = Document()
section = doc.sections[0]
sect_pr = section._sectPr

# Add two columns with a separator line
cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', '2')
cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}sep', '1')  # Enable separator

doc.add_paragraph('Left column content')
doc.add_paragraph('Right column content')

doc.save('columns_with_line.docx')
print("Columns with separator line created.")

Columns with separator line created.

The separator line appears between columns. It makes the layout look more professional.

Best Practices for Multi-Column Layouts

Follow these tips for clean documents. First, always test your layout in Word. The XML approach can have subtle issues.

Second, use consistent margins. Columns work best with equal spacing. Third, avoid mixing column counts in the same section.

For complex layouts, consider using Python docx Best Practices for Clean Generation. It covers formatting standards.

Also, check Python DOCX Templates with Jinja2 for dynamic content generation. It pairs well with multi-column layouts.

Troubleshooting Common Issues

If columns don't appear, check the XML namespace. Use the correct OpenXML namespace. The python-docx library uses http://schemas.openxmlformats.org/wordprocessingml/2006/main.

Another issue is content overflow. Ensure your content fits within column widths. Use shorter text or adjust column sizes.

If the document crashes, verify the XML structure. Use etree.tostring() to debug the section XML.


# Debugging XML
print(etree.tostring(sect_pr, pretty_print=True).decode())

This shows the raw XML. You can spot missing attributes or incorrect namespaces.

Advanced: Dynamic Column Counts

You can create a function to add columns dynamically. This is useful for generating reports with variable layouts.


from docx import Document
import lxml.etree as etree

def add_columns(section, num_cols, space=360):
    """Add columns to a section."""
    sect_pr = section._sectPr
    cols = etree.SubElement(sect_pr, '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}cols')
    cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}num', str(num_cols))
    cols.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}space', str(space))

# Usage
doc = Document()
section = doc.sections[0]
add_columns(section, 3)

doc.add_paragraph('Three-column layout')
doc.add_paragraph('Content for column 1')
doc.add_paragraph('Content for column 2')
doc.add_paragraph('Content for column 3')

doc.save('dynamic_columns.docx')
print("Dynamic columns created.")

Dynamic columns created.

This function makes your code reusable. You can call it for any section.

Conclusion

Multi-column layouts in python-docx are achievable with XML manipulation. You now have the tools to create professional documents.

Start with simple two-column layouts. Then experiment with custom widths and breaks. For advanced automation, see Future of Python DOCX Automation Trends.

Remember to test your output in Word. The XML approach gives you full control. Happy coding!