HTML / JS / CSS files
Overview¤
Each component can have single "primary" HTML, CSS and JS file associated with them.
Each of these can be either defined inline, or in a separate file:
- HTML files are defined using
Component.template
orComponent.template_file
- CSS files are defined using
Component.css
orComponent.css_file
- JS files are defined using
Component.js
orComponent.js_file
@register("calendar")
class Calendar(Component):
template_file = "calendar.html"
css_file = "calendar.css"
js_file = "calendar.js"
or
@register("calendar")
class Calendar(Component):
template = """
<div class="welcome">
Hi there!
</div>
"""
css = """
.welcome {
color: red;
}
"""
js = """
console.log("Hello, world!");
"""
These "primary" files will have special behavior. For example, each will receive variables from the component's data methods. Read more about each file type below:
In addition, you can define extra "secondary" CSS / JS files using the nested Component.Media
class, by setting Component.Media.js
and Component.Media.css
.
Single component can have many secondary files. There is no special behavior for them.
You can use these for third-party libraries, or for shared CSS / JS files.
Read more about Secondary JS / CSS files.
Warning
You cannot use both inlined code and separate file for a single language type (HTML, CSS, JS).
However, you can freely mix these for different languages:
HTML¤
Components use Django's template system to define their HTML. This means that you can use Django's template syntax to define your HTML.
Inside the template, you can access the data returned from the get_template_data()
method.
You can define the HTML directly in your Python code using the template
attribute:
class Button(Component):
template = """
<button class="btn">
{% if icon %}
<i class="{{ icon }}"></i>
{% endif %}
{{ text }}
</button>
"""
def get_template_data(self, args, kwargs, slots, context):
return {
"text": kwargs.get("text", "Click me"),
"icon": kwargs.get("icon", None),
}
Or you can define the HTML in a separate file and reference it using template_file
:
class Button(Component):
template_file = "button.html"
def get_template_data(self, args, kwargs, slots, context):
return {
"text": kwargs.get("text", "Click me"),
"icon": kwargs.get("icon", None),
}
<button class="btn">
{% if icon %}
<i class="{{ icon }}"></i>
{% endif %}
{{ text }}
</button>
HTML processing¤
Django Components expects the rendered template to be a valid HTML. This is needed to enable features like CSS / JS variables.
Here is how the HTML is post-processed:
-
Insert component ID: Each root element in the rendered HTML automatically receives a
data-djc-id-cxxxxxx
attribute containing a unique component instance ID. -
Insert CSS ID: If the component defines CSS variables through
get_css_data()
, the root elements also receive adata-djc-css-xxxxxx
attribute. This attribute links the element to its specific CSS variables. -
Insert JS and CSS: After the HTML is rendered, Django Components handles inserting JS and CSS dependencies into the page based on the dependencies rendering strategy (document, fragment, or inline).
For example, if your component contains the
{% component_js_dependencies %}
or{% component_css_dependencies %}
tags, or the<head>
and<body>
elements, the JS and CSS scripts will be inserted into the HTML.For more information on how JS and CSS dependencies are rendered, see Rendering JS / CSS.
JS¤
The component's JS script is executed in the browser:
- It is executed AFTER the "secondary" JS files from
Component.Media.js
are loaded. - The script is only executed once, even if there are multiple instances of the component on the page.
- Component JS scripts are executed in the order how they appeared in the template / HTML (top to bottom).
You can define the JS directly in your Python code using the js
attribute:
class Button(Component):
js = """
console.log("Hello, world!");
"""
def get_js_data(self, args, kwargs, slots, context):
return {
"text": kwargs.get("text", "Click me"),
}
Or you can define the JS in a separate file and reference it using js_file
:
class Button(Component):
js_file = "button.js"
def get_js_data(self, args, kwargs, slots, context):
return {
"text": kwargs.get("text", "Click me"),
}
CSS¤
You can define the CSS directly in your Python code using the css
attribute:
class Button(Component):
css = """
.btn {
width: 100px;
color: var(--color);
}
"""
def get_css_data(self, args, kwargs, slots, context):
return {
"color": kwargs.get("color", "red"),
}
Or you can define the CSS in a separate file and reference it using css_file
:
class Button(Component):
css_file = "button.css"
def get_css_data(self, args, kwargs, slots, context):
return {
"text": kwargs.get("text", "Click me"),
}
File paths¤
Compared to the secondary JS / CSS files, the definition of file paths for the main HTML / JS / CSS files is quite simple - just strings, without any lists, objects, or globs.
However, similar to the secondary JS / CSS files, you can specify the file paths relative to the component's directory.
So if you have a directory with following files:
[project root]/components/calendar/
├── calendar.html
├── calendar.css
├── calendar.js
└── calendar.py
You can define the component like this:
from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_file = "calendar.html"
css_file = "calendar.css"
js_file = "calendar.js"
Assuming that COMPONENTS.dirs
contains path [project root]/components
, the example above is the same as writing out:
from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_file = "calendar/template.html"
css_file = "calendar/style.css"
js_file = "calendar/script.js"
If the path cannot be resolved relative to the component, django-components will attempt to resolve the path relative to the component directories, as set in COMPONENTS.dirs
or COMPONENTS.app_dirs
.
Read more about file path resolution.
Access component definition¤
Component's HTML / CSS / JS is resolved and loaded lazily.
This means that, when you specify any of template_file
, js_file
, css_file
, or Media.js/css
, these file paths will be resolved only once you either:
-
Access any of the following attributes on the component:
-
Render the component.
Once the component's media files have been loaded once, they will remain in-memory on the Component class:
- HTML from
Component.template_file
will be available underComponent.template
- CSS from
Component.css_file
will be available underComponent.css
- JS from
Component.js_file
will be available underComponent.js
Thus, whether you define HTML via Component.template_file
or Component.template
, you can always access the HTML content under Component.template
. And the same applies for JS and CSS.
Example:
# When we create Calendar component, the files like `calendar/template.html`
# are not yet loaded!
@register("calendar")
class Calendar(Component):
template_file = "calendar/template.html"
css_file = "calendar/style.css"
js_file = "calendar/script.js"
class Media:
css = "calendar/style1.css"
js = "calendar/script2.js"
# It's only at this moment that django-components reads the files like `calendar/template.html`
print(Calendar.css)
# Output:
# .calendar {
# width: 200px;
# background: pink;
# }
Warning
Do NOT modify HTML / CSS / JS after it has been loaded
django-components assumes that the component's media files like js_file
or Media.js/css
are static.
If you need to dynamically change these media files, consider instead defining multiple Components.
Modifying these files AFTER the component has been loaded at best does nothing. However, this is an untested behavior, which may lead to unexpected errors.