How to add placeholder to an Entry in tkinter?

My solution is to subclass the tk.Entry and control the content and color, binding the <FocusIn> and <FocusOut> events to methods that fill and clear the text as necessary. This is the behavior:

enter image description here

Here the complete example code:

import tkinter as tk

class PlaceholderEntry(tk.Entry):
    def __init__(self, master=None, placeholder='', cnf={}, fg='black',
                 fg_placeholder='grey50', *args, **kw):
        super().__init__(master=None, cnf={}, bg='white', *args, **kw)
        self.fg = fg
        self.fg_placeholder = fg_placeholder
        self.placeholder = placeholder
        self.bind('<FocusOut>', lambda event: self.fill_placeholder())
        self.bind('<FocusIn>', lambda event: self.clear_box())
        self.fill_placeholder()

    def clear_box(self):
        if not self.get() and super().get():
            self.config(fg=self.fg)
            self.delete(0, tk.END)

    def fill_placeholder(self):
        if not super().get():
            self.config(fg=self.fg_placeholder)
            self.insert(0, self.placeholder)
    
    def get(self):
        content = super().get()
        if content == self.placeholder:
            return ''
        return content

class App(tk.Frame):
    def __init__(self, master=None):
        self.root = master
        super().__init__(master, borderwidth=0, relief=tk.RAISED)
        
        self.root.title('Placeholder example')
        self.pack_propagate(False)
        self.pack()
        
        self.entry = PlaceholderEntry(self.root, placeholder='This text is a placeholder')
        self.entry.pack()
        
        self.btn = tk.Button(self.root, text='Nothing', highlightcolor='cyan')
        self.btn.pack()
        

root = tk.Tk()
app = App(master=root)

app.root.mainloop()

You need to set a default value for this entry. Like this:

from tkinter import *

ui = Tk()

e1 = Entry(ui)
e1.insert(0, 'username')
e1.pack()

ui.mainloop()

Then if you want to delete the content when you click the entry, then you have to bind a mouse click event with an event handler method to update content of this entry. Here is a link for you.


You can create a class that inherits from Entry like below:

import tkinter as tk

class EntryWithPlaceholder(tk.Entry):
    def __init__(self, master=None, placeholder="PLACEHOLDER", color='grey'):
        super().__init__(master)

        self.placeholder = placeholder
        self.placeholder_color = color
        self.default_fg_color = self['fg']

        self.bind("<FocusIn>", self.foc_in)
        self.bind("<FocusOut>", self.foc_out)

        self.put_placeholder()

    def put_placeholder(self):
        self.insert(0, self.placeholder)
        self['fg'] = self.placeholder_color

    def foc_in(self, *args):
        if self['fg'] == self.placeholder_color:
            self.delete('0', 'end')
            self['fg'] = self.default_fg_color

    def foc_out(self, *args):
        if not self.get():
            self.put_placeholder()

if __name__ == "__main__": 
    root = tk.Tk() 
    username = EntryWithPlaceholder(root, "username")
    password = EntryWithPlaceholder(root, "password", 'blue')
    username.pack()
    password.pack()  
    root.mainloop()

Updated (Improved Answer):

  • Use the on_focus_out function to reinsert the placeholder if the text field is empty (if you don't want this to happen, you can use the method from the older code)
import tkinter as tk


def on_focus_in(entry):
    if entry.cget('state') == 'disabled':
        entry.configure(state='normal')
        entry.delete(0, 'end')


def on_focus_out(entry, placeholder):
    if entry.get() == "":
        entry.insert(0, placeholder)
        entry.configure(state='disabled')


root = tk.Tk()

entry_x = tk.Entry(root, width=50)
entry_x.pack(pady=10)
entry_x.insert(0, "Place Holder X")
entry_x.configure(state='disabled')

entry_y = tk.Entry(root, width=50)
entry_y.pack(pady=10)
entry_y.insert(0, "Place Holder Y")
entry_y.configure(state='disabled')

x_focus_in = entry_x.bind('<Button-1>', lambda x: on_focus_in(entry_x))
x_focus_out = entry_x.bind(
    '<FocusOut>', lambda x: on_focus_out(entry_x, 'Place Holder X'))

y_focus_in = entry_y.bind('<Button-1>', lambda x: on_focus_in(entry_y))
y_focus_out = entry_y.bind(
    '<FocusOut>', lambda x: on_focus_out(entry_y, 'Place Holder Y'))

root.mainloop()

Note:

  • It is discouraged to import *, so we should import like this import tkinter as tk.
  • I have created two Entry widgets to depict the changes.

Old (Not Recommended):

This will work for any placeholder you want.

from tkinter import *
root = Tk()

my_entry = Entry(root, width=50)
my_entry.pack()
my_entry.insert(0, "Place Holder")
my_entry.configure(state=DISABLED)

def on_click(event):
    my_entry.configure(state=NORMAL)
    my_entry.delete(0, END)

    # make the callback only work once
    my_entry.unbind('<Button-1>', on_click_id)

on_click_id = my_entry.bind('<Button-1>', on_click)

root.mainloop()