--- title: Components description: Declaring and using components. ---
## Declaring and Using Components The components are simple text files that look like regular Jinja templates, with three requirements: **First**, components must be placed inside a folder registered in the catalog or a subfolder of it. ```python catalog.add_folder("myapp/components") ``` You can call that folder whatever you want, not just "components". You can also add more than one folder: ```python catalog.add_folder("myapp/layouts") catalog.add_folder("myapp/components") ``` If you end up having more than one component with the same name, the one in the first folder will take priority. **Second**, they must have a ".jinja" extension. This also helps code editors automatically select the correct language syntax to highlight. However, you can configure it in the catalog. **Third**, the component name must start with an uppercase letter. Why? This is how JinjaX differentiates a component from a regular HTML tag when using it. I recommend using PascalCase names, like Python classes. The name of the file (minus the extension) is also how you call the component. For example, if the file is "components/PersonForm.jinja": ``` └ myapp/ ├── app.py ├── components/ └─ PersonForm.jinja ``` The name of the component is "PersonForm" and can be called like this: From Python code or a non-component template: - `catalog.render("PersonForm")` From another component: - ` some content `, or - `` If the component is in a subfolder, the name of that folder becomes part of its name too: ``` └ myapp/ ├── app.py ├── components/ └─ person └─ PersonForm.jinja ``` A "components/person/PersonForm.jinja" component is named "person.PersonForm", meaning the name of the subfolder and the name of the file separated by a dot. This is the full name you use to call it: From Python code or a non-component template: - `catalog.render("person.PersonForm")` From another component: - ` some content `, or - `` Notice how the folder name doesn't need to start with an uppercase if you don't want it to. ## Taking Arguments More often than not, a component takes one or more arguments to render. Every argument must be declared at the beginning of the component with `{#def arg1, arg2, ... #}`. ```html+jinja {#def action, method="post", multipart=False #}
{{ content }}
``` In this example, the component takes three arguments: "action", "method", and "multipart". The last two have default values, so they are optional, but the first one doesn't. That means it must be passed a value when rendering the component. The syntax is exactly like how you declare the arguments of a Python function (in fact, it's parsed by the same code), so it can even include type comments, although they are not used by JinjaX (yet!). ```python {#def data: dict[str, str], method: str = "post", multipart: bool = False #} ... ``` ## Passing Arguments There are two types of arguments: strings and expressions. ### String Strings are passed like regular HTML attributes: ```html+jinja
...
... ``` ### Expressions There are two different but equivalent ways to pass non-string arguments: "Jinja-like", where you use double curly braces instead of quotes: ```html+jinja title="Jinja-like" ``` ... and "Vue-like", where you keep using quotes, but prefix the name of the attribute with a colon: ```html+jinja title="Vue-like" ``` For `True` values, you can just use the name, like in HTML:
```html+jinja
You can also use dashes when passing an argument, but they will be translated to underscores:
```html+jinja ```
```html+jinja title="Example.jinja" {#def aria_label = "" #} ... ```
## With Content There is always an extra implicit argument: **the content** inside the component. Read more about it in the [next](/guide/slots) section. ## Extra Arguments If you pass arguments not declared in a component, those are not discarded but rather collected in an `attrs` object. You then call `attrs.render()` to render the received arguments as HTML attributes. For example, this component: ```html+jinja title="Card.jinja" {#def title #}

{{ title }}

{{ content }}
``` Called as: ```html bla ``` Will be rendered as: ```html

Products

bla
``` You can add or remove arguments before rendering them using the other methods of the `attrs` object. For example: ```html+jinja {#def title #} {% do attrs.set(id="mycard") -%}

{{ title }}

{{ content }}
``` Or directly in the `attrs.render()` call: ```html+jinja {#def title #}

{{ title }}

{{ content }}
``` The string values passed into components as attrs are not cast to `str` until the string representation is **actually** needed, for example when `attrs.render()` is invoked. ### `attrs` Methods #### `.render(name=value, ...)` Renders the attributes and properties as a string. Any arguments you use with this function are merged with the existing attibutes/properties by the same rules as the `HTMLAttrs.set()` function: - Pass a name and a value to set an attribute (e.g. `type="text"`) - Use `True` as a value to set a property (e.g. `disabled`) - Use `False` to remove an attribute or property - The existing attribute/property is overwritten **except** if it is `class`. The new classes are appended to the old ones instead of replacing them. - The underscores in the names will be translated automatically to dashes, so `aria_selected` becomes the attribute `aria-selected`. To provide consistent output, the attributes and properties are sorted by name and rendered like this: ` + `. ```html+jinja ``` ```html+jinja
``` Using `` to pass the extra arguments to other components **WILL NOT WORK**. That is because the components are translated to macros before the page render. You must pass them as the special argument `_attrs`. ```html+jinja {#--- WRONG 😵 ---#} {#--- GOOD 👍 ---#} ``` #### `.set(name=value, ...)` Sets an attribute or property - Pass a name and a value to set an attribute (e.g. `type="text"`) - Use `True` as a value to set a property (e.g. `disabled`) - Use `False` to remove an attribute or property - If the attribute is "class", the new classes are appended to the old ones (if not repeated) instead of replacing them. - The underscores in the names will be translated automatically to dashes, so `aria_selected` becomes the attribute `aria-selected`. ```html+jinja title="Adding attributes/properties" {% do attrs.set( id="loremipsum", disabled=True, data_test="foobar", class="m-2 p-4", ) %} ``` ```html+jinja title="Removing attributes/properties" {% do attrs.set( title=False, disabled=False, data_test=False, class=False, ) %} ``` #### `.setdefault(name=value, ...)` Adds an attribute, but only if it's not already present. The underscores in the names will be translated automatically to dashes, so `aria_selected` becomes the attribute `aria-selected`. ```html+jinja {% do attrs.setdefault( aria_label="Products" ) %} ``` #### `.add_class(name1, name2, ...)` Adds one or more classes to the list of classes, if not already present. ```html+jinja {% do attrs.add_class("hidden") %} {% do attrs.add_class("active", "animated") %} ``` #### `.remove_class(name1, name2, ...)` Removes one or more classes from the list of classes. ```html+jinja {% do attrs.remove_class("hidden") %} {% do attrs.remove_class("active", "animated") %} ``` #### `.get(name, default=None)` Returns the value of the attribute or property, or the default value if it doesn't exist. ```html+jinja {%- set role = attrs.get("role", "tab") %} ``` ...