# Passing Parameters to Components django-bird provides two ways to pass parameters to your components: attributes and properties. While attributes and properties may look similar when using a component, they serve different purposes. Attributes are made available to your component template as a flattened string via the `{{ attrs }}` template context variable which can be used to apply HTML attributes to elements, while properties are accessible as individual values (e.g. `{{ props.variant }}`) that your component can use internally to control its rendering logic. Note that these parameters are distinct from [Slots](slots.md) - they are used to configured how your component behaves or renders, while slots define where content should be inserted into your component's template. For example, a button component might use properties to control its styling and attributes to set HTML attributes, while using slots to define its content: ```htmldjango {% bird button variant="primary" data_analytics="signup" %} Click here {# This content will go in the default slot #} {% endbird %} ``` ## Attributes Attributes (i.e. `attrs`) let you pass additional HTML attributes to your components. This feature provides flexibility and customization without modifying the component template. In your component template, the `{{ attrs }}` variable is a special variable that contains all the attributes passed to the component as a pre-rendered string. Unlike props which can be accessed individually, attributes are flattened into a single string ready to be inserted into an HTML element. The `{{ attrs }}` variable automatically handles both key-value attributes (like `class="btn"`) and boolean attributes (like `disabled`). ### Basic Usage Here's a simple example of a button component that accepts attributes: ```{code-block} htmldjango :caption: templates/bird/button.html ``` Use this component and pass attributes like this: ```htmldjango {% bird button class="btn" %} Click me! {% endbird %} ``` It will render as: ```html ``` ### Multiple Attributes You can pass multiple attributes to a component: ```htmldjango {% bird button class="btn btn-primary" id="submit-btn" disabled %} Submit {% endbird %} ``` This will render as: ```html ``` ### Attribute Names When rendering attributes, underscores in attribute names are automatically converted to hyphens. This is particularly useful for data attributes and other hyphenated HTML attributes: ```htmldjango {% bird button data_analytics="signup" hx_get="/api/data" %} Load Data {% endbird %} ``` This will render as: ```html ``` ### Component ID Attribute When the [`ENABLE_BIRD_ATTRS` setting](configuration.md#enable_bird_attrs) is enabled (the default), django-bird automatically adds data attributes to your components, available via the `{{ attrs }}` context variable. The following attributes are included: - `data-bird-`: This attribute will contain the name of the component. This is not unique across component instances in the DOM. - `data-bird-id`: This attribute contains a unique identifier that helps identify specific component instances in the DOM. For example, for a component template like this: ```htmldjango ``` And used like this: ```htmldjango {% bird button class="btn" %} Click me {% endbird %} ``` It will be rendered as: ```html ``` The ID is automatically generated from a hash of the component's name and template content. It also contains a sequence counter that will increment for any uses of a component across a single template. The above example button component template, used like this: ```htmldjango {% bird button class="btn" %} Click me once {% endbird %} {% bird button class="btn" %} Click me twice {% endbird %} {% bird button class="btn" %} Click me three times a lady {% endbird %} ``` Will be rendered like this, with the unique sequence numbers added to the component's hashed ID: ```html ``` You can disable this feature globally by setting `ENABLE_BIRD_ATTRS = False` in your Django settings: ```python DJANGO_BIRD = { "ENABLE_BIRD_ATTRS": False, } ``` When disabled, no data attributes will be added to your components' `attrs` template context variable. ## Properties Properties (i.e. `props`) allow you to define parameters that your component expects, with optional default values. Unlike attributes which are provided as a flattened string via `{{ attrs }}`, props are processed by the component and made available as individual values (e.g. `{{ props.variant }}`) that can be used to control rendering logic. In your component template, props are defined using the `{% bird:prop %}` tag and accessed via the `{{ props }}` context variable. You can define as many props as needed using separate `{% bird:prop %}` tags. When a prop is defined, any matching attribute passed to the component will be removed from `{{ attrs }}` and made available in `{{ props }}` instead. ### Basic Usage Here's a simple example of a button component that uses props: ```{code-block} htmldjango :caption: templates/bird/button.html {% bird:prop variant='primary' %} ``` Use this component and override the default variant like this: ```htmldjango {% bird button variant="secondary" id="secondary-button" %} Click me! {% endbird %} ``` It will render as: ```html ``` Notice how this works: - The `variant` attribute is removed from `attrs` because it matches a defined prop - Its value "secondary" is made available as `props.variant` - The `id` attribute remains in `attrs` since it's not defined as a prop - The final HTML only includes `variant`'s value as part of the class name, while `id` appears as a direct attribute This separation allows you to use props to control your component's logic while still accepting arbitrary HTML attributes. ### Multiple Props Components often need multiple props to control different aspects of their behavior. Each prop is defined with its own `{% bird:prop %}` tag: ```{code-block} htmldjango :caption: templates/bird/button.html {% bird:prop variant='primary' %} {% bird:prop size='md' %} ``` Use the component by setting any combination of these props: ```htmldjango {% bird button variant="secondary" size="lg" disabled=True %} Click me! {% endbird %} ``` It will render as: ```html ``` This approach of using separate tags for each prop makes it easier to expand the prop system in the future - for example, adding features like type validation or choice constraints while maintaining a clean syntax. ### Props with Defaults Props can be defined with or without default values: ```htmldjango {% bird:prop id %} {# No default value #} {% bird:prop variant='primary' %} {# With default value #} ``` When used, props will take their value from either the passed attribute or fall back to their default: ```htmldjango {% bird button variant="secondary" %} Submit {% endbird %} ``` This will render as: ```html ``` ```{note} Props defined without a default value will render as an empty string if no value is provided when using the component. This behavior may change in a future version to either require default values or handle undefined props differently. ``` ## Value Resolution Both attributes and properties support literal (quoted) and dynamic (unquoted) values. This allows you to either hard-code values or resolve them from the template context. The rules for value resolution are: - Quoted values (`"value"` or `'value'`) are treated as literal strings - Unquoted values are compiled as Django template expressions (including filters) - If a plain variable expression cannot be resolved, the literal value is used as a fallback - Boolean values can be passed directly (`disabled=True`) or as strings (`disabled="True"`) - Both attributes and properties follow these same resolution rules ### Literal Values Using quoted values ensures the exact string is used: ```htmldjango {% bird button class="btn-primary" variant="large" disabled="true" %} Click me {% endbird %} ``` Renders as: ```html ``` ### Dynamic Values Unquoted values are resolved from the template context: ```htmldjango {% bird button class=button_class variant=size disabled=is_disabled %} Click me {% endbird %} ``` With this context: ```python { "button_class": "btn-secondary", "size": "small", "is_disabled": True, } ``` Renders as: ```html ``` ### Filter Expressions You can pass full Django template filter expressions in `bird` tag arguments: ```htmldjango {% bird button badge_count=users|length title=user.name|default:"Anonymous" %} Click me {% endbird %} ``` With this context: ```python { "users": ["Ada", "Grace", "Linus"], "user": {}, } ``` The component receives: - `badge_count = 3` - `title = "Anonymous"` If an unquoted value cannot be resolved from the context, it falls back to using the literal string: ```htmldjango {% bird button class=undefined_class %} Click me {% endbird %} ``` With empty context, renders as: ```html ``` You can also access nested attributes using dot notation: ```htmldjango {% bird button class=theme.button.class disabled=user.is_inactive %} Click me {% endbird %} ``` With this context: ```python { "theme": { "button": { "class": "themed-button", } }, "user": { "is_inactive": True, }, } ``` Renders as: ```html ``` ## Context Isolation By default, components have access to their parent template's context. This means variables defined in the parent template are available inside the component. You can use the `only` keyword to prevent a component from accessing its parent context: ```htmldjango {% bird button only %} Click me {% endbird %} ``` When `only` is used: - The component cannot access variables from the parent context - Props, slots, and other component-specific context still work normally - Default values in the component template will be used when parent context variables are not available You can also make isolation the default globally with [`DEFAULT_ONLY`](configuration.md#default_only): ```python DJANGO_BIRD = { "DEFAULT_ONLY": True, } ``` When `DEFAULT_ONLY=True`, use `inherit` to allow parent context for a specific render: ```htmldjango {% bird button inherit %} {{ user.name }} {% endbird %} ``` ### Examples Without `only`, components can access parent context: ```htmldjango {# Parent template with user in context #} {% bird button %} {{ user.name }} {# Will render "John" #} {% endbird %} ``` With `only`, parent context is isolated: ```htmldjango {# Parent template with user in context #} {% bird button only %} {{ user.name|default:"Anonymous" }} {# Will render "Anonymous" #} {% endbird %} ``` Props and slots still work with `only`: ```htmldjango {% bird button variant="primary" only %} {% bird:slot prefix %}→{% endbird:slot %} Submit {% endbird %} ```