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 the rect parameter to reserve space at the top
  • Alternatively, use plt.subplots_adjust(top=...) after tight_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 figure
  • tight_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 rect tuple is [left, bottom, right, top] in figure coordinates (0 to 1)
  • Adjust the top value between 0.90 and 0.95 depending 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.

8. Resources