Simplifies creating demo pages in django-mvp by using standardized view classes and template inheritance.

Install

mkdir -p .claude/skills/demo-views && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10300" && unzip -o skill.zip -d .claude/skills/demo-views && rm skill.zip

Installs to .claude/skills/demo-views

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Guide for creating demo views in the django-mvp Demo App. Use when adding new demo pages to showcase components, layouts, or features — covers view creation, template structure, URL wiring, and sidebar menu registration. Emphasizes single configurable view classes with .as_view() attribute overrides instead of duplicating views.
330 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Create demo views
  • Wire URL patterns
  • Register sidebar menus
  • Configure layout variations

How it works

It standardizes demo page creation using configurable view classes and template blocks.

Inputs & outputs

You give it
Demo requirements
You get back
Configured demo view and template

When to use demo-views

  • Adding new component demos
  • Configuring layout variations for demo pages
  • Wiring new URL patterns in the demo app

About this skill

Creating Demo Views for the Demo App

The demo/ app demonstrates django-mvp features. Each demo page follows a consistent pattern: one configurable view class, a template extending base.html, a URL pattern (with optional .as_view() overrides), and a sidebar menu entry.

File Locations

FilePurpose
demo/views.pyView classes
demo/urls.pyURL patterns
demo/menus.pySidebar menu items
demo/templates/demo/<name>.htmlPage templates
demo/templates/base.htmlProject-level base (extends mvp/base.html)

Step 1: Create the View

Always include LayoutConfigMixin

Every demo view must inherit LayoutConfigMixin first — it reads query parameters for interactive layout configuration (fixed sidebar/header/footer, breakpoints, etc.):

from django.views.generic import TemplateView
from demo.views import LayoutConfigMixin

class MyDemoView(LayoutConfigMixin, TemplateView):
    template_name = "demo/my_demo.html"

One View, Many Configurations — Use .as_view() Overrides

Do NOT create separate view classes for minor variations. Instead, define one view with configurable class attributes and override them in urls.py:

# views.py — ONE view class
class WidgetDemoView(LayoutConfigMixin, TemplateView):
    template_name = "demo/widget_demo.html"
    variant = "default"
    show_icons = True
# urls.py — multiple URL patterns with .as_view() overrides
urlpatterns = [
    path("widgets/", WidgetDemoView.as_view(), name="widget_demo"),
    path("widgets/compact/", WidgetDemoView.as_view(variant="compact"), name="widget_demo_compact"),
    path("widgets/no-icons/", WidgetDemoView.as_view(show_icons=False), name="widget_demo_no_icons"),
]

Only create a separate view class when it needs different methods or significantly different behavior.

View Patterns by Django Generic View Type

TemplateView — simplest; for static demos or component showcases:

class MyDemoView(LayoutConfigMixin, TemplateView):
    template_name = "demo/my_demo.html"

ListView — for list/grid demos; combine with MVPListViewMixin and PageModifierMixin:

from mvp.views import MVPListViewMixin, PageModifierMixin

class MyListDemo(LayoutConfigMixin, PageModifierMixin, MVPListViewMixin, ListView):
    model = MyModel
    template_name = "mvp/list_view.html"
    list_item_template = "cards/my_card.html"
    paginate_by = 12
    grid = {"cols": 1, "md": 2}          # Override via .as_view(grid={...})
    search_fields = ["name", "description"]  # Optional
    order_by = [("name", "Name (A-Z)"), ("-name", "Name (Z-A)")]  # Optional

CreateView / FormView — for form demos:

class MyFormDemo(LayoutConfigMixin, CreateView):
    model = MyModel
    fields = ["name", "description"]
    template_name = "mvp/form_view.html"
    extra_context = {"page_title": "My Form Demo"}

FilterView — for filterable list demos (django-filter):

from django_filters.views import FilterView

class MyFilterDemo(LayoutConfigMixin, PageModifierMixin, MVPListViewMixin, FilterView):
    model = MyModel
    template_name = "mvp/list_view.html"
    list_item_template = "cards/my_card.html"
    filterset_fields = ["category", "status"]

SingleTableView — for django-tables2 demos:

from django_tables2 import SingleTableView

class MyTableDemo(LayoutConfigMixin, SingleTableView):
    model = MyModel
    table_class = MyTable
    template_name = "demo/my_table.html"
    paginate_by = 25

Adding Extra Context

Use get_context_data when the template needs dynamic data. Always call super():

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context["sample_items"] = [...]
    return context

For static context, prefer extra_context:

class MyDemo(LayoutConfigMixin, TemplateView):
    template_name = "demo/my_demo.html"
    extra_context = {"page_title": "My Demo"}

