From Tkinter to CustomTkinter: a migration guide
Posted 2026-04-21 on the pythondeck.com blog
If you have a working Tkinter app that looks stuck in 1998, CustomTkinter is the lowest-effort path to a modern look. The API is deliberately close to Tk's, but with rounded corners, theming, dark mode and HiDPI awareness.
What stays the same
Geometry managers (pack, grid, place) are unchanged. Event binding with .bind() is unchanged. The after() scheduler is unchanged. Anything outside the widget layer (file dialogs, message boxes, threading) stays exactly as you wrote it.
What changes
You import customtkinter as ctk instead of tkinter as tk for widgets. Class names get a CTk prefix: CTk, CTkButton, CTkEntry, CTkFrame, CTkLabel. Some option names differ slightly (e.g. fg_color instead of bg). Setting the theme is two lines at the top of your file:
import customtkinter as ctk
ctk.set_appearance_mode("system") # 'light', 'dark', 'system'
ctk.set_default_color_theme("blue")
A minimal port
# before
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="Hello").pack(padx=20, pady=20)
tk.Button(root, text="OK").pack(pady=8)
root.mainloop()
# after
import customtkinter as ctk
ctk.set_appearance_mode("system")
root = ctk.CTk()
ctk.CTkLabel(root, text="Hello").pack(padx=20, pady=20)
ctk.CTkButton(root, text="OK").pack(pady=8)
root.mainloop()
What to do about non-CTk widgets
Not every Tk widget has a CTk equivalent (notably Text, Canvas, Treeview). You can mix them - place a regular tk.Text inside a CTkFrame - but expect a small visual seam. For a fully unified look, wrap them in a frame and tint the surrounding chrome to match.
Designer support
Hand-porting a large UI by hand is tedious. A CustomTkinter-aware designer cuts this down to minutes: open the existing layout, swap widget classes in bulk, retheme, export.