Skip to main content
Model tabs provide a powerful navigation system that allows you to create custom tab-based navigation across different models, views, and pages in your Django admin interface.

Overview

Model tabs are configured at the site level and allow you to group related models or views into a tabbed interface. This is useful for creating dashboard-like views or organizing related administrative tasks.
Model tabs navigation example

Configuration

Model tabs are configured in your admin site settings using the TABS configuration:
admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.sites import UnfoldAdminSite

class CustomAdminSite(UnfoldAdminSite):
    def get_tabs_list(self, request):
        return [
            {
                'models': ['app_name.modelname'],
                'items': [
                    {'link': '/admin/app/model/', 'title': 'All Items'},
                    {'link': '/admin/app/model/?status=active', 'title': 'Active'},
                    {'link': '/admin/app/model/?status=archived', 'title': 'Archived'},
                ]
            }
        ]

admin_site = CustomAdminSite(name='unfoldadmin')
Model tabs are defined in the TABS configuration and can be customized per request.

Tab Structure

Each tab configuration consists of:
models
list[str] | list[dict]
List of model identifiers that should display these tabs. Can be strings in 'app.model' format or dictionaries with additional options.
items
list[dict]
List of tab items to display. Each item must have link and title properties.
page
str
Specific page identifier if tabs should only appear on certain pages (e.g., 'changelist', 'changeform').

Basic Example

Create tabs for filtering products by status:
admin.py
from unfold.sites import UnfoldAdminSite

class MyAdminSite(UnfoldAdminSite):
    def get_tabs_list(self, request):
        return [
            {
                'models': ['shop.product'],
                'items': [
                    {
                        'title': 'All Products',
                        'link': '/admin/shop/product/',
                    },
                    {
                        'title': 'In Stock',
                        'link': '/admin/shop/product/?stock__gt=0',
                    },
                    {
                        'title': 'Out of Stock',
                        'link': '/admin/shop/product/?stock=0',
                    },
                    {
                        'title': 'Discontinued',
                        'link': '/admin/shop/product/?status=discontinued',
                    },
                ]
            }
        ]

Model-Specific Tabs

You can specify different tabs for changelist and detail views:
admin.py
def get_tabs_list(self, request):
    return [
        {
            'models': [
                {'name': 'shop.product', 'detail': False}  # Only on changelist
            ],
            'items': [
                {'title': 'All Products', 'link': '/admin/shop/product/'},
                {'title': 'Featured', 'link': '/admin/shop/product/?featured=true'},
            ]
        },
        {
            'models': [
                {'name': 'shop.product', 'detail': True}  # Only on detail view
            ],
            'items': [
                {'title': 'Overview', 'link': '?tab=overview'},
                {'title': 'Analytics', 'link': '?tab=analytics'},
            ]
        }
    ]
Use the detail key in model dictionaries to control whether tabs appear on the changelist (detail=False) or change form (detail=True).

Page-Specific Tabs

Tabs can be limited to specific page types:
admin.py
def get_tabs_list(self, request):
    return [
        {
            'page': 'changelist',  # Only on list view
            'items': [
                {'title': 'Dashboard', 'link': '/admin/dashboard/'},
                {'title': 'Reports', 'link': '/admin/reports/'},
            ]
        }
    ]

Dynamic Tab Generation

Generate tabs dynamically based on user permissions or other criteria:
admin.py
class MyAdminSite(UnfoldAdminSite):
    def get_tabs_list(self, request):
        tabs = [
            {
                'models': ['shop.product'],
                'items': [
                    {'title': 'All Products', 'link': '/admin/shop/product/'},
                ]
            }
        ]
        
        # Add admin-only tabs
        if request.user.is_superuser:
            tabs[0]['items'].extend([
                {'title': 'Pending Review', 'link': '/admin/shop/product/?status=pending'},
                {'title': 'Flagged', 'link': '/admin/shop/product/?flagged=true'},
            ])
        
        return tabs

Active Tab Detection

The system automatically detects the active tab by comparing:
  1. The current URL path
  2. Query parameters
