Skip to main content

Base Widget Class

All widgets inherit from the base Widget class.
Widget(attrs=None)
attrs
dict
HTML attributes to add to the rendered widget. Common attributes include class, id, placeholder, etc.
widget = forms.TextInput(attrs={
    'class': 'form-control',
    'placeholder': 'Enter your name',
    'maxlength': '100',
})

Widget Properties

needs_multipart_form
bool
default:"False"
Whether the widget requires multipart form encoding (e.g., file uploads).
is_hidden
bool
Whether the widget renders as a hidden input.
media
Media
CSS and JavaScript files required by the widget.

Text Input Widgets

TextInput

Renders an <input type="text"> element.
TextInput(attrs=None)
name = forms.CharField(
    widget=forms.TextInput(attrs={'placeholder': 'Full name'})
)
Rendered HTML:
<input type="text" name="name" placeholder="Full name">

Textarea

Renders a <textarea> element.
Textarea(attrs=None)
Default attributes: {'cols': '40', 'rows': '10'}
comment = forms.CharField(
    widget=forms.Textarea(attrs={'rows': 5, 'cols': 50})
)
Rendered HTML:
<textarea name="comment" rows="5" cols="50"></textarea>

PasswordInput

Renders an <input type="password"> element.
PasswordInput(
    attrs=None,
    render_value=False
)
render_value
bool
default:"False"
Whether to render the value in the password field (not recommended for security).
password = forms.CharField(
    widget=forms.PasswordInput()
)

HiddenInput

Renders an <input type="hidden"> element.
HiddenInput(attrs=None)
user_id = forms.IntegerField(
    widget=forms.HiddenInput()
)

EmailInput

Renders an <input type="email"> element with HTML5 validation.
EmailInput(attrs=None)
email = forms.EmailField(
    widget=forms.EmailInput(attrs={'placeholder': '[email protected]'})
)

URLInput

Renders an <input type="url"> element.
URLInput(attrs=None)

NumberInput

Renders an <input type="number"> element.
NumberInput(attrs=None)
Supports min, max, and step attributes:
age = forms.IntegerField(
    widget=forms.NumberInput(attrs={'min': '0', 'max': '120'})
)

ColorInput

Renders an <input type="color"> element.
ColorInput(attrs=None)
background_color = forms.CharField(
    widget=forms.ColorInput()
)

SearchInput

Renders an <input type="search"> element.
SearchInput(attrs=None)

TelInput

Renders an <input type="tel"> element.
TelInput(attrs=None)

Date and Time Widgets

DateInput

Renders a text input for dates.
DateInput(
    attrs=None,
    format=None
)
format
str
Date format string (e.g., '%Y-%m-%d').
birthdate = forms.DateField(
    widget=forms.DateInput(attrs={'type': 'date'})
)

TimeInput

Renders a text input for times.
TimeInput(
    attrs=None,
    format=None
)
appointment = forms.TimeField(
    widget=forms.TimeInput(attrs={'type': 'time'})
)

DateTimeInput

Renders a text input for datetimes.
DateTimeInput(
    attrs=None,
    format=None
)
published_at = forms.DateTimeField(
    widget=forms.DateTimeInput(attrs={'type': 'datetime-local'})
)

SplitDateTimeWidget

Renders separate date and time inputs.
SplitDateTimeWidget(
    attrs=None,
    date_format=None,
    time_format=None,
    date_attrs=None,
    time_attrs=None
)
date_attrs
dict
Attributes for the date input.
time_attrs
dict
Attributes for the time input.
event = forms.SplitDateTimeField(
    widget=forms.SplitDateTimeWidget(
        date_attrs={'type': 'date'},
        time_attrs={'type': 'time'}
    )
)

SelectDateWidget

Renders three select boxes (year, month, day).
SelectDateWidget(
    attrs=None,
    years=None,
    months=None,
    empty_label=None
)
years
list | range
List or range of years to display. Defaults to current year + 10 years.
months
dict
Dictionary mapping month numbers to names.
empty_label
str | tuple
Label for empty option. Can be a single string or 3-tuple for (year, month, day).
birthdate = forms.DateField(
    widget=forms.SelectDateWidget(
        years=range(1920, 2024),
        empty_label=("Year", "Month", "Day")
    )
)

Choice Widgets

Select

Renders a <select> dropdown.
Select(
    attrs=None,
    choices=()
)
choices
list
List of 2-tuples (value, label) for options.
country = forms.ChoiceField(
    choices=[('us', 'United States'), ('uk', 'United Kingdom')],
    widget=forms.Select(attrs={'class': 'form-select'})
)
Rendered HTML:
<select name="country" class="form-select">
  <option value="us">United States</option>
  <option value="uk">United Kingdom</option>
</select>

SelectMultiple

