Organizing Components¶
As components grow in complexity, effective organization becomes crucial. Let’s explore approaches, starting from a basic implementation to more sophisticated structures.
Let’s start with a simple icon component and evolve its organization. We’ll create a component using the Heroicons library.
Note
This is for demonstration purposes. For real-world applications using Heroicons, consider using Adam Johnson’s heroicon Python package.
Basic Approach: Single File¶
Let’s start by including all component variations in a single file:
{% bird:prop name %}
{% if props.name == "arrow-down" %}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3" />
</svg>
{% elif props.name == "arrow-left" %}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>
{% elif props.name == "arrow-right" %}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>
{% elif props.name == "arrow-up" %}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" />
</svg>
{% endif %}
In this example, the icon variant is a passed-in attribute. Using this component looks like this:
{% bird icon name="arrow-down" / %}
Tip
The / at the end of the component definition means it’s a self-closing component. You don’t need a {% endbird %} closing tag and can use it for components that don’t need to provide any slots for content.
This approach is simple and works for smaller components, but can quickly lead to an unwieldy file as the number of variations increases. Additionally, this does not allow composing different components together.
Alternative Approaches¶
Let’s explore scalable ways to organize and structure components.
Flat Structure
Split components into separate files in the same directory. This approach is simple to implement and navigate for a moderate number of components.
templates/bird/ ├── icon.arrow-down.html ├── icon.arrow-left.html ├── icon.arrow-right.html └── icon.arrow-up.html
Bird components take the component name from the filename. Using components this way changes the syntax slightly:
{% bird icon.arrow-down / %}
Dedicated Directories
templates/bird/ └── icon/ ├── arrow-down.html ├── arrow-left.html ├── arrow-right.html └── arrow-up.html
This allows better organization as the component count grows, making it easier to locate related components.
django-bird converts the directory divider into a
., so our usage remains unchanged from the flat structure:{% bird icon.arrow-down / %}
Deeply Nested Directory Structure
You can nest the components as deep as required for more granular organization.
templates/bird/ └── icon/ └── arrow/ ├── down.html ├── left.html ├── right.html └── up.html
This structure allows for specific categorization, which is useful for large projects with many related component variations.
Converting the directory structure to component names would change our usage:
{% bird icon.arrow.down / %}
Real-World Example: Accordion¶
Let’s examine a complex, real-world example: an accordion component. This component consists of multiple nested parts, demonstrating how our organizational approaches apply to sophisticated structures.
Here’s an example of using our accordion component, based on an MDN example:
{% bird accordion %}
{% bird accordion.item name="reqs" %}
{% bird accordion.heading %}
Graduation Requirements
{% endbird %}
{% bird accordion.content %}
Requires 40 credits, including a passing grade in health, geography,
history, economics, and wood shop.
{% endbird %}
{% endbird %}
{% bird accordion.item name="reqs" %}
{% bird accordion.heading %}
System Requirements
{% endbird %}
{% bird accordion.content %}
Requires a computer running an operating system. The computer must have some
memory and ideally some kind of long-term storage. An input device as well
as some form of output device is recommended.
{% endbird %}
{% endbird %}
{% bird accordion.item name="reqs" %}
{% bird accordion.heading %}
Job Requirements
{% endbird %}
{% bird accordion.content %}
Requires knowledge of HTML, CSS, JavaScript, accessibility, web performance,
privacy, security, and internationalization, as well as a dislike of
broccoli.
{% endbird %}
{% endbird %}
{% endbird %}
Here’s how these components might be implemented internally:
accordion¶<div>
{{ slot }}
</div>
accordion.item¶<details {{ attrs }}>
{{ slot }}
</details>
accordion.heading¶<summary>
{{ slot }}
</summary>
accordion.content¶<p>
{{ slot }}
</p>
These internal implementations show the accordion’s structure, with slots for content.
Let’s explore different ways to organize the accordion component files:
In the base of the bird components directory:
templates/bird/ ├── accordion.html ├── accordion.heading.html ├── accordion.item.html └── accordion.content.html
This approach keeps all accordion-related components in the same directory as other components.
With a dedicated accordion directory:
templates/bird/ └── accordion/ ├── accordion.html ├── heading.html ├── item.html └── content.html
We group all accordion components in a dedicated directory.
When using
{% bird accordion %}, the library will look for components in the following order:accordion/accordion.htmlaccordion/index.htmlaccordion.html
This search order allows for more organizational flexibility.
For example, you can structure your files like this:
templates/bird/ └── accordion/ ├── heading.html ├── index.html # primary accordion component ├── item.html └── content.html
Or like this:
templates/bird/ ├── accordion │ ├── heading.html │ ├── item.html │ └── content.html └── accordion.html
This flexibility in naming and organization allows you to use
accordion.htmlfor explicit naming,index.htmlas a generic entry point, or use a flat structure if neither exists in the accordion directory.The component usage in your templates remains the same, regardless of the structure. This approach provides options for organizing components based on project needs and team preferences while maintaining consistent usage patterns.
Choosing the Right Approach¶
Each organizational method offers different trade-offs between simplicity and structure.
The choice depends on various factors:
Project size and complexity
Team size and preferences
Future scalability needs
Maintenance ease
These approaches aren’t mutually exclusive. Larger projects might benefit from using a combination of these methods, applying different structures to different parts of the application as needed.
As your project evolves, you may benefit from refactoring your component organization. Starting with a simpler structure and moving to more complex ones as needed helps maintain clarity and scalability throughout your project’s lifecycle.