Using Vite.js with Django 3.x
Create a new Django app
I prefer to keep ma front-end code contained to a single Django app, which is convenient for most Django websites.
Create a new app called theme
.
django-admin startapp theme
In your settings.py
, make sure you add the newly created app to your INSTALLED_APPS
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
'theme',
]
<PROJECT_ROOT>
├── db.sqlite3
├── manage.py
├── requirements.txt
├── theme
├── __init__.py
├── apps.py
├── views.py
├── models.py
└── templates/
└── theme
└── index.html
Setup Vite
- Create a
main.js
and amain.css
file that’ll serve as an entry point.
// main.js
import "./main.css"
console.log("hello world");
// main.css
body {
background-color: tomato;
}
- Install dependencies.
In our newly created app
theme
, we’ll setup apackage.json
. The only dependency we’ll be using isvite
, as it’s already packing everything you need to compile js and css.
cd PROJECT_ROOT/theme
npm init -y
npm -S install vite
- Under
PROJECT_ROOT/theme/
create avite.config.js
// vite.config.js
const { resolve } = require('path');
export default {
build: {
manifest: true, // adds a manifest.json
rollupOptions: {
input: [
resolve(__dirname, './main.js'),
]
},
outDir: 'static', // puts the manifest.json in PROJECT_ROOT/theme/static/
assetsDir: 'theme', // puts asset files in in PROJECT_ROOT/theme/static/theme
},
plugins: [],
server: {
port: 3001, // make sure this doesn't conflict with other ports you're using
open: false,
}
};
In your package.json
,
"scripts": {
"theme:dev": "vite",
"theme:build": "vite build"
}
Now you’re able to run your build.
Use the compiled assets
In PROJECT_ROOT/theme
, create a new python module templatetags
and add a vite.py
file in it
cd PROJECT_ROOT/theme
mkdir templatetags/
touch templatetags/__init__.py
touch templatetags/vite.py
The final structure should look like the following:
<PROJECT_ROOT>
├── db.sqlite3
├── manage.py
├── requirements.txt
└── theme
├── __init__.py
├── apps.py
├── views.py
├── models.py
├── templates/
└── templatetags/
├── __init__.py
└── vite.py
Now invite templatetags/vite.py
, add:
# PROJECT_ROOT/theme/templatetags/vite.py
from os import path
import re
import json
from django import template
from django.conf import settings
from django.utils.safestring import mark_safe
from django.templatetags.static import static
register = template.Library()
is_absolute_url = lambda url: re.match("^https?://", url)
DEV = settings.DEBUG
DEV_SERVER_ROOT = "http://localhost:3001"
def vite_manifest(entries_names):
app_name = 'theme'
manifest_filepath = path.join(app_name, 'static/manifest.json')
if DEV:
scripts = [
f"{DEV_SERVER_ROOT}/@vite/client",
]
for name in entries_names:
scripts.append(f'{DEV_SERVER_ROOT}/{name}')
styles = []
return scripts, styles
else:
with open(manifest_filepath) as fp:
manifest = json.load(fp)
_processed = set()
def _process_entries(names):
scripts = []
styles = []
for name in names:
if name in _processed:
continue
chunk = manifest[name]
import_scripts, import_styles = _process_entries(chunk.get('imports', []))
scripts += import_scripts
styles += import_styles
scripts += [chunk['file']]
styles += [css for css in chunk.get('css', [])]
_processed.add(name)
return scripts, styles
return _process_entries(entries_names)
@register.simple_tag(name="vite_styles")
def vite_styles(*entries_names):
"""
Populate an html template with styles generated by vite
Usage::
{% vite_styles 'main.js' 'other-entry.js' %}
Examples::
<head>
...
{% vite_styles 'main.js' 'other-entry.js' %}
</head>
"""
_, styles = vite_manifest(entries_names)
styles = map(lambda href: href if is_absolute_url(href) else static(href), styles)
as_link_tag = lambda href: f'<link rel="stylesheet" href="{href}" />'
return mark_safe("\n".join(map(as_link_tag, styles)))
@register.simple_tag(name="vite_scripts")
def vite_scripts(*entries_names):
"""
Populate an html template with script tags generated by vite
Usage::
{% vite_scripts 'main.js' 'other-entry.js' %}
Examples::
<body>
<!-- Your HTML -->
{% vite_scripts 'main.js' 'other-entry.js' %}
</body>
"""
scripts, _ = vite_manifest(entries_names)
scripts = map(lambda src: src if is_absolute_url(src) else static(src), scripts)
as_script_tag = lambda src: f'<script type="module" src="{src}"></script>'
return mark_safe("\n".join(map(as_script_tag, scripts)))
Then in your index.html
:
<html>
<head>
{% vite_styles 'main.js' 'other-entry.js' %}
</head>
<body>
{% vite_scripts 'main.js' %}
</body>
</html>