Step 2: Create the Template

Templates live in demo/templates/demo/ and extend base.html.

Block Hierarchy

base.html (extends mvp/base.html, overrides "app")
├── navbar_widgets     — navbar widget components (top-right)
├── main               — wraps <c-page>; override to replace entire inner layout
│   ├── toolbar
│   │   └── toolbar_content
│   │       ├── breadcrumbs  — add breadcrumb items
│   │       └── toolbar_widgets — toolbar actions (right side)
│   ├── content        — main page content ← MOST COMMON OVERRIDE
│   └── footer         — page footer
├── app_footer         — app-level footer
└── extra_js           — additional scripts

Typical Template (override breadcrumbs + content)

Most demo templates only need two blocks:

{% extends "base.html" %}

{% block breadcrumbs %}
  <c-breadcrumbs.item text="My Demo" />
{% endblock breadcrumbs %}

{% block content %}
  <div class="container-fluid">
    <h2>My Demo</h2>
    <!-- demo content here -->
  </div>
{% endblock content %}

When to Override Other Blocks

BlockOverride when...
breadcrumbsAlways — add the page breadcrumb
contentAlways — the main demo content
toolbar_widgetsPage needs action buttons in the toolbar
footerPage needs a custom footer or no footer
mainPage needs to replace the entire <c-page> structure (rare)
app (from mvp/base.html)Page needs to reconfigure <c-app> props like breakpoints (rare)
extra_jsPage needs additional JavaScript
navbar_widgetsPage needs custom navbar widgets (rare — usually keep defaults)

Template with Toolbar Widgets

{% extends "base.html" %}

{% block breadcrumbs %}
  <c-breadcrumbs.item text="My Demo" />
{% endblock breadcrumbs %}

{% block toolbar_widgets %}
  <button class="btn btn-sm btn-primary">Action</button>
{% endblock toolbar_widgets %}

{% block content %}
  <div class="container-fluid">
    <!-- content -->
  </div>
{% endblock content %}

Step 3: Add the URL Pattern

In demo/urls.py, add the path. Import only what's needed:

from .views import MyDemoView

urlpatterns = [
    # ... existing patterns ...
    path("my-demo/", MyDemoView.as_view(), name="my_demo"),
]

For configuration variants, override attributes via .as_view():

urlpatterns = [
    path("my-demo/", MyDemoView.as_view(), name="my_demo"),
    path("my-demo/alt/", MyDemoView.as_view(variant="alt"), name="my_demo_alt"),
]

Step 4: Add the Sidebar Menu Entry

In demo/menus.py, add items using AppMenu.extend() or mutate existing groups. Read the sidebar-menu skill for full reference.

Adding a standalone menu item

AppMenu.extend([
    MenuItem(
        name="my_demo",
        view_name="my_demo",
        extra_context={"label": "My Demo", "icon": "star"},
    ),
])

Adding to an existing MenuGroup

Use AppMenu.get() to find a group and add children:

group = AppMenu.get("list_views")
if group:
    group.add(MenuItem(
        name="my_list_variant",
        view_name="my_list_variant",
        extra_context={"label": "My Variant", "icon": "circle"},
    ))

Creating a new MenuGroup for related demos

MenuGroup(
    name="my_demos_section",
    extra_context={"label": "MY DEMOS"},
    children=[
        MenuItem(name="demo_a", view_name="demo_a",
                 extra_context={"label": "Demo A", "icon": "circle"}),
        MenuItem(name="demo_b", view_name="demo_b",
                 extra_context={"label": "Demo B", "icon": "circle"}),
    ],
)

Checklist

Before considering a demo view complete, verify:

  1. ✅ View inherits LayoutConfigMixin as first parent
  2. ✅ Only ONE view class exists per feature (variants use .as_view() overrides)
  3. ✅ Template extends base.html and overrides breadcrumbs + content at minimum
  4. ✅ URL pattern added in demo/urls.py with a named route
  5. ✅ Sidebar menu entry added in demo/menus.py (standalone or within a group)
  6. ✅ View name in MenuItem matches the URL pattern name

When not to use it

  • Non-django-mvp projects
  • Projects without demo app

Prerequisites

LayoutConfigMixin

Limitations

  • Requires LayoutConfigMixin
  • Limited to demo app structure

How it compares

It uses single configurable view classes instead of duplicating views for variations.

Compared to similar skills

demo-views side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
demo-views (this skill)01moNo flagsIntermediate
django-pro204moNo flagsIntermediate
python-configuration52moReviewBeginner
django-patterns34moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry