This article shows how to fix the common Matplotlib issue where tight_layout() doesn't account for suptitle — causing the main figure title to overlap with subplot content. This is one of the most frequently searched Matplotlib layout problems on Stack Overflow.
1. Why Does tight_layout() Ignore suptitle?
plt.suptitle() adds a centered title above all subplots, but tight_layout() doesn't include it in its spacing calculations by default. The result: the suptitle overlaps the top row of subplots.
This matters when you're building:
- Multi-chart dashboards with a shared heading
- Reports where each figure has a descriptive title
- Presentations or exports where layout precision is required
2. Steps to Fix tight_layout() with suptitle
- Add your subplots with
plt.subplots() - Set a figure title using
plt.suptitle() - Use
plt.tight_layout()with therectparameter to reserve space at the top - Alternatively, use
plt.subplots_adjust(top=...)aftertight_layout() - Display or save the figure
3. Example Data
We'll use annual profit data for four major US banks — JPMorgan, Bank of America, Wells Fargo, and Citibank — across four years.
years = ["2020", "2021", "2022", "2023"]
data = {
"JPMorgan": [29, 48, 37, 49],
"Bank of America": [17, 31, 27, 35],
"Wells Fargo": [3, 21, 13, 19],
"Citibank": [11, 22, 14, 9],
}
4. Example: Fix Suptitle Overlap Using tight_layout(rect=...)
The cleanest fix is passing a rect argument to tight_layout(). The rect parameter defines the bounding box [left, bottom, right, top] that the layout fits into — setting top=0.93 reserves space for the suptitle above it.
import matplotlib.pyplot as plt
years = ["2020", "2021", "2022", "2023"]
data = {"JPMorgan": [29,48,37,49], "Bank of America": [17,31,27,35],
"Wells Fargo": [3,21,13,19], "Citibank": [11,22,14,9]}
fig, axes = plt.subplots(2, 2, figsize=(9, 6))
for ax, (bank, values) in zip(axes.flatten(), data.items()):
ax.bar(years, values, color="steelblue")
ax.set_title(bank)
plt.suptitle("US Bank Annual Profits (Billions)", fontsize=14, fontweight="bold")
plt.tight_layout(rect=[0, 0, 1, 0.93])
plt.show()
Output: Result:
A 2×2 grid of bar charts — one per bank — with a bold figure title "US Bank Annual Profits (Billions)" sitting cleanly above all subplots, no overlap, no clipping.
5. Explanation of the Code
plt.suptitle()— adds a single title centered above all subplots in the figuretight_layout(rect=[0, 0, 1, 0.93])— restricts the auto-layout area to the bottom 93% of the figure, leaving the top 7% free for the suptitle- The
recttuple is[left, bottom, right, top]in figure coordinates (0 to 1) - Adjust the
topvalue between0.90and0.95depending on font size and figure height
6. Bonus Example: Fix Using subplots_adjust(top=...)
An alternative approach uses plt.subplots_adjust(top=...) after calling tight_layout(). This is useful when you want tight_layout() to run first and then manually push the top margin down.
import matplotlib.pyplot as plt
quarters = ["Q1", "Q2", "Q3", "Q4"]
data = {"Nike": [11,13,12,15], "Adidas": [5,6,7,8],
"Puma": [2,3,3,4], "Under Armour": [1,2,2,3]}
fig, axes = plt.subplots(2, 2, figsize=(9, 6))
for ax, (brand, values) in zip(axes.flatten(), data.items()):
ax.plot(quarters, values, marker="o")
ax.set_title(brand)
plt.suptitle("Sportswear Brand Quarterly Revenue", fontsize=13, fontweight="bold")
plt.tight_layout()
plt.subplots_adjust(top=0.90)
plt.show()
Output: Result:
A 2×2 line chart grid for Nike, Adidas, Puma, and Under Armour — the suptitle appears cleanly above all subplots with no overlap, using subplots_adjust to push down the subplot area after tight_layout() runs.
7. Customization
| Goal | Code |
|---|---|
| Reserve more space for suptitle | tight_layout(rect=[0, 0, 1, 0.90]) |
| Reserve less space | tight_layout(rect=[0, 0, 1, 0.96]) |
| Manual top margin adjustment | plt.subplots_adjust(top=0.88) |
| Styled suptitle | plt.suptitle("Title", fontsize=16, fontweight="bold", color="navy") |
| Add vertical position control | plt.suptitle("Title", y=0.98) |
| Matplotlib 3.6+ constrained layout | plt.subplots(constrained_layout=True) then plt.suptitle(...) |
Pro tip: In Matplotlib 3.6+, you can replace tight_layout() entirely with constrained_layout=True in plt.subplots(). This layout engine natively accounts for suptitle — no rect workaround needed.