diff --git a/src/docs/.gitignore b/src/docs/.gitignore
deleted file mode 100644
index 10e83c68..00000000
--- a/src/docs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-reference/
\ No newline at end of file
diff --git a/src/docs/reference/api.md b/src/docs/reference/api.md
new file mode 100644
index 00000000..6a6d7e47
--- /dev/null
+++ b/src/docs/reference/api.md
@@ -0,0 +1,105 @@
+
+
+# API
+
+
+::: django_components.Component
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ComponentFileEntry
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ComponentRegistry
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ComponentVars
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ComponentView
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ComponentsSettings
+ options:
+ show_if_no_docstring: true
+
+::: django_components.ContextBehavior
+ options:
+ show_if_no_docstring: true
+
+::: django_components.EmptyDict
+ options:
+ show_if_no_docstring: true
+
+::: django_components.EmptyTuple
+ options:
+ show_if_no_docstring: true
+
+::: django_components.RegistrySettings
+ options:
+ show_if_no_docstring: true
+
+::: django_components.Slot
+ options:
+ show_if_no_docstring: true
+
+::: django_components.SlotContent
+ options:
+ show_if_no_docstring: true
+
+::: django_components.SlotFunc
+ options:
+ show_if_no_docstring: true
+
+::: django_components.SlotRef
+ options:
+ show_if_no_docstring: true
+
+::: django_components.SlotResult
+ options:
+ show_if_no_docstring: true
+
+::: django_components.TagFormatterABC
+ options:
+ show_if_no_docstring: true
+
+::: django_components.TagResult
+ options:
+ show_if_no_docstring: true
+
+::: django_components.autodiscover
+ options:
+ show_if_no_docstring: true
+
+::: django_components.cached_template
+ options:
+ show_if_no_docstring: true
+
+::: django_components.get_component_dirs
+ options:
+ show_if_no_docstring: true
+
+::: django_components.get_component_files
+ options:
+ show_if_no_docstring: true
+
+::: django_components.import_libraries
+ options:
+ show_if_no_docstring: true
+
+::: django_components.register
+ options:
+ show_if_no_docstring: true
+
+::: django_components.registry
+ options:
+ show_if_no_docstring: true
+
+::: django_components.render_dependencies
+ options:
+ show_if_no_docstring: true
+
diff --git a/src/docs/reference/commands.md b/src/docs/reference/commands.md
new file mode 100644
index 00000000..73979ece
--- /dev/null
+++ b/src/docs/reference/commands.md
@@ -0,0 +1,166 @@
+
+
+# Commands
+
+These are all the [Django management commands](https://docs.djangoproject.com/en/5.1/ref/django-admin)
+that will be added by installing `django_components`:
+
+
+## `upgradecomponent`
+
+```txt
+usage: manage.py upgradecomponent [-h] [--path PATH] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color]
+ [--force-color] [--skip-checks]
+
+```
+
+
+
+See source code
+
+
+
+Updates component and component_block tags to the new syntax
+
+**Options:**
+
+- `-h`, `--help`
+ - show this help message and exit
+- `--path PATH`
+ - Path to search for components
+- `--version`
+ - Show program's version number and exit.
+- `-v {0,1,2,3}`, `--verbosity {0,1,2,3}`
+ - Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output
+- `--settings SETTINGS`
+ - The Python path to a settings module, e.g. "myproject.settings.main". If this isn't provided, the DJANGO_SETTINGS_MODULE environment variable will be used.
+- `--pythonpath PYTHONPATH`
+ - A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".
+- `--traceback`
+ - Raise on CommandError exceptions.
+- `--no-color`
+ - Don't colorize the command output.
+- `--force-color`
+ - Force colorization of the command output.
+- `--skip-checks`
+ - Skip system checks.
+
+
+
+
+
+## `startcomponent`
+
+```txt
+usage: manage.py startcomponent [-h] [--path PATH] [--js JS] [--css CSS] [--template TEMPLATE] [--force] [--verbose] [--dry-run] [--version] [-v {0,1,2,3}]
+ [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color] [--skip-checks]
+ name
+
+```
+
+
+
+See source code
+
+
+
+Create a new django component.
+
+**Positional Arguments:**
+
+- `name`
+ - The name of the component to create. This is a required argument.
+
+**Options:**
+
+- `-h`, `--help`
+ - show this help message and exit
+- `--path PATH`
+ - The path to the component's directory. This is an optional argument. If not provided, the command will use the `COMPONENTS.dirs` setting from your Django settings.
+- `--js JS`
+ - The name of the JavaScript file. This is an optional argument. The default value is `script.js`.
+- `--css CSS`
+ - The name of the CSS file. This is an optional argument. The default value is `style.css`.
+- `--template TEMPLATE`
+ - The name of the template file. This is an optional argument. The default value is `template.html`.
+- `--force`
+ - This option allows you to overwrite existing files if they exist. This is an optional argument.
+- `--verbose`
+ - This option allows the command to print additional information during component creation. This is an optional argument.
+- `--dry-run`
+ - This option allows you to simulate component creation without actually creating any files. This is an optional argument. The default value is `False`.
+- `--version`
+ - Show program's version number and exit.
+- `-v {0,1,2,3}`, `--verbosity {0,1,2,3}`
+ - Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output
+- `--settings SETTINGS`
+ - The Python path to a settings module, e.g. "myproject.settings.main". If this isn't provided, the DJANGO_SETTINGS_MODULE environment variable will be used.
+- `--pythonpath PYTHONPATH`
+ - A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".
+- `--traceback`
+ - Raise on CommandError exceptions.
+- `--no-color`
+ - Don't colorize the command output.
+- `--force-color`
+ - Force colorization of the command output.
+- `--skip-checks`
+ - Skip system checks.
+
+
+
+
+### Management Command Usage
+
+To use the command, run the following command in your terminal:
+
+```bash
+python manage.py startcomponent --path --js --css --template --force --verbose --dry-run
+```
+
+Replace ``, ``, ``, ``, and `` with your desired values.
+
+### Management Command Examples
+
+Here are some examples of how you can use the command:
+
+#### Creating a Component with Default Settings
+
+To create a component with the default settings, you only need to provide the name of the component:
+
+```bash
+python manage.py startcomponent my_component
+```
+
+This will create a new component named `my_component` in the `components` directory of your Django project. The JavaScript, CSS, and template files will be named `script.js`, `style.css`, and `template.html`, respectively.
+
+#### Creating a Component with Custom Settings
+
+You can also create a component with custom settings by providing additional arguments:
+
+```bash
+python manage.py startcomponent new_component --path my_components --js my_script.js --css my_style.css --template my_template.html
+```
+
+This will create a new component named `new_component` in the `my_components` directory. The JavaScript, CSS, and template files will be named `my_script.js`, `my_style.css`, and `my_template.html`, respectively.
+
+#### Overwriting an Existing Component
+
+If you want to overwrite an existing component, you can use the `--force` option:
+
+```bash
+python manage.py startcomponent my_component --force
+```
+
+This will overwrite the existing `my_component` if it exists.
+
+#### Simulating Component Creation
+
+If you want to simulate the creation of a component without actually creating any files, you can use the `--dry-run` option:
+
+```bash
+python manage.py startcomponent my_component --dry-run
+```
+
+This will simulate the creation of `my_component` without creating any files.
+
+
diff --git a/src/docs/reference/components.md b/src/docs/reference/components.md
new file mode 100644
index 00000000..bba6868b
--- /dev/null
+++ b/src/docs/reference/components.md
@@ -0,0 +1,15 @@
+
+
+# Components
+
+These are the components provided by django_components.
+
+
+::: django_components.components.dynamic.DynamicComponent
+ options:
+ inherited_members: false
+ show_root_heading: true
+ show_signature: false
+ separate_signature: false
+ members: false
+
diff --git a/src/docs/reference/exceptions.md b/src/docs/reference/exceptions.md
new file mode 100644
index 00000000..78c2ac43
--- /dev/null
+++ b/src/docs/reference/exceptions.md
@@ -0,0 +1,17 @@
+
+
+# Exceptions
+
+
+::: django_components.AlreadyRegistered
+ options:
+ show_if_no_docstring: true
+
+::: django_components.NotRegistered
+ options:
+ show_if_no_docstring: true
+
+::: django_components.TagProtectedError
+ options:
+ show_if_no_docstring: true
+
diff --git a/src/docs/reference/middlewares.md b/src/docs/reference/middlewares.md
new file mode 100644
index 00000000..08c2b63b
--- /dev/null
+++ b/src/docs/reference/middlewares.md
@@ -0,0 +1,16 @@
+
+
+# Middlewares
+
+
+::: django_components.dependencies.ComponentDependencyMiddleware
+ options:
+ inherited_members: false
+ show_root_heading: true
+ show_signature: false
+ separate_signature: false
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
diff --git a/src/docs/reference/settings.md b/src/docs/reference/settings.md
new file mode 100644
index 00000000..8dfa16a9
--- /dev/null
+++ b/src/docs/reference/settings.md
@@ -0,0 +1,205 @@
+
+
+# Settings
+
+You can configure django_components with a global `COMPONENTS` variable in your Django settings file, e.g. `settings.py`.
+By default you don't need it set, there are resonable [defaults](#settings-defaults).
+
+To configure the settings you can instantiate [`ComponentsSettings`](../api#django_components.ComponentsSettings)
+for validation and type hints. Or, for backwards compatibility, you can also use plain dictionary:
+
+```python
+# settings.py
+from django_components import ComponentsSettings
+
+COMPONENTS = ComponentsSettings(
+ autodiscover=True,
+ ...
+)
+
+# or
+
+COMPONENTS = {
+ "autodiscover": True,
+ ...
+}
+```
+
+
+### Settings defaults
+
+Here's overview of all available settings and their defaults:
+
+```py
+defaults = ComponentsSettings(
+ autodiscover=True,
+ context_behavior=ContextBehavior.DJANGO.value, # "django" | "isolated"
+ # Root-level "components" dirs, e.g. `/path/to/proj/components/`
+ dirs=[Path(settings.BASE_DIR) / "components"],
+ # App-level "components" dirs, e.g. `[app]/components/`
+ app_dirs=["components"],
+ dynamic_component_name="dynamic",
+ libraries=[], # E.g. ["mysite.components.forms", ...]
+ multiline_tags=True,
+ reload_on_file_change=False,
+ static_files_allowed=[
+ ".css",
+ ".js", ".jsx", ".ts", ".tsx",
+ # Images
+ ".apng", ".png", ".avif", ".gif", ".jpg",
+ ".jpeg", ".jfif", ".pjpeg", ".pjp", ".svg",
+ ".webp", ".bmp", ".ico", ".cur", ".tif", ".tiff",
+ # Fonts
+ ".eot", ".ttf", ".woff", ".otf", ".svg",
+ ],
+ static_files_forbidden=[
+ # See https://marketplace.visualstudio.com/items?itemName=junstyle.vscode-django-support
+ ".html", ".django", ".dj", ".tpl",
+ # Python files
+ ".py", ".pyc",
+ ],
+ tag_formatter="django_components.component_formatter",
+ template_cache_size=128,
+)
+```
+
+::: django_components.app_settings.ComponentsSettings.app_dirs
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.autodiscover
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.context_behavior
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.dirs
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.dynamic_component_name
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.forbidden_static_files
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.libraries
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.multiline_tags
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.reload_on_file_change
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.reload_on_template_change
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.static_files_allowed
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.static_files_forbidden
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.tag_formatter
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
+::: django_components.app_settings.ComponentsSettings.template_cache_size
+ options:
+ show_root_heading: true
+ show_signature: true
+ separate_signature: true
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+
diff --git a/src/docs/reference/tag_formatters.md b/src/docs/reference/tag_formatters.md
new file mode 100644
index 00000000..9cdc2b16
--- /dev/null
+++ b/src/docs/reference/tag_formatters.md
@@ -0,0 +1,40 @@
+
+
+# Tag Formatters
+
+[Tag formatters](../../concepts/advanced/tag_formatter) allow you to change the syntax for calling components from within
+the Django templates.
+
+Tag formatter are set via the [tag_formatter](../settings#django_components.app_settings.ComponentsSettings.tag_formatter)
+setting.
+
+
+### Available tag formatters
+
+- `django_components.component_formatter` for [ComponentFormatter](#django_components.tag_formatter.ComponentFormatter)
+
+- `django_components.component_shorthand_formatter` for [ShorthandComponentFormatter](#django_components.tag_formatter.ShorthandComponentFormatter)
+::: django_components.tag_formatter.ComponentFormatter
+ options:
+ inherited_members: false
+ show_root_heading: true
+ show_signature: false
+ separate_signature: false
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+ members: false
+
+::: django_components.tag_formatter.ShorthandComponentFormatter
+ options:
+ inherited_members: false
+ show_root_heading: true
+ show_signature: false
+ separate_signature: false
+ show_symbol_type_heading: false
+ show_symbol_type_toc: false
+ show_if_no_docstring: true
+ show_labels: false
+ members: false
+
diff --git a/src/docs/reference/template_tags.md b/src/docs/reference/template_tags.md
new file mode 100644
index 00000000..933624f7
--- /dev/null
+++ b/src/docs/reference/template_tags.md
@@ -0,0 +1,543 @@
+
+
+# Template tags
+
+
+All following template tags are defined in
+
+`django_components.templatetags.component_tags`
+
+Import as
+```django
+{% load component_tags %}
+```
+
+## component
+
+```django
+{% component [arg, ...] **kwargs [only] %}
+{% endcomponent %}
+```
+
+
+
+See source code
+
+
+
+Renders one of the components that was previously registered with
+[`@register()`](./api.md#django_components.register)
+decorator.
+
+**Args:**
+
+- `name` (str, required): Registered name of the component to render
+- All other args and kwargs are defined based on the component itself.
+
+If you defined a component `"my_table"`
+
+```python
+from django_component import Component, register
+
+@register("my_table")
+class MyTable(Component):
+ template = """
+
+
+ {% for header in headers %}
+ {{ header }} |
+ {% endfor %}
+
+
+ {% for row in rows %}
+
+ {% for cell in row %}
+ {{ cell }} |
+ {% endfor %}
+
+ {% endfor %}
+
+
+ """
+
+ def get_context_data(self, rows: List, headers: List):
+ return {
+ "rows": rows,
+ "headers": headers,
+ }
+```
+
+Then you can render this component by referring to `MyTable` via its
+registered name `"my_table"`:
+
+```django
+{% component "my_table" rows=rows headers=headers ... / %}
+```
+
+### Component input
+
+Positional and keyword arguments can be literals or template variables.
+
+The component name must be a single- or double-quotes string and must
+be either:
+
+- The first positional argument after `component`:
+
+ ```django
+ {% component "my_table" rows=rows headers=headers ... / %}
+ ```
+
+- Passed as kwarg `name`:
+
+ ```django
+ {% component rows=rows headers=headers name="my_table" ... / %}
+ ```
+
+### Inserting into slots
+
+If the component defined any [slots](../concepts/fundamentals/slots.md), you can
+pass in the content to be placed inside those slots by inserting [`{% fill %}`](#fill) tags,
+directly within the `{% component %}` tag:
+
+```django
+{% component "my_table" rows=rows headers=headers ... / %}
+ {% fill "pagination" %}
+ < 1 | 2 | 3 >
+ {% endfill %}
+{% endcomponent %}
+```
+
+### Isolating components
+
+By default, components behave similarly to Django's
+[`{% include %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#include),
+and the template inside the component has access to the variables defined in the outer template.
+
+You can selectively isolate a component, using the `only` flag, so that the inner template
+can access only the data that was explicitly passed to it:
+
+```django
+{% component "name" positional_arg keyword_arg=value ... only %}
+```
+
+## component_css_dependencies
+
+```django
+{% component_css_dependencies %}
+```
+
+
+
+See source code
+
+
+
+Marks location where CSS link tags should be rendered after the whole HTML has been generated.
+
+Generally, this should be inserted into the `` tag of the HTML.
+
+If the generated HTML does NOT contain any `{% component_css_dependencies %}` tags, CSS links
+are by default inserted into the `` tag of the HTML. (See
+[JS and CSS output locations](../../concepts/advanced/rendering_js_css/#js-and-css-output-locations))
+
+Note that there should be only one `{% component_css_dependencies %}` for the whole HTML document.
+If you insert this tag multiple times, ALL CSS links will be duplicately inserted into ALL these places.
+
+## component_js_dependencies
+
+```django
+{% component_js_dependencies %}
+```
+
+
+
+See source code
+
+
+
+Marks location where JS link tags should be rendered after the whole HTML has been generated.
+
+Generally, this should be inserted at the end of the `` tag of the HTML.
+
+If the generated HTML does NOT contain any `{% component_js_dependencies %}` tags, JS scripts
+are by default inserted at the end of the `` tag of the HTML. (See
+[JS and CSS output locations](../../concepts/advanced/rendering_js_css/#js-and-css-output-locations))
+
+Note that there should be only one `{% component_js_dependencies %}` for the whole HTML document.
+If you insert this tag multiple times, ALL JS scripts will be duplicately inserted into ALL these places.
+
+## fill
+
+```django
+{% fill name data=None default=None %}
+{% endfill %}
+```
+
+
+
+See source code
+
+
+
+Use this tag to insert content into component's slots.
+
+`{% fill %}` tag may be used only within a `{% component %}..{% endcomponent %}` block.
+Runtime checks should prohibit other usages.
+
+**Args:**
+
+- `name` (str, required): Name of the slot to insert this content into. Use `"default"` for
+ the default slot.
+- `default` (str, optional): This argument allows you to access the original content of the slot
+ under the specified variable name. See
+ [Accessing original content of slots](../../concepts/fundamentals/slots#accessing-original-content-of-slots)
+- `data` (str, optional): This argument allows you to access the data passed to the slot
+ under the specified variable name. See [Scoped slots](../../concepts/fundamentals/slots#scoped-slots)
+
+**Examples:**
+
+Basic usage:
+```django
+{% component "my_table" %}
+ {% fill "pagination" %}
+ < 1 | 2 | 3 >
+ {% endfill %}
+{% endcomponent %}
+```
+
+### Accessing slot's default content with the `default` kwarg
+
+```django
+{# my_table.html #}
+
+ ...
+ {% slot "pagination" %}
+ < 1 | 2 | 3 >
+ {% endslot %}
+
+```
+
+```django
+{% component "my_table" %}
+ {% fill "pagination" default="default_pag" %}
+
+ {{ default_pag }}
+
+ {% endfill %}
+{% endcomponent %}
+```
+
+### Accessing slot's data with the `data` kwarg
+
+```django
+{# my_table.html #}
+
+ ...
+ {% slot "pagination" pages=pages %}
+ < 1 | 2 | 3 >
+ {% endslot %}
+
+```
+
+```django
+{% component "my_table" %}
+ {% fill "pagination" data="slot_data" %}
+ {% for page in slot_data.pages %}
+
+ {{ page.index }}
+
+ {% endfor %}
+ {% endfill %}
+{% endcomponent %}
+```
+
+### Accessing slot data and default content on the default slot
+
+To access slot data and the default slot content on the default slot,
+use `{% fill %}` with `name` set to `"default"`:
+
+```django
+{% component "button" %}
+ {% fill name="default" data="slot_data" default="default_slot" %}
+ You clicked me {{ slot_data.count }} times!
+ {{ default_slot }}
+ {% endfill %}
+{% endcomponent %}
+```
+
+## html_attrs
+
+```django
+{% html_attrs attrs=None defaults=None **kwargs %}
+```
+
+
+
+See source code
+
+
+
+Generate HTML attributes (`key="value"`), combining data from multiple sources,
+whether its template variables or static text.
+
+It is designed to easily merge HTML attributes passed from outside with the internal.
+See how to in [Passing HTML attributes to components](../../guides/howto/passing_html_attrs/).
+
+**Args:**
+
+- `attrs` (dict, optional): Optional dictionary that holds HTML attributes. On conflict, overrides
+ values in the `default` dictionary.
+- `default` (str, optional): Optional dictionary that holds HTML attributes. On conflict, is overriden
+ with values in the `attrs` dictionary.
+- Any extra kwargs will be appended to the corresponding keys
+
+The attributes in `attrs` and `defaults` are merged and resulting dict is rendered as HTML attributes
+(`key="value"`).
+
+Extra kwargs (`key=value`) are concatenated to existing keys. So if we have
+
+```python
+attrs = {"class": "my-class"}
+```
+
+Then
+
+```django
+{% html_attrs attrs class="extra-class" %}
+```
+
+will result in `class="my-class extra-class"`.
+
+**Example:**
+```django
+