Skip to content

Subclassing components

In larger projects, you might need to write multiple components with similar behavior. In such cases, you can extract shared behavior into a standalone component class to keep things DRY.

When subclassing a component, there's a couple of things to keep in mind:

Template, JS, and CSS Inheritance¤

When it comes to the pairs:

inheritance follows these rules:

  • If a child component class defines either member of a pair (e.g., either template or template_file), it takes precedence and the parent's definition is ignored completely.
  • For example, if a child component defines template_file, the parent's template or template_file will be ignored.
  • This applies independently to each pair - you can inherit the JS while overriding the template, for instance.

For example:

class BaseCard(Component):
    template = """
        <div class="card">
            <div class="card-content">{{ content }}</div>
        </div>
    """
    css = """
        .card {
            border: 1px solid gray;
        }
    """
    js = "console.log('Base card loaded');"

# This class overrides parent's template, but inherits CSS and JS
class SpecialCard(BaseCard):
    template = """
        <div class="card special">
            <div class="card-content">✨ {{ content }} ✨</div>
        </div>
    """

# This class overrides parent's template and CSS, but inherits JS
class CustomCard(BaseCard):
    template_file = "custom_card.html"
    css = """
        .card {
            border: 2px solid gold;
        }
    """

Media Class Inheritance¤

The Component.Media nested class follows Django's media inheritance rules:

  • If both parent and child define a Media class, the child's media will automatically include both its own and the parent's JS and CSS files.
  • This behavior can be configured using the extend attribute in the Media class, similar to Django's forms. Read more on this in Controlling Media Inheritance.

For example:

class BaseModal(Component):
    template = "<div>Modal content</div>"

    class Media:
        css = ["base_modal.css"]
        js = ["base_modal.js"]  # Contains core modal functionality

class FancyModal(BaseModal):
    class Media:
        # Will include both base_modal.css/js AND fancy_modal.css/js
        css = ["fancy_modal.css"]  # Additional styling
        js = ["fancy_modal.js"]    # Additional animations

class SimpleModal(BaseModal):
    class Media:
        extend = False  # Don't inherit parent's media
        css = ["simple_modal.css"]  # Only this CSS will be included
        js = ["simple_modal.js"]    # Only this JS will be included

Regular Python Inheritance¤

All other attributes and methods (including the Component.View class and its methods) follow standard Python inheritance rules.

For example:

class BaseForm(Component):
    template = """
        <form>
            {{ form_content }}
            <button type="submit">
                {{ submit_text }}
            </button>
        </form>
    """

    def get_context_data(self, **kwargs):
        return {
            "form_content": self.get_form_content(),
            "submit_text": "Submit"
        }

    def get_form_content(self):
        return "<input type='text' name='data'>"

class ContactForm(BaseForm):
    # Extend parent's "context"
    # but override "submit_text"
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["submit_text"] = "Send Message"  
        return context

    # Completely override parent's get_form_content
    def get_form_content(self):
        return """
            <input type='text' name='name' placeholder='Your Name'>
            <input type='email' name='email' placeholder='Your Email'>
            <textarea name='message' placeholder='Your Message'></textarea>
        """