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 themeIn 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.htmlSetup Vite
- Create a
main.jsand amain.cssfile 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.pyThe 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.pyNow 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>