Recently, I was working on a PyQt6 application where I needed to create a responsive UI that would adapt to different window sizes. The challenge was to keep elements properly aligned and spaced regardless of how the user resized the window. That’s when I discovered the power of QSpacerItem in PyQt6.
In this article, I will share how QSpacerItem can solve your layout problems and create truly dynamic interfaces. I’ll cover different approaches to implement spacers effectively and show you real-world examples you can use in your projects.
Let us start..!
QSpacerItem in PyQt6
QSpacerItem is a layout item that creates space within layouts. Think of it as an invisible, stretchable component that pushes widgets apart or aligns them properly within your application window.
Unlike visible widgets, spacers don’t display anything on screen, they simply consume space to help position other elements.
1. Create Horizontal Spacers
Let’s start with a basic example of using horizontal spacers to center a widget in a layout:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QPushButton, QSpacerItem, QSizePolicy)
class HorizontalSpacerDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Horizontal Spacer Demo")
self.resize(600, 100)
# Create horizontal layout
layout = QHBoxLayout()
self.setLayout(layout)
# Add a spacer on the left
layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
# Add a button in the middle
layout.addWidget(QPushButton("Centered Button"))
# Add a spacer on the right
layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = HorizontalSpacerDemo()
window.show()
sys.exit(app.exec())I executed the above example code and added the screenshot below.

