unfold-admin

Django Unfold admin theme - build, configure, and enhance modern Django admin interfaces with Unfold. Use when working with: (1) Django admin UI customisation or theming, (2) Unfold ModelAdmin, inlines, actions, filters, widgets, or decorators, (3) Admin dashboard components and KPI cards, (4) Sidebar navigation, tabs, or conditional fields, (5) Any mention of 'unfold', 'django-unfold', or 'unfold admin'. Covers the full Unfold feature set: site configuration, actions system, display decorators, filter types, widget overrides, inline variants, dashboard components, datasets, sections, theming, and third-party integrations.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "unfold-admin" with this command: npx skills add 0xdarkmatter/claude-mods/0xdarkmatter-claude-mods-unfold-admin

Django Unfold Admin

Modern Django admin theme with Tailwind CSS, HTMX, and Alpine.js. Replaces Django's default admin with a polished, feature-rich interface.

Quick Start

Installation

# settings.py - unfold MUST be before django.contrib.admin
INSTALLED_APPS = [
    "unfold",
    "unfold.contrib.filters",          # advanced filters
    "unfold.contrib.forms",            # array/wysiwyg widgets
    "unfold.contrib.inlines",          # nonrelated inlines
    "unfold.contrib.import_export",    # styled import/export
    "unfold.contrib.guardian",         # django-guardian integration
    "unfold.contrib.simple_history",   # django-simple-history integration
    "unfold.contrib.constance",        # django-constance integration
    "unfold.contrib.location_field",   # django-location-field integration
    # ...
    "django.contrib.admin",
]

Minimal Admin

from unfold.admin import ModelAdmin

@admin.register(MyModel)
class MyModelAdmin(ModelAdmin):
    pass  # inherits Unfold styling

Site Configuration

Replace the default AdminSite or configure via UNFOLD dict in settings. See references/configuration.md for the complete settings reference.

UNFOLD = {
    "SITE_TITLE": "My Admin",
    "SITE_HEADER": "My Admin",
    "SITE_SYMBOL": "dashboard",  # Material Symbols icon name
    "SIDEBAR": {
        "show_search": True,
        "navigation": [
            {
                "title": _("Navigation"),
                "items": [
                    {
                        "title": _("Dashboard"),
                        "icon": "dashboard",
                        "link": reverse_lazy("admin:index"),
                    },
                ],
            },
        ],
    },
}

Core Workflow

When building Unfold admin interfaces, follow this sequence:

  1. Configure site - UNFOLD settings dict (branding, sidebar, theme)
  2. Register models - Extend unfold.admin.ModelAdmin
  3. Enhance display - @display decorator for list columns
  4. Add actions - @action decorator for row/list/detail/submit actions
  5. Configure filters - Replace default filters with Unfold filter classes
  6. Override widgets - Apply Unfold widgets via formfield_overrides
  7. Set up inlines - Use Unfold's inline classes with tabs, pagination, sorting
  8. Build dashboard - @register_component + BaseComponent for KPI cards

ModelAdmin Attributes

Unfold extends Django's ModelAdmin with these additional attributes:

AttributeTypePurpose
list_fullwidthboolFull-width changelist (no sidebar)
list_filter_submitboolAdd submit button to filters
list_filter_sheetboolFilters in sliding sheet panel
compressed_fieldsboolCompact field spacing in forms
warn_unsaved_formboolWarn before leaving unsaved form
ordering_fieldstrField name for drag-to-reorder
hide_ordering_fieldboolHide the ordering field column
list_horizontal_scrollbar_topboolScrollbar at top of list
list_disable_select_allboolDisable "select all" checkbox
change_form_show_cancel_buttonboolShow cancel button on form
actions_listlistGlobal changelist actions
actions_rowlistPer-row actions in changelist
actions_detaillistActions on change form
actions_submit_linelistActions in form submit area
actions_list_hide_defaultboolHide default list actions
actions_detail_hide_defaultboolHide default detail actions
conditional_fieldsdictJS expressions for field visibility
change_form_datasetslistBaseDataset subclasses for change form
list_sectionslistTableSection/TemplateSection for list
list_sections_classesstrCSS grid classes for sections
readonly_preprocess_fieldsdictTransform readonly field content
add_fieldsetslistSeparate fieldsets for add form (like UserAdmin)

Template Injection Points

Insert custom HTML before/after changelist or change form:

class MyAdmin(ModelAdmin):
    # Changelist
    list_before_template = "myapp/list_before.html"
    list_after_template = "myapp/list_after.html"
    # Change form (inside <form> tag)
    change_form_before_template = "myapp/form_before.html"
    change_form_after_template = "myapp/form_after.html"
    # Change form (outside <form> tag)
    change_form_outer_before_template = "myapp/outer_before.html"
    change_form_outer_after_template = "myapp/outer_after.html"

Conditional Fields

Show/hide fields based on other field values (Alpine.js expressions):

class MyAdmin(ModelAdmin):
    conditional_fields = {
        "premium_features": "plan == 'PRO'",
        "discount_amount": "has_discount == true",
    }

Actions System

Four action types, each with different signatures. See references/actions-filters.md for complete reference.

from unfold.decorators import action
from unfold.enums import ActionVariant

# List action (no object context)
@action(description=_("Rebuild Index"), icon="sync", variant=ActionVariant.PRIMARY)
def rebuild_index(self, request):
    # process...
    return redirect(request.headers["referer"])

# Row action (receives object_id)
@action(description=_("Approve"), url_path="approve")
def approve_row(self, request, object_id):
    obj = self.model.objects.get(pk=object_id)
    return redirect(request.headers["referer"])

# Detail action (receives object_id, shown on change form)
@action(description=_("Send Email"), permissions=["send_email"])
def send_email(self, request, object_id):
    return redirect(reverse_lazy("admin:myapp_mymodel_change", args=[object_id]))

# Submit line action (receives obj instance, runs on save)
@action(description=_("Save & Publish"))
def save_and_publish(self, request, obj):
    obj.published = True

Action Groups (Dropdown Menus)

actions_list = [
    "primary_action",
    {
        "title": _("More"),
        "variant": ActionVariant.PRIMARY,
        "items": ["secondary_action", "tertiary_action"],
    },
]

Permissions

@action(permissions=["can_export", "auth.view_user"])
def export_data(self, request):
    pass

def has_can_export_permission(self, request):
    return request.user.is_superuser

Display Decorator

Enhance list_display columns. See references/actions-filters.md.

from unfold.decorators import display

# Colored status labels
@display(description=_("Status"), ordering="status", label={
    "active": "success",    # green
    "pending": "info",      # blue
    "warning": "warning",   # orange
    "inactive": "danger",   # red
})
def show_status(self, obj):
    return obj.status

# Rich header with avatar
@display(description=_("User"), header=True)
def show_header(self, obj):
    return [
        obj.full_name,           # primary text
        obj.email,               # secondary text
        obj.initials,            # badge text
        {"path": obj.avatar.url, "width": 24, "height": 24, "borderless": True},
    ]

# Interactive dropdown
@display(description=_("Teams"), dropdown=True)
def show_teams(self, obj):
    return {
        "title": f"{obj.teams.count()} teams",
        "items": [{"title": t.name, "link": t.get_admin_url()} for t in obj.teams.all()],
        "striped": True,
        "max_height": 200,
    }

# Boolean checkmark
@display(description=_("Active"), boolean=True)
def is_active(self, obj):
    return obj.is_active

Filters

Unfold provides advanced filter classes. See references/actions-filters.md.

from unfold.contrib.filters.admin import (
    TextFilter, RangeNumericFilter, RangeDateFilter, RangeDateTimeFilter,
    SingleNumericFilter, SliderNumericFilter, RelatedDropdownFilter,
    RelatedCheckboxFilter, ChoicesCheckboxFilter, AllValuesCheckboxFilter,
    BooleanRadioFilter, CheckboxFilter, AutocompleteSelectMultipleFilter,
)

class MyAdmin(ModelAdmin):
    list_filter_submit = True  # required for input-based filters
    list_filter = [
        ("salary", RangeNumericFilter),
        ("status", ChoicesCheckboxFilter),
        ("created_at", RangeDateFilter),
        ("category", RelatedDropdownFilter),
        ("is_active", BooleanRadioFilter),
    ]

Custom Text Filter

class NameFilter(TextFilter):
    title = _("Name")
    parameter_name = "name"

    def queryset(self, request, queryset):
        if self.value() in EMPTY_VALUES:
            return queryset
        return queryset.filter(name__icontains=self.value())

Widgets

Override form widgets for Unfold styling. See references/widgets-inlines.md.

from unfold.widgets import (
    UnfoldAdminTextInputWidget, UnfoldAdminSelectWidget, UnfoldAdminSelect2Widget,
    UnfoldBooleanSwitchWidget, UnfoldAdminColorInputWidget,
    UnfoldAdminSplitDateTimeWidget, UnfoldAdminImageFieldWidget,
)
from unfold.contrib.forms.widgets import WysiwygWidget, ArrayWidget

class MyAdmin(ModelAdmin):
    formfield_overrides = {
        models.TextField: {"widget": WysiwygWidget},
        models.ImageField: {"widget": UnfoldAdminImageFieldWidget},
    }

Text Input with Icons

widget = UnfoldAdminTextInputWidget(attrs={
    "prefix_icon": "search",
    "suffix_icon": "euro",
})

Inlines

Unfold inlines support tabs, pagination, sorting, and nonrelated models. See references/widgets-inlines.md.

from unfold.admin import TabularInline, StackedInline
from unfold.contrib.inlines.admin import NonrelatedStackedInline

class OrderItemInline(TabularInline):
    model = OrderItem
    tab = True          # show as tab
    per_page = 10       # paginated
    ordering_field = "weight"  # drag-to-reorder
    hide_title = True
    collapsible = True