Renders a multi-select <select multiple> box.
SelectMultiple(
    attrs=None,
    choices=()
)
hobbies = forms.MultipleChoiceField(
    choices=[('reading', 'Reading'), ('sports', 'Sports'), ('music', 'Music')],
    widget=forms.SelectMultiple()
)

RadioSelect

Renders a list of radio buttons.
RadioSelect(
    attrs=None,
    choices=()
)
STATUS_CHOICES = [
    ('active', 'Active'),
    ('inactive', 'Inactive'),
    ('pending', 'Pending'),
]

status = forms.ChoiceField(
    choices=STATUS_CHOICES,
    widget=forms.RadioSelect()
)
Rendered HTML:
<div>
  <label><input type="radio" name="status" value="active"> Active</label>
  <label><input type="radio" name="status" value="inactive"> Inactive</label>
  <label><input type="radio" name="status" value="pending"> Pending</label>
</div>

CheckboxSelectMultiple

Renders a list of checkboxes.
CheckboxSelectMultiple(
    attrs=None,
    choices=()
)
preferences = forms.MultipleChoiceField(
    choices=[('email', 'Email'), ('sms', 'SMS'), ('push', 'Push')],
    widget=forms.CheckboxSelectMultiple()
)

CheckboxInput

Renders a single checkbox.
CheckboxInput(
    attrs=None,
    check_test=None
)
check_test
callable
Function that takes a value and returns True if checkbox should be checked.
agree = forms.BooleanField(
    widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)

NullBooleanSelect

Renders a select with True/False/Unknown options.
NullBooleanSelect(attrs=None)
Options:
  • "unknown" → Unknown
  • "true" → Yes
  • "false" → No
is_active = forms.NullBooleanField(
    widget=forms.NullBooleanSelect()
)

File Widgets

FileInput

Renders an <input type="file"> element.
FileInput(attrs=None)
attachment = forms.FileField(
    widget=forms.FileInput(attrs={'accept': '.pdf,.doc,.docx'})
)

ClearableFileInput

File input with option to clear existing file.
ClearableFileInput(attrs=None)
Provides a checkbox to clear the current file value.
avatar = forms.ImageField(
    widget=forms.ClearableFileInput()
)
Rendered HTML includes:
  • Current file link
  • Clear checkbox
  • New file input

Composite Widgets

MultiWidget

Base class for widgets composed of multiple widgets.
MultiWidget(
    widgets,
    attrs=None
)
widgets
list | dict
required
List or dictionary of widget instances.
Must implement decompress(value) method to split value for sub-widgets.
class SplitNameWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        widgets = [
            forms.TextInput(attrs={'placeholder': 'First'}),
            forms.TextInput(attrs={'placeholder': 'Last'}),
        ]
        super().__init__(widgets, attrs)
    
    def decompress(self, value):
        if value:
            return value.split(' ', 1)
        return ['', '']

MultipleHiddenInput

Renders multiple hidden inputs for a list of values.
MultipleHiddenInput(attrs=None)
Used automatically for MultipleChoiceField with hidden widget.

Widget Customization

Adding CSS Classes

field = forms.CharField(
    widget=forms.TextInput(attrs={
        'class': 'form-control custom-input',
    })
)

Adding Placeholder Text

email = forms.EmailField(
    widget=forms.EmailInput(attrs={
        'placeholder': '[email protected]',
    })
)

Adding Data Attributes

price = forms.DecimalField(
    widget=forms.NumberInput(attrs={
        'data-currency': 'USD',
        'data-precision': '2',
    })
)

Disabling Autocomplete

password = forms.CharField(
    widget=forms.PasswordInput(attrs={
        'autocomplete': 'new-password',
    })
)

Adding ARIA Attributes

name = forms.CharField(
    widget=forms.TextInput(attrs={
        'aria-label': 'Your full name',
        'aria-describedby': 'name-help',
    })
)

Widget Media

Widgets can declare CSS and JavaScript dependencies:
class CustomWidget(forms.Widget):
    class Media:
        css = {
            'all': ('custom.css',)
        }
        js = ('custom.js',)
Access in templates:
<head>
  {{ form.media.css }}
  {{ form.media.js }}
</head>
Or separately:
{{ form.media.css }}
{{ form.media.js }}

Template Rendering

Widgets use Django templates for rendering. Default templates:
  • django/forms/widgets/text.html
  • django/forms/widgets/select.html
  • django/forms/widgets/checkbox.html
  • etc.
Customize by overriding template_name:
class CustomTextInput(forms.TextInput):
    template_name = 'myapp/widgets/custom_text.html'

Widget Context

Widgets provide context variables to templates:
{
    'widget': {
        'name': 'field_name',
        'value': 'current value',
        'attrs': {'class': 'form-control'},
        'is_hidden': False,
        'required': True,
        'template_name': 'django/forms/widgets/text.html',
    }
}

Build docs developers (and LLMs) love