In this example, I’ve added spacers on both sides of a button. The spacers have an expanding horizontal policy, which means they’ll grow equally as the window expands, keeping the button centered.
The parameters for QSpacerItem are:
- Width hint (40 pixels)
- Height hint (20 pixels)
- Horizontal size policy (Expanding)
- Vertical size policy (Minimum)
Read QTreeView Widget in PyQt6
2. Create Vertical Spacers
Vertical spacers work similarly but control spacing in the vertical direction:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout,
QPushButton, QSpacerItem, QSizePolicy)
class VerticalSpacerDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Vertical Spacer Demo")
self.resize(300, 400)
# Create vertical layout
layout = QVBoxLayout()
self.setLayout(layout)
# Add a spacer at the top
layout.addSpacerItem(QSpacerItem(20, 40,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding))
# Add a button in the middle
layout.addWidget(QPushButton("Vertically Centered"))
# Add a spacer at the bottom
layout.addSpacerItem(QSpacerItem(20, 40,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = VerticalSpacerDemo()
window.show()
sys.exit(app.exec())I executed the above example code and added the screenshot below.

Notice that with vertical spacers, I’ve swapped the size policies: the vertical policy is now Expanding while the horizontal is Minimum.
3. Fixed vs. Expanding Spacers
Sometimes you need fixed spacing rather than expanding space. Here’s how to create both types:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QPushButton, QSpacerItem, QSizePolicy, QLabel)
class SpacerTypesDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Fixed vs Expanding Spacers")
self.resize(600, 100)
# Create horizontal layout
layout = QHBoxLayout()
self.setLayout(layout)
# Add first button
layout.addWidget(QPushButton("Left Button"))
# Add a fixed spacer (40px wide, won't expand)
layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Fixed,
QSizePolicy.Policy.Minimum))
# Add a label
layout.addWidget(QLabel("Fixed 40px gap →"))
# Add an expanding spacer (grows with window)
layout.addSpacerItem(QSpacerItem(10, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
# Add a label
layout.addWidget(QLabel("← Expanding gap"))
# Add last button
layout.addWidget(QPushButton("Right Button"))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SpacerTypesDemo()
window.show()
sys.exit(app.exec())I executed the above example code and added the screenshot below.

The key difference is in the size policy:
QSizePolicy.Policy.Fixedcreates a spacer with a fixed sizeQSizePolicy.Policy.Expandingcreates a spacer that grows/shrinks with the window
4. Create Responsive Forms with Spacers
One common UI pattern is a form that needs to stay properly aligned. Let’s create a login form using spacers:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QSpacerItem,
QSizePolicy)
class LoginFormDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Login Form with Spacers")
self.resize(400, 200)
# Main layout
main_layout = QVBoxLayout()
self.setLayout(main_layout)
# Add spacer at top
main_layout.addSpacerItem(QSpacerItem(20, 20,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding))
# Username row
username_layout = QHBoxLayout()
username_layout.addWidget(QLabel("Username:"))
username_layout.addWidget(QLineEdit())
main_layout.addLayout(username_layout)
# Password row
password_layout = QHBoxLayout()
password_layout.addWidget(QLabel("Password:"))
password_layout.addWidget(QLineEdit())
main_layout.addLayout(password_layout)
# Button row with spacers for centering
button_layout = QHBoxLayout()
button_layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
button_layout.addWidget(QPushButton("Login"))
button_layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
main_layout.addLayout(button_layout)
# Add spacer at bottom
main_layout.addSpacerItem(QSpacerItem(20, 20,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LoginFormDemo()
window.show()
sys.exit(app.exec())This form demonstrates vertical spacers at the top and bottom to center the form vertically, plus horizontal spacers to center the button.
Check out Create Alert Dialogs with QMessageBox in PyQt6
5. Use addStretch() as a Shortcut
While QSpacerItem gives you precise control, PyQt6 layouts also offer a convenient shortcut called addStretch() method in Python:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QHBoxLayout,
QPushButton)
class StretchDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Using addStretch()")
self.resize(500, 100)
# Create horizontal layout
layout = QHBoxLayout()
self.setLayout(layout)
# Add a button on the left
layout.addWidget(QPushButton("Left"))
# Add a stretch (equivalent to an expanding spacer)
layout.addStretch(1) # The parameter is the stretch factor
# Add a button on the right
layout.addWidget(QPushButton("Right"))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StretchDemo()
window.show()
sys.exit(app.exec())The addStretch(1) method is equivalent to adding an expanding spacer. The parameter (1) represents the stretch factor, which determines how much space this stretch will take relative to other stretches or expandable widgets.
Create a Real-World Dashboard Layout
Let’s build a more complex example – a dashboard with multiple sections that uses spacers to create a balanced layout:
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QFrame, QSpacerItem,
QSizePolicy)
from PyQt6.QtCore import Qt
class DashboardDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Dashboard with Spacers")
self.resize(800, 600)
# Main layout
main_layout = QVBoxLayout()
self.setLayout(main_layout)
# Header section
header = QFrame()
header.setFrameShape(QFrame.Shape.StyledPanel)
header.setMinimumHeight(80)
header_layout = QHBoxLayout(header)
header_layout.addWidget(QLabel("US Sales Dashboard"))
header_layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
header_layout.addWidget(QPushButton("Refresh"))
header_layout.addWidget(QPushButton("Sign Out"))
main_layout.addWidget(header)
# Content section
content = QHBoxLayout()
# Left sidebar
sidebar = QFrame()
sidebar.setFrameShape(QFrame.Shape.StyledPanel)
sidebar.setMinimumWidth(150)
sidebar_layout = QVBoxLayout(sidebar)
sidebar_layout.addWidget(QPushButton("Dashboard"))
sidebar_layout.addWidget(QPushButton("Sales Reports"))
sidebar_layout.addWidget(QPushButton("Customer Data"))
sidebar_layout.addWidget(QPushButton("Analytics"))
sidebar_layout.addSpacerItem(QSpacerItem(20, 40,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding))
content.addWidget(sidebar)
# Main content area
main_content = QFrame()
main_content.setFrameShape(QFrame.Shape.StyledPanel)
main_content_layout = QVBoxLayout(main_content)
# Top stats row
stats_row = QHBoxLayout()
# Create 3 stat boxes with equal spacing
for region in ["West Region", "Central Region", "East Region"]:
stat_box = QFrame()
stat_box.setFrameShape(QFrame.Shape.StyledPanel)
stat_box.setMinimumHeight(100)
stat_layout = QVBoxLayout(stat_box)
stat_layout.addWidget(QLabel(f"{region}"))
stat_layout.addWidget(QLabel("$1,234,567"))
stat_layout.addWidget(QLabel("+12.3% YoY"))
stats_row.addWidget(stat_box)
# Add spacer between boxes (but not after the last one)
if region != "East Region":
stats_row.addSpacerItem(QSpacerItem(20, 20,
QSizePolicy.Policy.Fixed,
QSizePolicy.Policy.Minimum))
main_content_layout.addLayout(stats_row)
# Add spacer between sections
main_content_layout.addSpacerItem(QSpacerItem(20, 20,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Fixed))
# Chart section
chart_section = QFrame()
chart_section.setFrameShape(QFrame.Shape.StyledPanel)
chart_section.setMinimumHeight(300)
chart_layout = QVBoxLayout(chart_section)
chart_layout.addWidget(QLabel("Sales Performance Chart"))
chart_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_content_layout.addWidget(chart_section)
content.addWidget(main_content)
# Add content to main layout
main_layout.addLayout(content)
# Footer
footer = QFrame()
footer.setFrameShape(QFrame.Shape.StyledPanel)
footer.setMinimumHeight(30)
footer_layout = QHBoxLayout(footer)
footer_layout.addWidget(QLabel("© 2025 US Sales Dashboard"))
footer_layout.addSpacerItem(QSpacerItem(40, 20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum))
footer_layout.addWidget(QLabel("Version 1.0.2"))
main_layout.addWidget(footer)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = DashboardDemo()
window.show()
sys.exit(app.exec())This comprehensive dashboard example demonstrates how spacers can create balanced layouts in a realistic application. Notice how I’ve used:
- Horizontal spacers in the header to push buttons to the right
- Vertical spacers in the sidebar to push content to the top
- Fixed spacers between stat boxes to create equal spacing
- Expanding spacers in the footer to separate copyright from version info
In this article, I have explained how to create a dynamic layout with QSpacerItem in PyQt6. I discussed five methods to accomplish this task they are: creating horizontal spacers, creating vertical spacers, fixed vs. expanding spacers, creating responsive forms with spacers, and using addStrech() as a shortcut.
PyQt6-related tutorials.
- How to Use QInputDialog in PyQt6
- Work with QColorDialog in PyQt6
- QFontDialog in PyQt6: Create and Customize Font

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.