Fieldset Tabs

Group fieldsets into tabs using "classes": ["tab"]:

fieldsets = [
    (None, {"fields": ["name", "email"]}),  # always visible
    (_("Profile"), {"classes": ["tab"], "fields": ["bio", "avatar"]}),
    (_("Settings"), {"classes": ["tab"], "fields": ["theme", "notifications"]}),
]

Dashboard Components

Build KPI cards and custom dashboard widgets. See references/dashboard.md.

from unfold.components import BaseComponent, register_component
from django.template.loader import render_to_string

@register_component
class ActiveUsersComponent(BaseComponent):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["children"] = render_to_string("myapp/kpi_card.html", {
            "total": User.objects.filter(is_active=True).count(),
            "label": "Active Users",
        })
        return context

Configure in settings:

UNFOLD = {
    "DASHBOARD_CALLBACK": "myapp.views.dashboard_callback",
}

Sections (Changelist Panels)

Embed related data panels in changelist views:

from unfold.sections import TableSection, TemplateSection

class RecentOrdersSection(TableSection):
    related_name = "order_set"
    fields = ["id", "total", "status"]
    height = 380

class ChartSection(TemplateSection):
    template_name = "myapp/chart.html"

class MyAdmin(ModelAdmin):
    list_sections = [RecentOrdersSection, ChartSection]
    list_sections_classes = "lg:grid-cols-2"

Datasets (Change Form Panels)

Embed model listings within change forms:

from unfold.datasets import BaseDataset

class RelatedItemsDatasetAdmin(ModelAdmin):
    list_display = ["name", "status"]
    search_fields = ["name"]

class RelatedItemsDataset(BaseDataset):
    model = RelatedItem
    model_admin = RelatedItemsDatasetAdmin
    tab = True  # show as tab

class MyAdmin(ModelAdmin):
    change_form_datasets = [RelatedItemsDataset]

Paginator

Use infinite scroll pagination:

from unfold.paginator import InfinitePaginator

class MyAdmin(ModelAdmin):
    paginator = InfinitePaginator
    show_full_result_count = False
    list_per_page = 20

Third-Party Integrations

Unfold provides styled wrappers for common Django packages. See references/resources.md for complete setup guides.

PackageUnfold ModuleSetup
django-import-exportunfold.contrib.import_exportUse ImportForm, ExportForm, SelectableFieldsExportForm
django-guardianunfold.contrib.guardianStyled guardian integration
django-simple-historyunfold.contrib.simple_historyStyled history integration
django-constanceunfold.contrib.constanceStyled constance config
django-location-fieldunfold.contrib.location_fieldLocation widget
django-modeltranslationCompatibleMix TabbedTranslationAdmin with ModelAdmin
django-celery-beatCompatible (rewire)Unregister 5 models, re-register with Unfold
django-moneyunfold.widgetsUnfoldAdminMoneyWidget
djangoqlCompatibleMix DjangoQLSearchMixin with ModelAdmin
django-crispy-formsCompatibleUnfold template pack available
# Multiple inheritance - Unfold ModelAdmin always last
@admin.register(MyModel)
class MyAdmin(DjangoQLSearchMixin, SimpleHistoryAdmin, GuardedModelAdmin, ModelAdmin):
    pass

Built-In Template Components

Unfold ships reusable template components for dashboards and custom pages:

ComponentPathKey Variables
Cardunfold/components/card.htmltitle, footer, label, icon
Bar Chartunfold/components/chart/bar.htmldata (JSON), height, width
Line Chartunfold/components/chart/line.htmldata (JSON), height, width
Progressunfold/components/progress.htmlvalue, title, description
Tableunfold/components/table.htmltable, card_included, striped
Buttonunfold/components/button.htmlname, href, submit
Trackerunfold/components/tracker.htmldata
Cohortunfold/components/cohort.htmldata
{% load unfold %}
{% component "MyKPIComponent" %}{% endcomponent %}

User Admin Forms

Unfold provides styled versions of Django's auth admin forms:

from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm

@admin.register(User)
class UserAdmin(BaseUserAdmin, ModelAdmin):
    form = UserChangeForm
    add_form = UserCreationForm
    change_password_form = AdminPasswordChangeForm

Reference Files

Detailed documentation split by topic:

Read the relevant reference file when you need detailed configuration options, the full list of available classes, complete code examples, or integration setup guides for a specific feature area.

Key External References

When uncertain about an implementation pattern, consult formula/admin.py and formula/settings.py in the Formula demo repo - it covers virtually every Unfold feature.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

file-search

No summary provided by upstream source.

Repository SourceNeeds Review
General

container-orchestration

No summary provided by upstream source.

Repository SourceNeeds Review
General

find-replace

No summary provided by upstream source.

Repository SourceNeeds Review
General

task-runner

No summary provided by upstream source.

Repository SourceNeeds Review