Setting Up Django With Vite On Windows Subsystem for Linux
This is documented piecemeal elsewhere, but I ran into enough small issues as a WSL user that it seemed worth writing up, and while django-vite
is a cool tool I think it assumes a lot of domain knowledge that new users may not have.
Though I've been a fan of the HTMX minimal dependencies philosophy for a while, I am accepting the necessity of having some degree of JavaScript build pipeline: even if you're keeping all your logic on the back-end, there are plenty of things it's preferable to be able to install through NPM, and the Django ecosystem for managing things like SCSS leaves something to be desired. More than anything it's really nice to have HMR and an automated build process for your frontend assets. So in my latest project, I decided to start out by setting up a simple vite pipeline.
(I'm going to use uv
for the python portion of these examples, since I've been pretty impressed with it as a drop-in replacement for pip
).
Create our project with vite, following the instructions (I'll call it django-vite-example
and stick with Vanilla JS for now):
npm create vite@latest
cd django-vite-example
npm install
uv init
source .venv/bin/activate
Install Django and django-vite:
uv add Django django-vite
Create your Django project:
django-admin startproject homepage ./
And create an app for this project: python manage.py startapp vite
And add django_vite
and our custom vite
app to your INSTALLED_APPS
in settings.py
:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_vite',
'vite',
]
If you run npx vite
, you'll be able to see the vite startup page in the browser, but it's currently not tied to our Django project at all, so let's change that.
Create a templates folder in homepage
and move the index.html
file generated by vite into it:
mkdir vite/templates && mv index.html vite/templates
Let's make sure our Django server is serving this view:
In vite/views.py
, add a route for it:
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
return render(request, "vite/index.html")
And make sure we're routing to it: create a urls.py
file in the vite
app folder:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]
And make sure our Django "project" is routing to these URLs as well, in homepage/urls.py
(I always find this separation of "project" and "app" confusing in Django, but maybe that's just me):
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("", include('vite.urls')),
path('admin/', admin.site.urls),
]
If we run python manage.py runserver
, we'll just get an error here, since vite still really doesn't know what to do. Let's create a vite.config.js
file:
import { defineConfig } from 'vite';
import {resolve, join} from 'path';
export default defineConfig(() => {
const INPUT_DIR = './src/'
const OUTPUT_DIR = './dist/'
return {
root: resolve(INPUT_DIR),
base: "/static/",
build: {
manifest: true,
outDir: resolve("OUTPUT_DIR"),
rollupOptions: {
input: {
static: join(INPUT_DIR, 'main.js'),
css: join(INPUT_DIR, 'style.css')
}
}
},
server: {
watch: {
usePolling:true
}
},
}})
Special attention is worth paying to usePolling: true
: this was the only way I was able to get HMR working on WSL.
And, finally, let's make sure our Django app is actually getting what it needs from Vite. The django-vite documentation left something to be desired on this front, but I figured it out from digging through their examples. In homepage/settings.py
add
DJANGO_VITE = {
"default": {
"dev_mode": True,
}
}
and make sure STATIC_URL = '/static/'
and STATIC_ROOT = BASE_DIR / "staticfiles"
. Now, add the django_vite
tags to index.html
, replacing its default <script>
tag:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
{% load django_vite %}
{% vite_hmr_client %}
</head>
<body>
<div id="app"></div>
{% vite_asset 'main.js' %
</body>
</html
And now we should have Vite HMR working in our django app! You can see the default vite index screen at localhost:8000
and, if we change something in src/main.js
, it should hot reload - try changing Vite App
to Hot Module Reloading
.

You can find the final code on my GitHub.
Member discussion