admin.py
# The active tab is automatically highlighted when:
# - The link matches the current path
# - All query params in the link match the current query params

{
    'title': 'Active Products',
    'link': '/admin/shop/product/?status=active',
    # Will be active when viewing /admin/shop/product/?status=active
    # But not when viewing /admin/shop/product/?status=active&page=2
}
For active tab detection to work correctly, ensure your tab links include all necessary query parameters.
Generate links dynamically using the link_callback option:
admin.py
from django.urls import reverse

def get_tabs_list(self, request):
    return [
        {
            'models': ['shop.product'],
            'items': [
                {
                    'title': 'All Products',
                    'link_callback': lambda req: reverse('admin:shop_product_changelist'),
                },
                {
                    'title': 'My Products',
                    'link_callback': lambda req: f"{reverse('admin:shop_product_changelist')}?owner={req.user.id}",
                },
            ]
        }
    ]
Use link_callback when you need to generate links dynamically based on the request object.

Combining with Sidebar Navigation

Model tabs work alongside sidebar navigation. When a sidebar link is active, the system checks if any associated model tabs should also be highlighted:
admin.py
class MyAdminSite(UnfoldAdminSite):
    def get_navigation(self, request):
        return [
            {
                'title': 'Products',
                'link': '/admin/shop/product/',
                # This sidebar link will be active when any product tab is active
            },
        ]
    
    def get_tabs_list(self, request):
        return [
            {
                'models': ['shop.product'],
                'items': [
                    {'title': 'All', 'link': '/admin/shop/product/'},
                    {'title': 'Active', 'link': '/admin/shop/product/?status=active'},
                ]
            }
        ]

Permissions

Filter tabs based on user permissions:
admin.py
class MyAdminSite(UnfoldAdminSite):
    def get_tabs_list(self, request):
        tabs = [
            {
                'models': ['shop.product'],
                'items': []
            }
        ]
        
        # Check permissions before adding items
        from shop.models import Product
        opts = Product._meta
        
        if request.user.has_perm(f'{opts.app_label}.view_{opts.model_name}'):
            tabs[0]['items'].append({
                'title': 'All Products',
                'link': '/admin/shop/product/',
            })
        
        if request.user.has_perm(f'{opts.app_label}.change_{opts.model_name}'):
            tabs[0]['items'].append({
                'title': 'Pending Approval',
                'link': '/admin/shop/product/?status=pending',
            })
        
        return tabs

Best Practices

Choose clear, action-oriented titles that describe what users will see.
# Good
{'title': 'Active Orders', 'link': '...'}
{'title': 'Pending Shipment', 'link': '...'}

# Avoid
{'title': 'Tab 1', 'link': '...'}
{'title': 'Other', 'link': '...'}
Use consistent query parameter names across your tabs for better maintainability.
# Good - consistent parameter names
{'title': 'Active', 'link': '/admin/shop/product/?status=active'}
{'title': 'Inactive', 'link': '/admin/shop/product/?status=inactive'}

# Avoid - inconsistent parameters
{'title': 'Active', 'link': '/admin/shop/product/?status=active'}
{'title': 'Inactive', 'link': '/admin/shop/product/?is_active=false'}
Too many tabs can overwhelm users. Consider grouping or using a different navigation pattern if you need more than 6-7 tabs.
Tab navigation adapts to smaller screens, but test your tab configuration on mobile devices to ensure a good experience.

Technical Details

Model tabs are processed through:
  • unfold.sites.UnfoldAdminSite.get_tabs_list() - Returns tab configuration
  • unfold.sites.UnfoldAdminSite._get_is_tab_active() - Determines active tab
  • unfold.templatetags.unfold.tab_list - Renders tabs in templates
Template tag usage:
{% load unfold %}
{% tab_list 'changelist' opts %}

Inline Tabs

Organize related model inlines in tabs

Fieldset Tabs

Create tabs within fieldsets

Sidebar Navigation

Configure the sidebar navigation

Custom Pages

Add custom pages to the admin

Build docs developers (and LLMs) love