Skip to content

Adding JS and CSS

Next we will add CSS and JavaScript to our template.

Info

In django-components, using JS and CSS is as simple as defining them on the Component class. You don't have to insert the <script> and <link> tags into the HTML manually.

Behind the scenes, django-components keeps track of which components use which JS and CSS files. Thus, when a component is rendered on the page, the page will contain only the JS and CSS used by the components, and nothing more!

1. Update project structureยค

Start by creating empty calendar.js and calendar.css files:

sampleproject/
โ”œโ”€โ”€ calendarapp/
โ”œโ”€โ”€ components/
โ”‚   โ””โ”€โ”€ calendar/
โ”‚       โ”œโ”€โ”€ calendar.py
โ”‚       โ”œโ”€โ”€ calendar.js       ๐Ÿ†•
โ”‚       โ”œโ”€โ”€ calendar.css      ๐Ÿ†•
โ”‚       โ””โ”€โ”€ calendar.html
โ”œโ”€โ”€ sampleproject/
โ”œโ”€โ”€ manage.py
โ””โ”€โ”€ requirements.txt

2. Write CSSยค

Inside calendar.css, write:

[project root]/components/calendar/calendar.css
.calendar {
  width: 200px;
  background: pink;
}
.calendar span {
  font-weight: bold;
}

Be sure to prefix your rules with unique CSS class like calendar, so the CSS doesn't clash with other rules.

Note

Use CssScope extension to automatically scope your CSS, so you won't have to worry about CSS class clashes.

This CSS will be inserted into the page as an inlined <style> tag, at the position defined by {% component_css_dependencies %}, or at the end of the inside the <head> tag (See Default JS / CSS locations).

So in your HTML, you may see something like this:

