Tags and Filters
Bundler Tags
bundler_embed
Return the embed HTML codes from the bundler to a specified asset.
Each asset can have multiple files associated with it. For example, a component might have javascript and css. You
can control which types of tags are included using the content_type kwarg. Common types are text/css and text/javascript,
but it is ultimately based on the file extension (e.g. .png will be image/png). Note that .css.ts is handled
as text/css and .ts and .tsx are handled as text/javascript.
By default, the tags are added to the HTML by the bundler_embed_collected_assets().
This allows assets to be embedded as needed in templates but all added in one place in the HTML (most likely the <head>).
You can force the tags to be outputted inline with inline=True. Note that this only applies CSS and JS; other assets,
like images, will always be outputted inline.
Must be passed a static path to an asset.
Usage:
{% load bundler %}
{% bundler_embed [path] [[content_type="css|js"] [inline=True] [html_*=...]] %}
Argument |
Description |
|---|---|
|
The path to the asset to embed. This must be a static value, i.e. it cannot be a template variable. |
|
(optional) If set to either ‘css’ or ‘js’ only assets of the matching type will be embedded. If omitted both types will be included (if available). |
|
(optional) If |
|
Any parameter with the |
Usage with bundler_embed_collected_assets:
{# in the base template (e.g. base.html) #}
<!doctype html>
{% load bundler %}
<html lang="en-AU">
<head>
{% bundler_embed_collected_assets %}
</head>
<body>{% block body %}{% endblock %}</body>
</html>
{# in other individual templates, e.g. 'myview.html' #}
{% extends "base.html" %}
{% block body %}
{% bundler_embed "MyComponent.ts" %}
{% bundler_embed "logo.png" html_alt="My Component Logo" %}
<h1>My View</h1>
{% endblock %}
would output:
<!doctype html>
<html lang="en-AU">
<head>
<script type="module" src="http://localhost:5173/assets/MyComponent.js"></script>
<link rel="stylesheet" href="http://localhost:5173/assets/MyComponent.css" />
</head>
<body>
<img src="http://localhost:5173/assets/logo.png" alt="My Component Logo" />
<h1>My View</h1>
</body>
</html>
Using inline=True instead:
{% extends "base.html" %}
{% block body %}
{% bundler_embed "MyComponent.ts" inline=True %}
<h1>My View</h1>
{% endblock %}
would output:
<!doctype html>
<html lang="en-AU">
<head></head>
<body>
<script type="module" src="http://localhost:5173/assets/MyComponent.js"></script>
<link rel="stylesheet" href="http://localhost:5173/assets/MyComponent.css" />
<h1>My View</h1>
</body>
</html>
Note that in the example above logo.png is always embedded inline as it is not a javascript or css file.
bundler_url
Return the URL from the bundler to a specified asset.
If you want to embed the asset with the appropriate HTML tags, use bundler_embed instead.
Must be passed a static path to an asset.
If dev, this will return the path to the asset in the dev server. If not dev, this will return the path to the built asset.
Usage:
{% load bundler %}
{% bundler_url [static path] [as varname] %}
Examples:
{% bundler_url "style.css" %}
would output, in dev:
http://localhost:5173/assets/style.css
in production:
/assets/style-abc123.css
{% bundler_url "script.js" as script_url %}
{# script_url is now available as a template variable #}
bundler_preamble
Adds necessary code for things like enabling HMR. This tag accepts no arguments.
Typically this is only required in development but that is up to the Bundler to decide - the tag should be included for both production and development.
Usage:
{% load bundler %}
{# In the <head> element #}
{% bundler_preamble %}
bundler_dev_checks
Performs dev specific checks and may render some HTML to communicate messages to user
Currently checks if the dev server is running for this project, and if not displays an error.
Error will be logged to Django dev console. In addition, an error icon and toggleable modal message will be shown
in the HTML unless BUNDLER_DISABLE_DEV_CHECK_HTML is set.
This only applies in development, in production this tag is a no-op.
This tag accepts no arguments.
Usage:
{% load bundler %}
{# At the end of the <body> element #}
<body>
...
{% bundler_dev_checks %}
</body>
bundler_embed_collected_assets
Add tags to header for assets required in page. This tag accepts no arguments.
This makes using assets in templates easier, without needing to worry about adding it to the correct template area or having duplicate tags from including the same asset more than once. You can embed assets as you need to use them, at any level of the template hierarchy, and they will be added to the header in one place with no duplication.
This works with BundlerAssetContext to collect all the assets used
within a template. See BundlerAssetContextMiddleware for how
this context is created for you in Django views.
Because each asset must specify asset paths statically, this tag can retrieve assets from BundlerAssetContext
and embed the required tags before the rest of the template is rendered.
Some existing assets are those created by the stylesheet(),
component(), or bundler_embed()
tags. See the individual implementations for options that may influence how they are embedded (e.g. the inline
option provided by bundler_embed).
html_target will control whether scripts are included
and whether CSS is outputted in line in style tags or linked externally.
Generally, this tag should be used in the <head> of the HTML document. All script tags are non-blocking by default.
Usage:
{% load bundler %}
{# In the <head> element #}
<head>
{% bundler_embed_collected_assets %}
</head>
<body>
{# The actual output for this tag will be handled by bundler_embed_collected_assets, so will appear in head #}
{% bundler_embed "style.css" %}
</body>
React Tags
component
Render a React component with the specified props
Usage:
{% load react %}
{# using common components, e.g. div, h1, etc. #}
{% component [dom element name] [prop_name=prop_value...] %} [children] {% endcomponent %}
{# using a named export #}
{% component [module path] [component import name] [prop_name=prop_value...] %} [children] {% endcomponent %}
{# component path should have a default export #}
{% component [component path] [component name] [prop_name=prop_value...] %} [children] {% endcomponent %}
There are three ways to specify which component to render. The first is for a “common component”
which is to say a built-in browser component (e.g. div):
{% component "h2" %}Heading{% endcomponent %}
The other two are for using a component defined in an external file. These will be loaded via
the specified bundler class (currently ViteBundler). With
a single argument it specifies that the default export from the file is the component to use:
{% component "components/Button" %}Click Me{% endcomponent %}
With two arguments the first is the file path and the second is the named export from that file:
{% component "components/Table" "Column" %}Name{% endcomponent %}
The last option has a variation for using a property of the export. This is useful for components
where related components are added as properties, e.g. Table and Table.Column:
{% component "components" "Table.Column" %}Name{% endcomponent %}
Note that this is only available when using named exports; default exports don’t support it due to
ambiguity around whether the . indicates file extension or property access.
You can omit the file extension - the above could resolve to components/Table.tsx (.js and .ts are also
supported). See Resolving Paths for details on how the file path is resolved.
Props are specified as keyword arguments to the tag:
{% component "components/Button" variant="primary" %}Click Me{% endcomponent %}
Additionally, a dict of props can be passed under the props kwarg:
{% component "components/Button" variant="primary" props=button_props %}Click Me{% endcomponent %}
Children can be passed between the opening {% component %} and closing {% endcomponent %}. Whitespace
is handled the same as in JSX:
Whitespace at the beginning and ending of lines is removed
Blank lines are removed
New lines adjacent to other components are removed
New lines in the middle of a string literal is replaced with a single space
So the following are all equivalent:
{% component "div" %}Hello World{% endcomponent %}
{% component %}
Hello world
{% endcomponent %}
{% component %}
Hello
world
{% endcomponent %}
{% component %}
Hello world
{% endcomponent %}
Components can be nested:
{% component "components/Button" type="primary" %}
{% components "icons" "Menu" %}{% endcomponent %}
Open Menu
{% end_component %}
and you can include HTML tags as children:
{% component "components/Button" type="primary" %}
<strong>Delete</strong> Item
{% end_component %}
You can use as <variable name> to store in a variable in context that can then be passed to another tag:
{% component "icons" "Menu" as icon %}{% end_component %}
{% component "components/Button" type="primary" icon=icon %}Open Menu{% end_component %}
All props must be JSON serializable. ComponentProp can be used to define
how to serialize data, with a matching implementation in propTransformers.tsx to de-serialize it.
For example DateProp handles serializing a python datetime and
un-serializing it as a native JS Date on the frontend. See ComponentProp
for documentation about adding your own complex props.
Components are rendered using the renderComponent function in REACT_RENDER_COMPONENT_FILE. This can be modified as needed,
for example if a new provider is required.
Note
All props passed through are converted to camel case automatically (i.e. my_prop will become myProp). This
transformation only applies to the prop name itself, any objects passed will not be recursively converted.
Server Side Rendering (SSR)
Components will automatically be rendered on the server. See Server Side Rendering (SSR) for details about how this works.
To opt out of SSR pass ssr:disabled=True to the component after the component name:
{% component 'components/Button.tsx' ssr:disabled=True %}...{% endcomponent %}
Alternatively, you can disable SSR entirely by passing disable_ssr=True to ViteBundler.
Note that when SSR is disabled, nothing will be rendered on the initial page load, so there will be a flash of content as the component is rendered on the client side.
Options
Various options can be passed to the component tag. To differentiate from actual props to the component they are prefixed with ssr: for server side rendering options, component: for general component options, or container: for options relating to the container the component is rendered into.
ssr:disabled=True- if specified, no server side rendering will occur for this componentcomponent:omit_if_empty=True- if specified, the component will not be rendered if it has no children. This is useful for when components may not be rendered based on permission checkscontainer:tag- the HTML tag to use for the container. Defaults to the custom elementdj-component.container:<any other prop>- any other props will be passed to the container element. For example, to add an id to the container you can usecontainer:id="my-id". Note that while you can pass a style string, it’s likely to be of little use with the default container styledisplay: contents. Most of the time you can just do the styling on the component itself.
For example:
{% component "components/Button" variant="Outlined" ssr:disabled=True %}
...
{% endcomponent %}
react_refresh_preamble
Add react-refresh support
Currently only works with ViteBundler. This must appear after
bundler_preamble().
This is a development only feature; in production the tag is a no-op.
See https://vitejs.dev/guide/backend-integration.html
Usage:
{% bundler_preamble %}
{% react_refresh_preamble %}
Vanilla Extract Stylesheet
stylesheet
Add a vanilla extract CSS file the page, optionally exposing class name mapping in a template variable.
Usage:
{% load vanilla_extract %}
{% stylesheet [path] [as varname] %}
The tag accepts a single argument, the path to the vanilla extract CSS file. This path must be a static value.
If the CSS file includes exported class names, you can access the mapping by specifying a variable with the syntax
as <var name>.
If you do not specify a variable using the as <var name> syntax, the styles will only be available globally,
and any specified variables will be ignored.
For more information on how paths are resolved, refer to the documentation on Resolving Paths.
The CSS file is not embedded inline where the tag is used, rather it is added by the bundler_embed_collected_assets
tag.
Warning
Be aware that, due to how {% block %} works, using this tag outside a block and attempting to read class
names inside the block will not work. For example, this will fail:
{% stylesheet "./styles.css.ts" as styles %}
{% block body %}
<main class="{{ styles.section }}">...</main>
{% endblock %}
Instead you must move it inside the block:
{% block body %}
{% stylesheet "./styles.css.ts" as styles %}
<main class="{{ styles.section }}">...</main>
{% endblock %}
Example:
{% load vanilla_extract %}
<head>
{% bundler_embed_collected_assets %}
</head>
{% stylesheet "./myView.css.ts" as styles %}
<div class="{{ styles.section }}">
<h1 class="{{ styles.heading }}">My View</h1>
...
</div>
Note
If you need to include a plain CSS file use the bundler_embed tag instead.
Note
This functionality relies on the @alliancesoftware/vite-plugin-django-vanilla-extract package.
Filters
merge_props
Merge props from two dicts together. You can pass this through the html_attr_to_jsx filter to convert
prop names to those expected in JSX.
Usage:
{% component "MyComponent" props=widget.attrs|merge_props:some_more_props|html_attr_to_jsx %}{% endcomponent %}
html_attr_to_jsx
Convert html attributes to casing expected by JSX
Calls transform_attribute_names()
If a style attribute is passed as a CSS string (e.g. "margin-right: 5px"), it will be converted to
the object form React expects (e.g. {"marginRight": "5px"}).
Usage:
{% component "MyComponent" props=widget.attrs|html_attr_to_jsx %}{% endcomponent %}
none_as_nan
Convert None to math.nan
This is useful for props that should be passed as NaN to the component. The NumberInput component uses NaN
instead of null for no value.
Usage:
{% component "@alliancesoftware/ui" "NumberInput" default_value=widget.value|none_as_nan %}{% endcomponent %}
camelize
Recursively converts dictionary keys from snake_case to camelCase for passing props to the frontend
Calls camelize
Usage:
{% component "MyComponent" props=widget.attrs|camelize %}{% endcomponent %}
Note
If you need to pass keys to ignore in the function call, you should run the camelize utility function in get_context_data manually,
rather than using this filter.
def get_context_data(self, **kwargs):
props = camelize(MySerializer(self.object).data, ignore=["ignored_key_1", "ignored_key_2"])
return super().get_context_data(**kwargs, props=props)