If you have ever tried to build a desktop app in Python, you know that placing buttons and labels exactly where you want them can be a bit of a headache.
In my years of developing Python GUI applications, I’ve found that most beginners struggle with layout management more than the actual logic of the code.
Tkinter offers three ways to manage layouts, but the pack() method is usually the first one we encounter. It is incredibly powerful once you understand how it “parses” the available space.
In this tutorial, I will show you exactly how to use the pack() manager based on my firsthand experience building tools for data analysis and retail systems.
Understand the Tkinter Pack Logic
Think of the pack() manager like a person packing a suitcase. It places widgets one after another, starting from the top by default.
It’s a “procedural” manager, meaning the order in which you call .pack() on your widgets matters immensely for the final look.
Method 1: Basic Vertical Packing (The Default Way)
When I first started using Tkinter, I simply called .pack() without any arguments. This stacks everything vertically.
In this example, let’s build a simple “Tax Filing Status” selector which is a common requirement for financial apps in the USA.
import tkinter as tk
def select_status():
print(f"Status selected: {status_var.get()}")
root = tk.Tk()
root.title("IRS Filing Status Selector")
root.geometry("400x300")
status_var = tk.StringVar(value="Single")
# Header Label
header = tk.Label(root, text="Select Your 2024 Filing Status", font=("Arial", 14, "bold"))
header.pack()
# Radio buttons packed vertically by default
tk.Radiobutton(root, text="Single", variable=status_var, value="Single").pack()
tk.Radiobutton(root, text="Married Filing Jointly", variable=status_var, value="MFJ").pack()
tk.Radiobutton(root, text="Head of Household", variable=status_var, value="HOH").pack()
submit_btn = tk.Button(root, text="Confirm Status", command=select_status, bg="blue", fg="white")
submit_btn.pack()
root.mainloop()I executed the code above and added the screenshot below.

When you run this, you’ll notice everything is centered and stacked. This is the simplest way to get elements onto the screen quickly.
Method 2: Use the Side Attribute for Horizontal Layouts
Standard vertical stacking isn’t always what you need. Often, you want buttons to sit side-by-side, like a “Submit” and “Cancel” button pair.
I frequently use the side attribute to control whether a widget hugs the TOP, BOTTOM, LEFT, or RIGHT of the available space.
Let’s create a “USD Currency Converter” interface layout.
import tkinter as tk
root = tk.Tk()
root.title("US Dollar Converter")
root.geometry("500x150")
# Input Frame
input_frame = tk.Frame(root)
input_frame.pack(pady=20)
label_amount = tk.Label(input_frame, text="Amount in USD: $")
label_amount.pack(side="left")
entry_amount = tk.Entry(input_frame)
entry_amount.pack(side="left")
# Action Buttons at the bottom
btn_frame = tk.Frame(root)
btn_frame.pack(side="bottom", fill="x", pady=10)
btn_convert = tk.Button(btn_frame, text="Convert to EUR", width=15)
btn_convert.pack(side="right", padx=10)
btn_clear = tk.Button(btn_frame, text="Clear Fields", width=15)
btn_clear.pack(side="right", padx=10)
root.mainloop()I executed the above example code and added the screenshot below.

