a blog about Django & Web Development

The Django Developer’s Guide to Vite

The easy way to add JavaScript to your Django projects.

What is the best way to add JavaScript to a Django project? It is often a question of how much JavaScript are you prepared to add to your frontend.

At one extreme, you could just write Vanilla JavaScript and import it into your templates. When you find yourself writing lots of code to do simple things, you might turn to jQuery.

Low JavaScript approaches are becoming more popular. htmx proves that you don’t have to write any JavaScript at all to make HTTP requests. Alpine.js is often used alongside htmx to add additional interactivity.

Speaking of Alpine.js, ultra-lightweight JavaScript frameworks are on the rise. Alpine JS is only 7.1kb. Even smaller is Preact at just 3kb. For Vue fans, petite-vue is designed for lightweight, progressive enhancement.

Mainstream JavaScript libraries such as Vue, React and Svelte are still popular. However, they are most commonly used with a fully decoupled architecture.

You don’t actually need to completely decoupled front and back ends in order to use a frontend framework. You might still want to keep the monolith, but progressively enhance your Django templates with JavaScript.

You do, however, need a way to incorporate JavaScript into your Django templates. Not just to load files during development, but also bundle them for production.

Vite is a tool to help you do that.

Why care about Vite?

Vite exists to make JavaScript development easier. When you make changes to your source files, Vite’s development server will pick up the changes immediately after the file is saved. It uses Rollup under the hood to bundle your assets ready for a deployment.

If you are already using Webpack to load and bundle JavaScript, and you’re happy with it, then Vite is probably not for you.

However, if you find Webpack slow, or difficult to work with, then Vite is your faster and simpler alternative.

I have spent a lot of time getting to know Vite, and I think it is well-suited for Django projects that use Vue but not using the fully decoupled SPA+API architecture. Vite is useful to integrate Vue into a monolith without the need to refactor the backend.

Adding Vue to Django templates isn’t new. This tutorial by Vladimir Supalov will get you started quickly and is well-suited for small projects. However, the challenge becomes greater in larger projects because you can’t use Single File Components to structure your code.

For projects with complex frontends, the Single Page App will give you the best of modern JavaScript. The problem occurs when you want to add a library like Vue or React to an existing project. Refactoring to decouple front and back ends may not be an option, but the developer experience of using Vue in Django templates is inferior.

Vite provides the best of both worlds. You can use Django’s built-in templates and session authentication and sprinkle in Vue where required. When your Vue components are updated, you will see the results in your browser straightaway.

What is Vite?

Vite provides a server to serve static assets (CSS, JavaScript etc.). It uses ES Modules and Hot Module Replacement (HMR) to process source files. This means when you change your CSS or update a Vue component, you can see the result in your browser immediately. This means no more hard refreshes to see your changes.

The reason Vite is fast is because it treats source-code (e.g Vue components) and dependencies (e.g. lodash) separately. Dependencies are files which are not changed often and are mostly found in the node_modules directory. Vite bundles them. Source code is edited frequently by developers, so Vite serves them over Native ESM. During development, you can view changes quickly, because Vite doesn’t have to rebundle the entire suite of assets every time a file is changed.

Vite is also a tool for bundling JavaScript, using Rollup 3 under the hood.

It also supports JSX, TypeScript and Vue’s Single File Components through plugins.

django-vite

Django templates will need to import JavaScript files from different places depending on whether the environment is for development or production. In development, it will get them from Vite’s development server. In production, it will need to fetch the bundled files.

The challenge for Django developers is ensuring assets are fetched from the correct place.

Django-vite is a Python package to make it easier to integrate Vite into a Django project by providing template tags.

It provides the logic for deciding whether to import assets from the Vite server or from the bundle.

{% block content %}

{% vite_hmr_client %}
{% vite_asset 'js/main.js' %}

<h2>{{ django_message }}</h2>

How to Integrate Vite into a Django Project

I am going to show you how to add Vite to an existing Django project using django-vite.

In a separate tutorial, I am going to show you how to use Vite to integrate Vue into an existing Django project.

This is what I’m going to cover.

  1. Set up your Django project
  2. Add npm
  3. Install Vite
  4. Install django-vite
  5. Loading JavaScript from the Vite server
  6. Build assets

This tutorial is a proof of concept of how Vite can work with Django. To keep the example simple, we will just import some CSS and print a message to the console.

I have a separate tutorial where I show you how to use Vite to enhance Django

Two screenshots (before and after). One has no CSS and the other has been styled.

Disclaimer

The config for this project is for demo purposes rather than a real-life project. Use this as a starting point but you may need extra config to make it production ready.

Prerequisites

You will need Node and your favourite JavaScript package manager installed. This tutorial will use npm

You will need a Django project with at least one view and template.

I have created a repository to store the source code for this tutorial. You can clone the START branch, which has the Django project without any Vite.

Step 1: Install Vite

