How to create custom form widgets with Django, AlpineJS and TailwindCSS
In this short guide, you will learn how to create a custom form widget for your Django app.
Prerequisites
- Basic knowledge of Django forms and views
- How to use AlpineJS, TailwindCSS with Django - see this post here
LEVEL - 💻 💻 Intermediate to advanced - Basic knowledge of Django forms and templates is assumed.
The problem
You have ditched javascript for your forms, and are using HTMX . Your forms are sleek, 100% Django views and forms, no page reloads on submission but your widgets still look like this.

Create a new widget
The form above is based on a basic user model with at least the following fields. Authentication is out of the scope of this article. If you need more information about user models visit this section in the docs.
users/models.py
users/forms.py
Notes
In Django, each model field is mapped to a default form field which in turn uses a default widget. A widget is Django’s representation of an HTML input element. It specifies specifying how the field will be rendered on the frontend and handles data extraction.
Using this setup, Django will use an image form field for the avatar model field. The default widget for images is the ClearableFileInput . It will render as <input type="file" ...>
with an additional checkbox input to clear the field’s value, if the field is not required and has initial data. The widget uses the django/forms/widgets/clearable_file_input.html
template.
Let's create a new custom file upload widget.
users/forms.py
Notes:
- We create a new file input widget class called
AvatarFileUploadInput
which inherits from the ClearableFileInput class and replaces the template.
- In the form class, we specify the new widget for the avatar field.
Add a custom file input template
In the snippet above, we specified a new template for our widget but have not created it yet. Let's do that now. For reference here is the original template from Django clearable_file_input.html
template.
clearable_file_input.html template [ Django source code ]
Our custom widget:
users/form_widgets/avatar_file_upload_input.html
Notes
x-data="{clear: false}"
- this will instantiate this div as an AlpineJS component. AlpineJS needs to be included in your templates. You can read the article on how to set this up here.<img class="mr-3" src="{{ widget.value.url }}"
- here we are replacing the avatar text link with an image for the preview. In the original template this is shown as an anchor tag with the widget value as the text.<a href="{{ widget.value.url }}">{{ widget.value }}</a>
- styling - TailwindCSS includes a collection of modifiers to help you with styling. In this example, we are using the file modifier to add some styling to our file upload field.
{% include "django/forms/widgets/attrs.html" %}
- default attributes included with the widget. for example the accept="image/* and id=avatar attribute- AlpineJS enhancements - You will notice that we bind the tailwind class
blur-sm
to the AlpineJS data variableclear
like this:class="clear && 'blur-sm'"
. We also bind the value of the clear input checkbox to the same variable x-model="clear". When a user checks the box the avatar will be blurred.
Conclusion
That's it, if you have done everything correctly your new file upload widget should look like this.


This is example is basic but demonstrates how flexible Django can be. Combined with AlpineJS and TailwindCSS, this is a powerful stack to build amazing web apps.
Comments
Post a Comment