-
The undocumented
Component.component_id
was removed. Instead, useComponent.id
. Changes:- While
component_id
was unique every time you instantiatedComponent
, The newid
is unique every time you render the component (e.g. withComponent.render()
) - The new
id
is available only during render, so e.g. from withinget_context_data()
- While
- Fix edge cases around rendering components whose templates used the
{% extends %}
template tag (#859)
- Add support for HTML fragments. HTML fragments can be rendered by passing
type="fragment"
toComponent.render()
orComponent.render_to_response()
. Read more on how to use HTML fragments with HTMX, AlpineJS, or vanillaJS.
- Fix the use of Django template filters (
|lower:"etc"
) with component inputs #855.
- Fix the use of translation strings
_("bla")
as inputs to components #849.
- Fix compatibility with custom subclasses of Django's
Template
that need to accessorigin
or other initialization arguments. (#828)
-
Compatibility with
django-debug-toolbar-template-profiler
:- Monkeypatching of Django's
Template
now happens atAppConfig.ready()
(#825)
- Monkeypatching of Django's
-
Internal parsing of template tags tag was updated. No API change. (#827)
-
Add support for
context_processors
andRenderContext
inside component templatesComponent.render()
andComponent.render_to_response()
now accept an extra kwargrequest
.def my_view(request) return MyTable.render_to_response( request=request )
-
When you pass in
request
, the component will useRenderContext
instead ofContext
. Thus the context processors will be applied to the context. -
NOTE: When you pass in both
request
andcontext
toComponent.render()
, andcontext
is already an instance ofContext
, therequest
kwarg will be ignored.
-
- The HTML parser no longer erronously inserts
<html><head><body>
on some occasions, and no longer tries to close unclosed HTML tags.
- Replaced Selectolax with BeautifulSoup4 as project dependencies.
-
Fix the order of execution of JS scripts:
- Scripts in
Component.Media.js
are executed in the order they are defined - Scripts in
Component.js
are executed AFTERMedia.js
scripts
- Scripts in
-
Fix compatibility with AlpineJS
- Scripts in
Component.Media.js
are now again inserted as<script>
tags - By default,
Component.Media.js
are inserted as synchronous<script>
tags, so the AlpineJS components registered in theMedia.js
scripts will now again run BEFORE the core AlpineJS script.
AlpineJS can be configured like so:
Option 1 - AlpineJS loaded in
<head>
withdefer
attribute:<html> <head> {% component_css_dependencies %} <script defer src="https://unpkg.com/alpinejs"></script> </head> <body> {% component 'my_alpine_component' / %} {% component_js_dependencies %} </body> </html>
Option 2 - AlpineJS loaded in
<body>
AFTER{% component_js_depenencies %}
:<html> <head> {% component_css_dependencies %} </head> <body> {% component 'my_alpine_component' / %} {% component_js_dependencies %} <script src="https://unpkg.com/alpinejs"></script> </body> </html>
- Scripts in
- Fix integration with ManifestStaticFilesStorage on Windows by resolving component filepaths
(like
Component.template_name
) to POSIX paths.
- Prevent rendering Slot tags during fill discovery stage to fix a case when a component inside a slot fill tried to access provided data too early.
- Ensure consistent order of scripts in
Component.Media.js
- Allow components to accept default fill even if no default slot was encountered during rendering
- Prevent rendering Component tags during fill discovery stage to fix a case when a component inside the default slot tried to access provided data too early.
-
Installation changes:
- If your components include JS or CSS, you now must use the middleware and add django-components' URLs to your
urlpatterns
(See "Adding support for JS and CSS")
- If your components include JS or CSS, you now must use the middleware and add django-components' URLs to your
-
Component typing signature changed from
Component[Args, Kwargs, Data, Slots]
to
Component[Args, Kwargs, Slots, Data, JsData, CssData]
-
If you rendered a component A with
Component.render()
and then inserted that into another component B, now you must passrender_dependencies=False
to component A:prerendered_a = CompA.render( args=[...], kwargs={...}, render_dependencies=False, ) html = CompB.render( kwargs={ content=prerendered_a, }, )
-
Intellisense and mypy validation for settings:
Instead of defining the
COMPONENTS
settings as a plain dict, you can useComponentsSettings
:# settings.py from django_components import ComponentsSettings COMPONENTS = ComponentsSettings( autodiscover=True, ... )
-
Use
get_component_dirs()
andget_component_files()
to get the same list of dirs / files that would be imported byautodiscover()
, but without actually importing them.
-
For advanced use cases, use can omit the middleware and instead manage component JS and CSS dependencies yourself with
render_dependencies
-
The
ComponentRegistry
settingsRegistrySettings
were lowercased to align with the global settings:RegistrySettings.CONTEXT_BEHAVIOR
->RegistrySettings.context_behavior
RegistrySettings.TAG_FORMATTER
->RegistrySettings.tag_formatter
The old uppercase settings
CONTEXT_BEHAVIOR
andTAG_FORMATTER
are deprecated and will be removed in v1. -
The setting
reload_on_template_change
was renamed toreload_on_file_change
. And now it properly triggers server reload when any file in the component dirs change. The old namereload_on_template_change
is deprecated and will be removed in v1. -
The setting
forbidden_static_files
was renamed tostatic_files_forbidden
to align withstatic_files_allowed
The old nameforbidden_static_files
is deprecated and will be removed in v1.
-
{% component_dependencies %}
tag was removed. Instead, use{% component_js_dependencies %}
and{% component_css_dependencies %}
-
The combined tag was removed to encourage the best practice of putting JS scripts at the end of
<body>
, and CSS styles inside<head>
.On the other hand, co-locating JS script and CSS styles can lead to a flash of unstyled content, as either JS scripts will block the rendering, or CSS will load too late.
-
-
The undocumented keyword arg
preload
of{% component_js_dependencies %}
and{% component_css_dependencies %}
tags was removed. This will be replaced with HTML fragment support.
- Allow using forward slash (
/
) when defining custom TagFormatter, e.g.{% MyComp %}..{% /MyComp %}
.
{% component_dependencies %}
tags are now OPTIONAL - If your components use JS and CSS, but you don't use{% component_dependencies %}
tags, the JS and CSS will now be, by default, inserted at the end of<body>
and at the end of<head>
respectively.
-
Fills can now be defined within loops (
{% for %}
) or other tags (like{% with %}
), or even other templates using{% include %}
.Following is now possible
{% component "table" %} {% for slot_name in slots %} {% fill name=slot_name %} {% endfill %} {% endfor %} {% endcomponent %}
-
If you need to access the data or the default content of a default fill, you can set the
name
kwarg to"default"
.Previously, a default fill would be defined simply by omitting the
{% fill %}
tags:{% component "child" %} Hello world {% endcomponent %}
But in that case you could not access the slot data or the default content, like it's possible for named fills:
{% component "child" %} {% fill name="header" data="data" %} Hello {{ data.user.name }} {% endfill %} {% endcomponent %}
Now, you can specify default tag by using
name="default"
:{% component "child" %} {% fill name="default" data="data" %} Hello {{ data.user.name }} {% endfill %} {% endcomponent %}
-
When inside
get_context_data()
or other component methods, the default fill can now be accessed asComponent.input.slots["default"]
, e.g.:class MyTable(Component): def get_context_data(self, *args, **kwargs): default_slot = self.input.slots["default"] ...
-
You can now dynamically pass all slots to a child component. This is similar to passing all slots in Vue:
class MyTable(Component): def get_context_data(self, *args, **kwargs): return { "slots": self.input.slots, } template: """ <div> {% component "child" %} {% for slot_name in slots %} {% fill name=slot_name data="data" %} {% slot name=slot_name ...data / %} {% endfill %} {% endfor %} {% endcomponent %} </div> """
-
Slots defined with
{% fill %}
tags are now properly accessible viaself.input.slots
inget_context_data()
-
Do not raise error if multiple slots with same name are flagged as default
-
Slots can now be defined within loops (
{% for %}
) or other tags (like{% with %}
), or even other templates using{% include %}
.Previously, following would cause the kwarg
name
to be an empty string:{% for slot_name in slots %} {% slot name=slot_name %} {% endfor %}
-
When you define multiple slots with the same name inside a template, you now have to set the
default
andrequired
flags individually.<div class="calendar-component"> <div class="header"> {% slot "image" default required %}Image here{% endslot %} </div> <div class="body"> {% slot "image" default required %}Image here{% endslot %} </div> </div>
This means you can also have multiple slots with the same name but different conditions.
E.g. in this example, we have a component that renders a user avatar
- a small circular image with a profile picture of name initials.
If the component is given
image_src
orname_initials
variables, theimage
slot is optional. But if neither of those are provided, you MUST fill theimage
slot.<div class="avatar"> {% if image_src %} {% slot "image" default %} <img src="{{ image_src }}" /> {% endslot %} {% elif name_initials %} {% slot "image" default required %} <div style=" border-radius: 25px; width: 50px; height: 50px; background: blue; "> {{ name_initials }} </div> {% endslot %} {% else %} {% slot "image" default required / %} {% endif %} </div>
-
The slot fills that were passed to a component and which can be accessed as
Component.input.slots
can now be passed through the Django template, e.g. as inputs to other tags.Internally, django-components handles slot fills as functions.
Previously, if you tried to pass a slot fill within a template, Django would try to call it as a function.
Now, something like this is possible:
class MyTable(Component): def get_context_data(self, *args, **kwargs): return { "child_slot": self.input.slots["child_slot"], } template: """ <div> {% component "child" content=child_slot / %} </div> """
NOTE: Using
{% slot %}
and{% fill %}
tags is still the preferred method, but the approach above may be necessary in some complex or edge cases. -
The
is_filled
variable (and the{{ component_vars.is_filled }}
context variable) now returnsFalse
when you try to access a slot name which has not been defined:Before:
{{ component_vars.is_filled.header }} -> True {{ component_vars.is_filled.footer }} -> False {{ component_vars.is_filled.nonexist }} -> "" (empty string)
After:
{{ component_vars.is_filled.header }} -> True {{ component_vars.is_filled.footer }} -> False {{ component_vars.is_filled.nonexist }} -> False
-
Components no longer raise an error if there are extra slot fills
-
Components will raise error when a slot is doubly-filled.
E.g. if we have a component with a default slot:
{% slot name="content" default / %}
Now there is two ways how we can target this slot: Either using
name="default"
orname="content"
.In case you specify BOTH, the component will raise an error:
{% component "child" %} {% fill slot="default" %} Hello from default slot {% endfill %} {% fill slot="content" data="data" %} Hello from content slot {% endfill %} {% endcomponent %}
-
django_components.safer_staticfiles
app was removed. It is no longer needed. -
Installation changes:
-
Instead of defining component directories in
STATICFILES_DIRS
, set them toCOMPONENTS.dirs
. -
You now must define
STATICFILES_FINDERS
-
- Beside the top-level
/components
directory, you can now define also app-level components dirs, e.g.[app]/components
(SeeCOMPONENTS.app_dirs
).
- When you call
as_view()
on a component instance, that instance will be passed toView.as_view()
- Fixed template caching. You can now also manually create cached templates with
cached_template()
-
The previously undocumented
get_template
was made private. -
In it's place, there's a new
get_template
, which supersedesget_template_string
(will be removed in v1). The newget_template
is the same asget_template_string
, except it allows to return either a string or a Template instance. -
You now must use only one of
template
,get_template
,template_name
, orget_template_name
.
-
Run-time type validation for Python 3.11+ - If the
Component
class is typed, e.g.Component[Args, Kwargs, ...]
, the args, kwargs, slots, and data are validated against the given types. (See Runtime input validation with types) -
Render hooks - Set
on_render_before
andon_render_after
methods onComponent
to intercept or modify the template or context before rendering, or the rendered result afterwards. (See Component hooks) -
component_vars.is_filled
context variable can be accessed from withinon_render_before
andon_render_after
hooks asself.is_filled.my_slot
- Added support for dynamic components, where the component name is passed as a variable. (See Dynamic components)
- Changed
Component.input
to raiseRuntimeError
if accessed outside of render context. Previously it returnedNone
if unset.
-
django_components now automatically configures Django to support multi-line tags. (See Multi-line tags)
-
New setting
reload_on_template_change
. Set this toTrue
to reload the dev server on changes to component template files. (See Reload dev server on component file changes)
-
Spread operator
...dict
inside template tags. (See Spread operator) -
Use template tags inside string literals in component inputs. (See Use template tags inside component inputs)
-
Dynamic slots, fills and provides - The
name
argument for these can now be a variable, a template expression, or via spread operator -
Component library authors can now configure
CONTEXT_BEHAVIOR
andTAG_FORMATTER
settings independently from user settings.
Component
class is no longer a subclass ofView
. To configure theView
class, set theComponent.View
nested class. HTTP methods likeget
orpost
can still be defined directly onComponent
class, andComponent.as_view()
internally callsComponent.View.as_view()
. (See Modifying the View class)
-
The inputs (args, kwargs, slots, context, ...) that you pass to
Component.render()
can be accessed from withinget_context_data
,get_template
andget_template_name
viaself.input
. (See Accessing data passed to the component) -
Typing:
Component
class supports generics that specify types forComponent.render
(See Adding type hints with Generics)
-
All tags (
component
,slot
,fill
, ...) now support "self-closing" or "inline" form, where you can omit the closing tag:{# Before #} {% component "button" %}{% endcomponent %} {# After #} {% component "button" / %}
-
All tags now support the "dictionary key" or "aggregate" syntax (
kwarg:key=val
):{% component "button" attrs:class="hidden" %}
-
You can change how the components are written in the template with TagFormatter.
The default is
django_components.component_formatter
:{% component "button" href="..." disabled %} Click me! {% endcomponent %}
While
django_components.shorthand_component_formatter
allows you to write components like so:{% button href="..." disabled %} Click me! {% endbutton %}
-
Autodiscovery module resolution changed. Following undocumented behavior was removed:
-
Previously, autodiscovery also imported any
[app]/components.py
files, and usedSETTINGS_MODULE
to search for component dirs.To migrate from:
-
[app]/components.py
- Define each module inCOMPONENTS.libraries
setting, or import each module inside theAppConfig.ready()
hook in respectiveapps.py
files. -
SETTINGS_MODULE
- Define component dirs usingSTATICFILES_DIRS
-
-
Previously, autodiscovery handled relative files in
STATICFILES_DIRS
. To align with Django,STATICFILES_DIRS
now must be full paths (Django docs).
-
- The order of arguments to
render_to_response
has changed, to align with the (now public)render
method ofComponent
class.
-
Component.render()
is public and documented -
Slots passed
render_to_response
andrender
can now be rendered also as functions.
- Vue-like provide/inject with the
{% provide %}
tag andinject()
method.
- Default value for the
COMPONENTS.context_behavior
setting was changes from"isolated"
to"django"
. If you did not set this value explicitly before, this may be a breaking change. See the rationale for change here.
-
The syntax for accessing default slot content has changed from
{% fill "my_slot" as "alias" %} {{ alias.default }} {% endfill %}
to
{% fill "my_slot" default="alias" %} {{ alias }} {% endfill %}
-
{% html_attrs %}
tag for formatting data as HTML attributes -
prefix:key=val
construct for passing dicts to components
-
{% if_filled "my_slot" %}
tags were replaced with{{ component_vars.is_filled.my_slot }}
variables. -
Simplified settings -
slot_context_behavior
andcontext_behavior
were merged. See the documentation for more details.
- Changed the default way how context variables are resolved in slots. See the documentation for more details.
-
{% component_block %}
is now{% component %}
, and{% component %}
blocks need an ending{% endcomponent %}
tag.The new
python manage.py upgradecomponent
command can be used to upgrade a directory (use--path
argument to point to each dir) of templates that use components to the new syntax automatically.This change is done to simplify the API in anticipation of a 1.0 release of django_components. After 1.0 we intend to be stricter with big changes like this in point releases.
- Components as views, which allows you to handle requests and render responses from within a component. See the documentation for more details.
- 'implicit' slot filling and the
default
option forslot
tags.
- A second installable app
django_components.safer_staticfiles
. It provides the same behavior asdjango.contrib.staticfiles
but with extra security guarantees (more info below in Security Notes).
-
Changed the syntax for
{% slot %}
tags. From now on, we separate defining a slot ({% slot %}
) from filling a slot with content ({% fill %}
). This means you will likely need to change a lot of slot tags to fill.We understand this is annoying, but it's the only way we can get support for nested slots that fill in other slots, which is a very nice feature to have access to. Hoping that this will feel worth it!
-
All files inside components subdirectores are autoimported to simplify setup.
An existing project might start to get
AlreadyRegistered
errors because of this. To solve this, either remove your custom loading of components, or set"autodiscover": False
insettings.COMPONENTS
.
-
Renamed
Component.context
andComponent.template
toget_context_data
andget_template_name
. The old methods still work, but emit a deprecation warning.This change was done to sync naming with Django's class based views, and make using django-components more familiar to Django users.
Component.context
andComponent.template
will be removed when version 1.0 is released.