django-vite: serving Vite-built assets from Django templates, one manifest at a time
Integration of ViteJS in a Django project.
At a glance
- What is it?
- django-vite is a small Django app that bridges Vite and the template layer, giving you template tags for the HMR client, for script assets and for React refresh. Its sharp edges are the manifest lookup and the two asset tags that do not behave the same way.
- Who is it for?
- django-vite fits a Django project that has decided to build its front end with Vite and wants hot module replacement in development and a manifest lookup in production, with Django still owning the templates. It does not fit a project that wants a single asset pipeline, and it does not fit a team unwilling to keep the Vite config and the Django config in step.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap: Vite produces the assets, Django renders the page
django-vite is described in one line as the integration of ViteJS in a Django project, and that description is accurate about the scope. It is not a bundler and not a Django asset pipeline replacement. It is the layer that lets a Django template reference files that Vite built.
The problem it solves is specific. Vite assumes a single page application with an index.html that it can parse to find entry points, and Django serves server-rendered templates with no such file. So the integration has to do three things: tell Vite where the output goes, tell it what the entry points are, and let a template ask for a built file by name and get back the right hashed URL.
That last part is the real content of this library, and it is why a manifest exists at all. Vite writes manifest.json as part of the build, mapping your logical entry point names to the hashed files it produced, and django-vite reads that file in production mode to turn a name into a URL. The name you write in the template is a key in the manifest, not a path on disk, and the documentation is careful about that distinction.
The audience is Django developers who have already chosen Vite for their front end. It is not for people looking for a way to avoid a build step.
One pip install, one entry in INSTALLED_APPS
Installation on the Django side is deliberately short. The package comes from PyPI:
pip install django-viteAnd then the app is registered, with the documentation being specific about ordering, telling you to add django_vite to INSTALLED_APPS in your settings.py before your apps that are using it:
INSTALLED_APPS = [
...
'django_vite',
...
]The ordering note is not cosmetic in a project that relies on template tag discovery, and it is the kind of instruction that only appears in a README written by someone who hit the failure.
The package metadata puts the floor at Django 3.2, with classifiers for Django 3.2 through 4.2 and Python 3.8 through 3.12. The licence is Apache 2.0, declared in the setup metadata as Apache License, Version 2.0, with the LICENSE file at the root of the repository.
The current release is 3.2.0, and the release history is not fast. The tagged versions are 3.2.0 on 2026-09-14, 3.1.0 on 2025-02-23 and 3.0.6 on 2024-12-30, and the last push to the repository was on 2026-09-14 with the repository not archived. The pace suggests a project that is maintained on the side of something else, which is a reasonable expectation for a library whose surface area is a handful of template tags.
The Vite side needs an explicit input because there is no index.html
The Vite configuration is where most of the work lands, and the reason is spelled out in the README. You are doing server side rendering rather than a single page application, so you do not have an index.html for Vite to inspect and work out what to compile. You have to tell it directly, in build.rollupOptions.input.
The whole shape of the config follows from the Django settings. The base option has to be set to the same value as the STATIC_URL Django setting, build.outDir has to point at where you want the assets compiled, and build.manifest has to be manifest.json:
export default defineConfig({
...
base: "/static/",
build: {
...
manifest: "manifest.json",
outDir: resolve("./assets"),
rollupOptions: {
input: {
<unique key>: '<path to your asset>'
}
}
}
})The key you choose inside input is the name you will later use in templates, which is the single most important detail to get right at the start.
There is one more step, and it comes from Vite's own backend integration guide. Your assets should include the modulepreload polyfill, which you import at the beginning of your app entry:
// Add this at the beginning of your app entry.
import 'vite/modulepreload-polyfill';Finally, the Django side has to be told where the output landed. The build.outDir from vite.config.js must be included in STATICFILES_DIRS:
STATICFILES_DIRS = [
BASE_DIR / "assets"
]dev_mode switches between the Vite server and compiled files
The configuration itself lives in a DJANGO_VITE dictionary in settings.py, keyed by a name that lets you run several Vite setups in one project:
DJANGO_VITE = {
"default": {
"dev_mode": True
}
}There is also a legacy form using module-level settings, and the README offers it for projects that have not migrated:
DJANGO_VITE_DEV_MODE = TrueWhat dev_mode actually changes is the delivery mechanism, and the documentation is explicit about it. In development mode, assets are included as modules served by the Vite web server, which is what enables hot module replacement. In production mode, assets are included as standard assets, with no Vite webserver and no HMR, like default Django static files. That has a direct consequence: your assets must be compiled with Vite before the production mode will work.
The README suggests you may set this to the same value as your DEBUG setting, but leaves it open to your needs, which is sensible because some teams develop against a compiled bundle on purpose.
The per-configuration keys are listed in the table of contents and include dev_mode, dev_server_protocol, dev_server_host, dev_server_port, static_url_prefix, manifest_path, legacy_polyfills_motif, ws_client_url, react_refresh_url and app_client_class. The host and port keys are what point the HMR client at a dev server that is not on localhost, and ws_client_url is what tells the browser where the WebSocket lives.
vite_asset reads the manifest and pulls in the CSS that goes with it
The template side starts with loading the tags in your base template, and adding the HMR client to the head element:
{% load django_vite %}{% vite_hmr_client %}The HMR client tag adds a script tag for the Vite HMR client, and it does so only when the dev mode setting is true, otherwise it renders nothing at all.
The main tag loads a script:
{% vite_asset '<path to your asset>' %}Its behaviour is worth spelling out, because several of these rules are easy to get wrong. In development and in production, scripts are included as modules, with type=module. The tag only accepts JS and TS; other kinds of asset have to be included from inside the script itself using import statements. The path has to be relative to the root key in your Vite config file, and it has to be a key inside the manifest file. Alternatively the path can be an asset name, for example "name": "foo" in the manifest, which is the key in the entrypoint object if you set up multiple entry points following the multi-page app approach.
In production mode the library reads the manifest.json file generated by Vite and imports all the CSS files dependent on that script, before importing the script itself. You can add as many of these tags as you like, one for each input you declared in the Vite configuration.
vite_asset_url skips the dependencies, and the difference will bite you
There is a second asset tag that looks similar and behaves differently:
{% vite_asset_url '<path to your asset>' %}It generates only the URL of an asset, with no surrounding tag. The README attaches a warning to it: this does not generate URLs for dependent assets of this one, unlike the previous tag. So a stylesheet that Vite associated with the entry point is not emitted by this tag, and if you rely on it to emit a CSS link, that link will not be there.
For React projects there is a third tag, vite_react_refresh, which generates the JavaScript script needed to support React hot module replacement, and it accepts keyword arguments that are added to the generated script tag:
{% vite_react_refresh nonce="{{ request.csp_nonce }}" %}That example is the documented way to work with a Content Security Policy, using django-csp and the request's nonce.
The same mechanism works for the other tags. By default all script tags are generated with type="module" and crossorigin="" attributes, and you can add or override attributes with keyword arguments:
{% vite_asset '<path to your asset>' foo="bar" hello="world" data_turbo_track="reload" %}Attribute values can also come from the request context, for example foo=request.GET.bar, and overriding a default is done by passing the same attribute again, such as crossorigin="anonymous". The documentation does suggest keeping the default type="module", since Vite build scripts are ES6 modules.
The test suite refuses to pass below full coverage
The pyproject.toml of the repository sets the testing bar unusually high. Pytest is configured with coverage over the django_vite package, with branch coverage enabled, HTML and term-missing reports, and a hard threshold:
addopts = '''
--cov=django_vite
--cov-report html
--cov-report term-missing
--cov-branch
--cov-fail-under=100
'''A coverage failure under 100 makes the test run fail. There is also a filter that ignores DeprecationWarning for django_vite specifically, rather than globally.
The rest of the tooling is small and conventional. Black with a line length of 88, and Ruff with a curated rule selection covering pycodestyle, pyflakes, pyupgrade, bugbear, pytest, simplify, django and isort. Two rules, unused imports and unused variables, are marked unfixable with a comment explaining that autofixing them costs developer experience.
The repository also carries tox.ini, a pre-commit configuration, MANIFEST.in and a tests/ directory, and the setup.py packages the django_vite package while excluding tests.
The practical reading of the coverage rule is that this library has a small, well understood surface, and the author treats any uncovered line as an unfinished behaviour. For an adopter it means you can trust the template tags to behave as documented, and for a contributor it means a new branch needs a test before it can be merged.
Where django-vite is the wrong tool
The first wrong fit is a project that wants one asset pipeline. Django ships its own staticfiles handling, and a common setup is a Django app with a little JavaScript and no bundler at all. Adding Vite to that is a cost with no return, and this library does nothing for you if you have no Vite build.
The second wrong fit is a project that wants a design system. The README lists a Vite Legacy Plugin, a multi-app configuration section, configuration variables including legacy_polyfills_motif, and a note on Whitenoise for serving assets. Those are integration details for a single page application build, not a component library or a token pipeline. If your problem is a shared design system across a Django estate, this is not where it lives.
The third is a project where the template and the build can drift apart. The coupling is tight by necessity: the key you write in build.rollupOptions.input has to match the key you write in the template, that key has to exist in the manifest, base has to equal STATIC_URL, and outDir has to be in STATICFILES_DIRS. Every one of those is a place where a rename in one file breaks the other, and a mistyped template name is a missing asset rather than a build error.
The fourth is a deployment that does not run a build. In production mode the assets must already be compiled, which is fine for a CI pipeline that builds and collects static files, and wrong for a deployment that expects the application container to produce them at startup.
And the CDN case is worth checking before you commit. The README says that by default django-vite will try to load a local manifest.json, and that if you want it to fetch the manifest from a non-standard source such as S3, you can subclass the client, but that section is cut off in the documentation available here, so the exact interface is something you will need to read in the repository rather than in the README.
Editorial conclusion
django-vite fits a Django project that has decided to build its front end with Vite and wants hot module replacement in development and a manifest lookup in production, with Django still owning the templates. It does not fit a project that wants a single asset pipeline, and it does not fit a team unwilling to keep the Vite config and the Django config in step. Before adopting it, build once with a manifest and confirm that every entry point you intend to reference appears as a key in manifest.json.
Frequently asked questions
How do I install django-vite?
Run pip install django-vite, then add django_vite to INSTALLED_APPS in your settings.py, before the apps that use it. The package requires Django 3.2 or newer.
What must I change in my Vite config for Django?
Set base to the same value as your STATIC_URL setting, set build.outDir to where the assets should be compiled, set build.manifest to manifest.json, and list your entry points in build.rollupOptions.input, because in server side rendering there is no index.html for Vite to inspect.
What is the difference between vite_asset and vite_asset_url?
vite_asset adds a script tag and, in production, reads manifest.json to import the CSS files dependent on that script before the script itself. vite_asset_url generates only the URL with no tag around it, and the README warns that it does not generate URLs for dependent assets.
How do I get hot module replacement in Django templates?
Set dev_mode to true in the DJANGO_VITE configuration and add the vite_hmr_client tag to the head of your base template. That tag renders nothing when dev mode is off, and in development the assets are served as modules by the Vite web server.
What licence is django-vite released under?
It is released under the Apache License, Version 2.0, as declared in the package metadata, with the LICENSE file at the root of the repository. The current release is 3.2.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mrbin99-django-vite)Was this useful?