<html>
  <head>
    ...
    <style>
      .calendar {
        width: 200px;
        background: pink;
      }
      .calendar span {
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    ...
  </body>
</html>

3. Write JSยค

Next we write a JavaScript file that specifies how to interact with this component.

You are free to use any javascript framework you want.

[project root]/components/calendar/calendar.js
(function () {
  document.querySelector(".calendar").onclick = () => {
    alert("Clicked calendar!");
  };
})();

A good way to make sure the JS of this component doesn't clash with other components is to define all JS code inside an anonymous self-invoking function ((() => { ... })()). This makes all variables defined only be defined inside this component and not affect other components.

Note

Soon, django-components will automatically wrap your JS in a self-invoking function by default (except for JS defined with <script type="module">).

Similarly to CSS, JS will be inserted into the page as an inlined <script> tag, at the position defined by {% component_js_dependencies %}, or at the end of the inside the <body> tag (See Default JS / CSS locations).

So in your HTML, you may see something like this:

<html>
  <head>
    ...
  </head>
  <body>
    ...
    <script>
      (function () {
        document.querySelector(".calendar").onclick = () => {
          alert("Clicked calendar!");
        };
      })();
    </script>
  </body>
</html>

Rules of JS executionยค

  1. JS is executed in the order in which the components are found in the HTML

    By default, the JS is inserted as a synchronous script (<script> ... </script>)

    So if you define multiple components on the same page, their JS will be executed in the order in which the components are found in the HTML.

    So if we have a template like so:

    <html>
      <head>
        ...
      </head>
      <body>
        {% component "calendar" / %}
        {% component "table" / %}
      </body>
    </html>
    

    Then the JS file of the component calendar will be executed first, and the JS file of component table will be executed second.

  2. JS will be executed only once, even if there is multiple instances of the same component

    In this case, the JS of calendar will STILL execute first (because it was found first), and will STILL execute only once, even though it's present twice:

    <html>
      <head>
        ...
      </head>
      <body>
        {% component "calendar" / %}
        {% component "table" / %}
        {% component "calendar" / %}
      </body>
    </html>
    

Finally, we return to our Python component in calendar.py to tie this together.

To link JS and CSS defined in other files, use js_file and css_file attributes:

[project root]/components/calendar/calendar.py
from django_components import Component

class Calendar(Component):
    template_file = "calendar.html"
    js_file = "calendar.js"   # <--- new
    css_file = "calendar.css"   # <--- new

    def get_template_data(self, args, kwargs, slots, context):
        return {
            "date": "1970-01-01",
        }

And that's it! If you were to embed this component in an HTML, django-components will automatically embed the associated JS and CSS.

Note

Similarly to the template file, the JS and CSS file paths can be either:

  1. Relative to the Python component file (as seen above),
  2. Relative to any of the component directories as defined by COMPONENTS.dirs and/or COMPONENTS.app_dirs (e.g. [your apps]/components dir and [project root]/components)
  3. Relative to any of the directories defined by STATICFILES_DIRS.

Special role of css and js

The "primary" JS and CSS you that specify via js/css and js_file/css_file have special role in many of django-components' features:

  • CSS variables from Python are available
  • JS variables from Python are available
  • CSS scoping a la Vue

This is not true for JS and CSS defined in Media.js/css, where the linked JS / CSS are rendered as is.

5. JS variablesยค

You can pass dynamic data from your Python component to your JavaScript using JS variables. This is done using the get_js_data() method.

The dictionary returned from get_js_data() will be serialized to JSON and made available to your component's JavaScript code.

To access these variables in your JavaScript, use the special $onComponent() callback function. $onComponent() is called when the component's JavaScript is loaded.

$onComponent() is a special function that is only available within the component's JavaScript code (Component.js or Component.js_file).

Let's update our calendar component to pass data to JavaScript:

[project root]/components/calendar/calendar.py
from django_components import Component

class Calendar(Component):
    template_file = "calendar.html"
    js_file = "calendar.js"
    css_file = "calendar.css"

    class Kwargs:
        date: str = "1970-01-01"
        theme: str = "light"
        timezone: str = "UTC"

    def get_template_data(self, args, kwargs: Kwargs, slots, context):
        return {
            "date": kwargs.date,
        }

    # New!
    def get_js_data(self, args, kwargs: Kwargs, slots, context):
        return {
            "date": kwargs.date,
            "timezone": kwargs.timezone,
        }

Now update your JavaScript to use these variables. The callback receives the data from get_js_data(). Use it to update the DOM or pass it to your own logic:

[project root]/components/calendar/calendar.js
$onComponent(({ date, timezone }, { els }) => {
  console.log(`Calendar initialized for date: ${date}, timezone: ${timezone}`);

  const containerEl = els[0];

  // Use the variables to update the component's DOM
  const dateEl = containerEl.querySelector(".calendar-date");
  if (dateEl) {
    dateEl.textContent = date;
  }
  const tzEl = containerEl.querySelector(".calendar-timezone");
  if (tzEl) {
    tzEl.textContent = timezone;
  }
});

When you render the component, django-components will automatically:

  1. Call get_js_data() to get the JS variables
  2. Serialize the data to JSON
  3. Register the data with the component's dependency manager
  4. Call $onComponent() callbacks with the appropriate data when the JavaScript loads

Each component instance will receive its own JS variables based on the data returned from get_js_data().

Learn more about JS variables.

6. CSS variablesยค

You can pass dynamic data from your Python component to your CSS using CSS variables. This is done using the get_css_data() method.

The dictionary returned from get_css_data() will be converted to CSS variables where:

  • Keys become CSS variable names (prefixed with --)
  • Values are serialized to strings

Let's update our calendar component to support different themes:

[project root]/components/calendar/calendar.py
from django_components import Component

class Calendar(Component):
    template_file = "calendar.html"
    js_file = "calendar.js"
    css_file = "calendar.css"

    class Kwargs:
        date: str = "1970-01-01"
        theme: str = "light"

    def get_template_data(self, args, kwargs: Kwargs, slots, context):
        return {
            "date": kwargs.date,
        }

    # New!
    def get_css_data(self, args, kwargs: Kwargs, slots, context):
        themes = {
            "light": {
                "bg_color": "#ffffff",
                "text_color": "#333333",
            },
            "dark": {
                "bg_color": "#242424",
                "text_color": "#f1f1f1",
            },
        }
        return themes.get(kwargs.theme, themes["light"])

Now update your CSS to use these variables:

[project root]/components/calendar/calendar.css
.calendar {
  width: 200px;
  background-color: var(--bg_color);
  color: var(--text_color);
}
.calendar span {
  font-weight: bold;
}

When you render the component, django-components will automatically:

  1. Call get_css_data() to get the CSS variables
  2. Generate a stylesheet with those variables scoped to this component instance
  3. Link the stylesheet to the component
{% component "calendar" date="2024-11-06" theme="dark" %}
{% endcomponent %}

This will render something like:

<div class="calendar" data-djc-css-a1b2c3>
  Today's date is <span>2024-11-06</span>
</div>

With a corresponding stylesheet:

[data-djc-css-a1b2c3] {
  --bg_color: #242424;
  --text_color: #f1f1f1;
}

Learn more about CSS variables.


Next, let's add third-party dependencies โžก๏ธ.