## Overview Each component can define extra or "secondary" CSS / JS files using the nested [`Component.Media`](../../reference/api.md#django_components.Component.Media) class, by setting [`Component.Media.js`](../../reference/api.md#django_components.ComponentMediaInput.js) and [`Component.Media.css`](../../reference/api.md#django_components.ComponentMediaInput.css). The [main HTML / JS / CSS files](../html_js_css_files) are limited to 1 per component. This is not the case for the secondary files, where components can have many of them. There is also no special behavior or post-processing for these secondary files, they are loaded as is. You can use these for third-party libraries, or for shared CSS / JS files. These must be set as paths, URLs, or [custom objects](#paths-as-objects). ```py @register("calendar") class Calendar(Component): class Media: js = [ "https://unpkg.com/alpinejs@3.14.7/dist/cdn.min.js", "calendar/script.js", ] css = [ "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", "calendar/style.css", ] ``` !!! note django-component's management of files is inspired by [Django's `Media` class](https://docs.djangoproject.com/en/5.2/topics/forms/media/). To be familiar with how Django handles static files, we recommend reading also: - [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.2/howto/static-files/) ## `Media` class Use the `Media` class to define secondary JS / CSS files for a component. This `Media` class behaves similarly to [Django's Media class](https://docs.djangoproject.com/en/5.2/topics/forms/media/#assets-as-a-static-definition): - **Static paths** - Paths are handled as static file paths, and are resolved to URLs with Django's [`{% static %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#static) template tag. - **URLs** - A path that starts with `http`, `https`, or `/` is considered a URL. URLs are NOT resolved with [`{% static %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#static). - **HTML tags** - Both static paths and URLs are rendered to ` # ``` When working with component media files, it is important to understand the difference: - `Component.Media` - Is the "raw" media definition, or the input, which holds only the component's **own** media definition - This class is NOT instantiated, it merely holds the JS / CSS files. - `Component.media` - Returns all resolved media files, **including** those inherited from parent components - Is an instance of [`Component.media_class`](../../reference/api.md#django_components.Component.media_class) ```python class ParentComponent(Component): class Media: js = ["parent.js"] class ChildComponent(ParentComponent): class Media: js = ["child.js"] # Access only this component's media print(ChildComponent.Media.js) # ["child.js"] # Access all inherited media print(ChildComponent.media._js) # ["parent.js", "child.js"] ``` !!! note You should **not** manually modify `Component.media` or `Component.Media` after the component has been resolved, as this may lead to unexpected behavior. If you want to modify the class that is instantiated for [`Component.media`](../../reference/api.md#django_components.Component.media), you can configure [`Component.media_class`](../../reference/api.md#django_components.Component.media_class) ([See example](#rendering-paths)). ## File paths Unlike the [main HTML / JS / CSS files](../html_js_css_files), the path definition for the secondary files are quite ergonomic. ### Relative to component As seen in the [getting started example](../../getting_started/your_first_component.md), to associate HTML / JS / CSS files with a component, you can set them as [`Component.template_file`](../../reference/api.md#django_components.Component.template_file), [`Component.js_file`](../../reference/api.md#django_components.Component.js_file) and [`Component.css_file`](../../reference/api.md#django_components.Component.css_file) respectively: ```py title="[project root]/components/calendar/calendar.py" from django_components import Component, register @register("calendar") class Calendar(Component): template_file = "template.html" css_file = "style.css" js_file = "script.js" ``` In the example above, we defined the files relative to the directory where the component file is defined. Alternatively, you can specify the file paths relative to the directories set in [`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs) or [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs). If you specify the paths relative to component's directory, django-componenents does the conversion automatically for you. Thus, assuming that [`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs) contains path `[project root]/components`, the example above is the same as writing: ```py title="[project root]/components/calendar/calendar.py" 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" ``` !!! important **File path resolution in-depth** At component class creation, django-components checks all file paths defined on the component (e.g. `Component.template_file`). For each file path, it checks if the file path is relative to the component's directory. And such file exists, the component's file path is re-written to be defined relative to a first matching directory in [`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs) or [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs). **Example:** ```py title="[root]/components/mytable/mytable.py" class MyTable(Component): template_file = "mytable.html" ``` 1. Component `MyTable` is defined in file `[root]/components/mytable/mytable.py`. 2. The component's directory is thus `[root]/components/mytable/`. 3. Because `MyTable.template_file` is `mytable.html`, django-components tries to resolve it as `[root]/components/mytable/mytable.html`. 4. django-components checks the filesystem. If there's no such file, nothing happens. 5. If there IS such file, django-components tries to rewrite the path. 6. django-components searches `COMPONENTS.dirs` and `COMPONENTS.app_dirs` for a first directory that contains `[root]/components/mytable/mytable.html`. 7. It comes across `[root]/components/`, which DOES contain the path to `mytable.html`. 8. Thus, it rewrites `template_file` from `mytable.html` to `mytable/mytable.html`. NOTE: In case of ambiguity, the preference goes to resolving the files relative to the component's directory. ### Globs Components can have many secondary files. To simplify their declaration, you can use globs. Globs MUST be relative to the component's directory. ```py title="[project root]/components/calendar/calendar.py" from django_components import Component, register @register("calendar") class Calendar(Component): class Media: js = [ "path/to/*.js", "another/path/*.js", ] css = "*.css" ``` How this works is that django-components will detect that the path is a glob, and will try to resolve all files matching the glob pattern relative to the component's directory. After that, the file paths are handled the same way as if you defined them explicitly. ### Supported types File paths can be any of: - `str` - `bytes` - `PathLike` (`__fspath__` method) - `SafeData` (`__html__` method) - `Callable` that returns any of the above, evaluated at class creation (`__new__`) To help with typing the union, use [`ComponentMediaInputPath`](../../../reference/api#django_components.ComponentMediaInputPath). ```py from pathlib import Path from django.utils.safestring import mark_safe class SimpleComponent(Component): class Media: css = [ mark_safe(''), Path("calendar/style1.css"), "calendar/style2.css", b"calendar/style3.css", lambda: "calendar/style4.css", ] js = [ mark_safe(''), Path("calendar/script1.js"), "calendar/script2.js", b"calendar/script3.js", lambda: "calendar/script4.js", ] ``` ### Paths as objects In the example [above](#supported-types), you can see that when we used Django's [`mark_safe()`](https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.safestring.mark_safe) to mark a string as a [`SafeString`](https://docs.djangoproject.com/en/5.2/ref/utils/#django.utils.safestring.SafeString), we had to define the URL / path as an HTML `' ) @register("calendar") class Calendar(Component): template_file = "calendar/template.html" class Media: css = "calendar/style1.css" js = [ # ', self.absolute_path(path) ) return tags @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" # Override the behavior of Media class media_class = MyMedia ```