First, deactivate your virtual environment using the deactivate command. This is because your virtual environment can prevent you from selecting a particular version of Node.

You may need to upgrade your version of Node. At time of writing, the minimum version to install Vite was version 14.7. I recommend Node Version Manager (nvm) to apply a particular version of Node.

Once you are sure you have the correct version of Node installed, you can install Vite.

$ npm install vite

This will install Vite. npm will automatically create a node_modules directory to store dependencies and a package.json file to keep track of what has been installed.

It’s helpful to add a name and version to your package.json.

{
  "name": "django-vite-starter",
  "version": "1.0.0",
  "dependencies": {
    "vite": "^4.0.4"
  }
}

Step 2: Start the Vite Server

Vite provides a development server for serving static files.

You can run with with the following command, which starts the server on port 5173.

npx vite

You can also add scripts to your package.json to run the Vite server.

{
  "name": "django-vite-starter",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vite": "^4.0.4"
  }
}

If you add the scripts, you can start your Vite server with the following command.

npm run dev

Whichever you choose, you should get an output similar to this…

  VITE v4.0.4  ready in 627 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

The server is now running but it’s not usable as the port isn’t exposed. We need to create a config file to expose the port.

Step 3: Add Config

Create a file vite.config.js at your project root (same directory as package.json). I have taken this config from this example and find them suitable for getting started.

const { resolve } = require('path');

module.exports = {
  root: resolve('./static/src'),
  base: '/static/',
  server: {
    host: '0.0.0.0',
    port: 3000,
    open: false,
    watch: {
      usePolling: true,
      disableGlobbing: false,
    },
  },
  resolve: {
    extensions: ['.js', '.json'],
  },
  build: {
    outDir: resolve('./static/dist'),
    assetsDir: '',
    manifest: true,
    emptyOutDir: true,
    target: 'es2015',
    rollupOptions: {
      input: {
        main: resolve('./static/src/js/main.js'),
      },
      output: {
        chunkFileNames: undefined,
      },
    },
  },
};

There are two parts of the settings. The settings in server are your settings for using Vite in development. Here you can tell Vite where to look for files and specify which port to run on.

The build section has the settings for bundling assets for production. Here, static/src/js/main.js is specified as the entrypoint.

If you restart your Vite server, you should get an output similar to this…

  VITE v4.0.4  ready in 675 ms

  ➜  Local:   http://localhost:3000/static/
  ➜  Network: http://192.168.1.96:3000/static/
  ➜  press h to show help

The Vite part of this tutorial is now complete. Now we have to link it to our Django project.

Step 4: Install django-vite

So far, we’ve installed Vite but it doesn’t really do anything. Django-vite is a Python package that will help you use Vite to import assets into your Django templates.

You can install django-vite with the following command…

pip install django-vite

In settings.py, you will need to add django_vite to your list of installed apps…

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    .
    .
    .
    "django_vite"
]

There are also some additional settings to add to configure django-vite. I’ve taken these settings from the example provided by the creator of Vite.

Step 5: Create a JavaScript File

My project has a directory called static to store CSS and JavaScript. The static directory has sub-directories called src and dist. Our source files go into src and dist will store the bundled files.

In static/src/js create main.js

Add whatever you like. For a proof of concept I’m going to keep it simple…

import '../css/styles.css';

console.log("Hello from Vite!")

Step 6: Import the JavaScript into the template

Django-vite provides template tags for importing assets.

In development, django-vite will get the files from the Vite server.

In production, it will get them from the dist directory.

{% extends 'base.html' %}
{% load django_vite %}

{% block content %}

{% vite_hmr_client %}
{% vite_asset 'js/main.js' %}

<h2>{{ django_message }}</h2>

<p>This page has been generated from a Django template.</p>
<p>The CSS was loaded by Vite.</p>
<p>Everything inside the box was rendered by Vue.</p>
<p>Vite is a way to integrate Vue into an existing Django project.</p>

<br>

<div id="app">

</div>

{% endblock %}

It works! Our stylesheet is now loaded via main.js via Vite.

Our console.log message also appears in the console.

Build the bundle

Vite bundles your assets to help make your application ready for production.

To do this, run the following command:

$ npm run build

With our current settings, the files will be saved to static/dist.

Vite also creates a file called manifest.json which maps the source file paths to the built files.

Django-vite also works with collectstatic.

After running npm run build, you can collect your static files using python manage.py collectstatic as normal. The files from static/dist will be copied into collectedstatic.

Next Steps

We have set up Vite on a Django project to load static assets.

Vite is fast because it only bundles files for production. For development, Vite provides a Hot Module Replacement server, which serves files using Native ESM.

Django-vite is a package that makes it easy to load Vite assets into your Django templates. During development, it will fetch source files from the Vite server. In production, it will fetch files from the bundle.

Vite provides a foundation for loading JavaScript into Django templates. Using this, we can begin to enhance our templates with Vue, which we will do in the next tutorial.

Related Posts