By using side=”left” or side=”right”, you can break the vertical mold and create more professional, horizontal toolbars.
Method 3: Control Growth with Fill and Expand
One of the most common issues I see is a window being resized, but the widgets staying tiny in the middle.
To make an app look modern, you want certain elements, like a search bar or a text area, to grow when the user stretches the window.
In Tkinter, we use fill (to decide which axis to stretch) and expand (to tell the manager to claim extra space).
Here is a “California Real Estate Listing” viewer layout that handles resizing elegantly.
import tkinter as tk
root = tk.Tk()
root.title("CA Property Search")
root.geometry("600x400")
# Top Search Bar
search_frame = tk.Frame(root, bg="lightgrey")
search_frame.pack(side="top", fill="x")
search_label = tk.Label(search_frame, text="Zip Code:", bg="lightgrey")
search_label.pack(side="left", padx=5, pady=5)
search_entry = tk.Entry(search_frame)
search_entry.pack(side="left", fill="x", expand=True, padx=5)
# Main Content Area
listbox = tk.Listbox(root)
# We use fill="both" and expand=True so the listbox takes up all remaining room
listbox.pack(fill="both", expand=True, padx=10, pady=10)
# Footer
footer = tk.Label(root, text="Source: California MLS Data v2.0", fg="grey")
footer.pack(side="bottom", fill="x")
# Populating some dummy data
listbox.insert(1, "San Francisco - 2BR/2BA - $1,200,000")
listbox.insert(2, "Los Angeles - 3BR/2BA - $950,000")
listbox.insert(3, "San Diego - 1BR/1BA - $650,000")
root.mainloop()I executed the above example code and added the screenshot below.

Notice how the search_entry expands to fill the horizontal space, and the listbox fills the entire remainder of the window. This makes your app feel much more robust.
Method 4: Fine-Tuning with Padding (padx and pady)
I’ve learned that the difference between a “hobbyist” app and a “professional” app is often just white space.
If your buttons are touching the edges of the window or each other, it looks cluttered. I always use padx (horizontal padding) and pady (vertical padding).
Imagine we are building a “Tip Calculator” for a New York City restaurant.
import tkinter as tk
root = tk.Tk()
root.title("NYC Tip Calculator")
# internal padding (ipadx) makes the widget itself larger
# external padding (padx) creates space around the widget
title_label = tk.Label(root, text="Total Bill Amount", font=("Helvetica", 12))
title_label.pack(pady=(20, 5)) # 20px top, 5px bottom
bill_entry = tk.Entry(root, font=("Helvetica", 14), justify="center")
bill_entry.pack(padx=50, pady=10, ipady=10) # ipady makes the entry box taller
tip_label = tk.Label(root, text="Select Tip Percentage")
tip_label.pack(pady=5)
# Horizontal buttons using padding
btn_container = tk.Frame(root)
btn_container.pack(pady=10)
tk.Button(btn_container, text="15%").pack(side="left", padx=10)
tk.Button(btn_container, text="20%").pack(side="left", padx=10)
tk.Button(btn_container, text="25%").pack(side="left", padx=10)
root.mainloop()Key Takeaways for Using pack()
Over the years, I’ve developed a few “golden rules” for using the pack manager that will save you hours of debugging:
- Use Frames: Never try to pack 20 widgets into the root window. Group related widgets into a Frame, pack the widgets inside that frame, and then pack the frame itself.
- Order Matters: Pack your “anchor” elements first (like a top header or a bottom footer) before filling the middle.
- Don’t Mix Managers: A very important tip, never use .pack() and .grid() in the same parent container. Your app will likely freeze or crash.
- The “Anchor” Option: If you want a widget to stay in a specific corner (like North-West), use the anchor attribute (e.g., anchor=”nw”).
Troubleshoot Common Pack Issues
If you find that your widgets are disappearing or overlapping, it’s usually because of the “parcel” system.
When you pack a widget, Tkinter allocates a “parcel” of space along one side of the remaining area. If you pack a giant widget with fill=”both” first, it might take up the whole screen, leaving no room for others.
Always try to pack smaller, fixed-size elements (like buttons and labels) before packing large, expanding elements (like text boxes or canvases).
Using the pack() geometry manager is one of the fastest ways to build functional layouts in Python. While it might feel less precise than the grid() manager at first, its ability to handle window resizing automatically makes it a favorite for many developers.
I hope this tutorial helps you get a better handle on your Tkinter layouts. It takes a bit of practice to predict exactly where things will land, but once you master the side, fill, and expand options, you can build almost any interface.
You may also like to read:
- Set and Manage Window Size in Python Tkinter
- Use the Tkinter Treeview Widget in Python
- Take User Input and Store It in a Variable Using Python Tkinter
- Cancel Scheduled Functions with after_cancel() in Python Tkinter

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.