---
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 #}
```
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") %}
```
...