How to Create Tabs in Python with Tkinter Notebook

If you’ve tried to fit a customer form, an order list and a payment screen into one window, you’ve probably ended up with frames stacked on top of each other. A Tkinter notebook fixes that: ttk.Notebook gives each frame its own clickable tab.

Here’s the finished order desk I built for this post, running on Windows 11:

Finished Tkinter notebook order desk with blue styled Customer, Order and Payment tabs, icons and a status line reading Viewing Order
The finished order desk with the Order tab open.

In this tutorial, I’ll build that Tkinter notebook one feature at a time on Python 3.12.5 with Tk 8.6, from adding tabs to styling them.

The sample app we’ll use: a coffee shop order desk

Every example uses the same small app for Bluebird Coffee, with three tabs:

  • Customer: name (in name_var) and phone.
  • Order: 2 Cold Brews at $4.75 and a $3.25 muffin.
  • Payment: $12.75 due; we’ll hide and disable it later.

I lay out the Customer fields with grid(); if that’s new, see using a Tkinter Frame with grid first.

Method 1: Create tabs with ttk.Notebook and add() (Recommended)

Use this for any window that has a few separate screens. Each tab is a ttk.Frame whose parent is the notebook. Here’s the full program:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.title("Bluebird Coffee - Order desk")
root.geometry("640x320")

notebook = ttk.Notebook(root)
notebook.pack(fill="both", expand=True, padx=10, pady=10)

customer = ttk.Frame(notebook, padding=16)
order = ttk.Frame(notebook, padding=16)
payment = ttk.Frame(notebook, padding=16)

notebook.add(customer, text="Customer")
notebook.add(order, text="Order")
notebook.add(payment, text="Payment")

name_var = tk.StringVar()
ttk.Label(customer, text="Name").grid(row=0, column=0, sticky="w")
ttk.Entry(customer, textvariable=name_var, width=30).grid(
    row=0, column=1, padx=8, pady=4)
ttk.Label(customer, text="Phone").grid(row=1, column=0, sticky="w")
ttk.Entry(customer, width=30).grid(row=1, column=1, padx=8, pady=4)

ttk.Label(order, text="2 x Cold Brew @ $4.75").pack(anchor="w")
ttk.Label(order, text="1 x Blueberry Muffin @ $3.25").pack(anchor="w")
ttk.Label(payment, text="Total due: $12.75").pack(anchor="w")

root.mainloop()

You get three tabs, and notebook.index("end") returns 3.

Tkinter notebook window with Customer, Order and Payment tabs boxed in red and the Customer tab showing name and phone entries
Three tabs from three frames; Customer opens first.

How does this code work?

ttk.Notebook(root) creates the tab bar and page area. Each add(frame, text=...) appends a tab, and the first one is selected automatically. A frame is just a container, as in my Python Tkinter Frame tutorial.

Important: give each tab’s widgets the frame as parent, not root, or they’ll land outside the notebook.

Method 2: Insert, hide and forget Tkinter notebook tabs

Same order desk, now with a Gift Card tab and a Payment tab that stays hidden until the order is ready. Add this before root.mainloop():

gift = ttk.Frame(notebook, padding=16)
ttk.Label(gift, text="Gift card balance: $25.00").pack(anchor="w")
notebook.insert(payment, gift, text="Gift Card")  # before Payment
notebook.hide(payment)                            # until order is ready

def remove_gift():
    notebook.forget(gift)          # gone for good

def ready_to_pay():
    notebook.add(payment)          # a hidden tab comes back in place
    notebook.select(payment)

ttk.Button(order, text="Remove gift card",
           command=remove_gift).pack(anchor="w", pady=(12, 4))
ttk.Button(order, text="Ready to pay",
           command=ready_to_pay).pack(anchor="w")
print("Tabs:", [notebook.tab(t, "text") for t in notebook.tabs()])
print("Payment state:", notebook.tab(payment, "state"))

On start it prints Tabs: ['Customer', 'Order', 'Gift Card', 'Payment'] and Payment state: hidden.

Before and after: a Tkinter notebook with Customer, Order and Gift Card tabs, then Customer, Order and Payment after removing the gift card and pressing Ready to pay
Left: Payment hidden. Right: after Remove gift card and Ready to pay.

How does this code work?

insert(payment, gift, ...) puts the new tab in front of Payment; a number like 1 works too.

hide() only hides the tab: it stays in tabs() with the state hidden, and add(payment) restores it in the same spot with its text. forget() removes the tab completely, though the frame itself still exists.

If you’d rather swap whole screens without a tab bar, my guide to going to the next page in Python Tkinter uses stacked frames instead.

How do I get the current tab in a Tkinter notebook?

Call notebook.select() with no argument for the open tab’s widget name, index("current") for its position and tab(tab_id, "text") for its label.

To react when the user switches, bind the <<NotebookTabChanged>> virtual event. Here the order desk gets a status line:

status = ttk.Label(root, anchor="w", padding=(12, 0, 0, 8))
status.pack(fill="x")

def on_tab_changed(event):
    nb = event.widget
    tab_id = nb.select()                 # widget name of the open tab
    index = nb.index(tab_id)             # same as nb.index("current")
    text = nb.tab(tab_id, "text")
    status.config(text=f"Viewing: {text} (tab {index + 1} "
                       f"of {nb.index('end')})")

notebook.bind("<<NotebookTabChanged>>", on_tab_changed)

After opening the Order tab, the status line reads Viewing: Order (tab 2 of 3).

Tkinter notebook with the Order tab selected and a status line below reading Viewing Order tab 2 of 3, both boxed in red
The event updates the status line each time a tab opens.

To see when it fires, I switched tabs from code in a test script, run in VS Code:

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
notebook = ttk.Notebook(root)
notebook.pack()
for name in ("Customer", "Order", "Payment"):
    notebook.add(ttk.Frame(notebook, width=320, height=120), text=name)

def on_tab_changed(event):
    nb = event.widget
    print(f"Changed -> {nb.index('current')}: {nb.tab('current', 'text')}")

notebook.bind("<<NotebookTabChanged>>", on_tab_changed)
print("Tab count:", notebook.index("end"))

root.after(400, lambda: notebook.select(1))
root.after(800, lambda: notebook.select(2))
root.after(1200, root.destroy)
root.mainloop()

Output:

Tab count: 3
Changed -> 0: Customer
Changed -> 1: Order
Changed -> 2: Payment
VS Code output of a Tkinter NotebookTabChanged test printing the tab count and each tab change
The event fires for the first tab too, then once per switch.

How does this code work?

select(1) opens a tab by position and fires the same event as a click. Note the first change line: the event also runs when the window first shows tab 0.

So your handler shouldn’t assume a user clicked. More event patterns are in Python Tkinter events.

Method 3: Disable a notebook tab until a form is filled in

A disabled tab stays visible but grayed out. On the order desk, Payment stays locked until the customer has a name:

notebook.tab(payment, state="disabled")   # grayed out, can't be opened

def check_customer(*args):
    ready = bool(name_var.get().strip())
    notebook.tab(payment, state="normal" if ready else "disabled")

name_var.trace_add("write", check_customer)

Before the name is typed, the tab state is disabled and select(payment) leaves the Customer tab open; after it, the state is normal.

Before and after: the Payment tab of a Tkinter notebook grayed out while the name is empty, then enabled after Emily Carter is entered
Left: Payment disabled. Right: enabled once the name is filled in.

How does this code work?

notebook.tab(tab, state=...) changes one tab’s options; the states are normal, disabled and hidden. trace_add("write", ...) calls check_customer after every keystroke in the entry, like the validation in my Python Tkinter Entry guide.

Method 4: Style Tkinter notebook tabs with ttk.Style

Notebook tabs are ttk widgets, so you style them with ttk.Style, not bg=. Same app, new look:

style = ttk.Style()
style.theme_use("clam")
style.configure("TNotebook", background="#e8eef5")
style.configure("TNotebook.Tab", padding=(18, 6),
                font=("Segoe UI", 10, "bold"))
style.map("TNotebook.Tab",
          background=[("selected", "#1d4ed8"), ("active", "#bfdbfe")],
          foreground=[("selected", "white")])
Tkinter notebook using the clam theme with a blue selected Customer tab in bold white text, boxed in red
The selected tab turns blue.

How does this code work?

TNotebook styles the whole widget and TNotebook.Tab styles each tab. style.map() sets values per state, so selected and active (hover) get their own colors.

I use clam because the default Windows theme, vista, ignores tab colors.

Pro Tip: a named style like Side.TNotebook changes one notebook only. Here it moves the tabs to the left:

style = ttk.Style()
style.configure("Side.TNotebook", tabposition="wn")  # west, top
notebook.configure(style="Side.TNotebook")