├── app ├── __init__.py ├── migrations │ └── __init__.py ├── models.py ├── admin.py ├── tests.py ├── apps.py ├── urls.py ├── views.py ├── static │ └── src │ │ ├── main.ts │ │ └── pages │ │ ├── About.vue │ │ └── Index.vue └── templates │ └── index.html ├── inertia_django_vite_vue_minimal ├── __init__.py ├── asgi.py ├── wsgi.py ├── urls.py └── settings.py ├── requirements.txt ├── .vscode ├── extensions.json └── settings.json ├── package.json ├── vite.config.ts ├── manage.py ├── README.md └── .gitignore /app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inertia_django_vite_vue_minimal/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==4.2.8 2 | django-vite==3.0.1 3 | inertia-django==0.5.3 4 | pip-autoremove==0.10.0 5 | whitenoise==6.6.0 -------------------------------------------------------------------------------- /app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AppConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "app" 7 | -------------------------------------------------------------------------------- /app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("", views.index, name="index"), 7 | path("about", views.about, name="about"), 8 | ] 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "batisteo.vscode-django", 4 | "esbenp.prettier-vscode", 5 | "hookyqr.beautify", 6 | "ms-python.vscode-pylance", 7 | "vue.volar" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /app/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpRequest 2 | from inertia import render 3 | 4 | 5 | def index(request): 6 | return render(request, "Index", props={"name": "World"}) 7 | 8 | 9 | def about(request): 10 | return render(request, "About", props={"pageName": "About"}) 11 | -------------------------------------------------------------------------------- /app/static/src/main.ts: -------------------------------------------------------------------------------- 1 | import "vite/modulepreload-polyfill"; 2 | import { createApp, h } from "vue"; 3 | import { createInertiaApp } from "@inertiajs/vue3"; 4 | 5 | createInertiaApp({ 6 | resolve: (name) => import(`./pages/${name}.vue`), 7 | setup({ el, App, props, plugin }) { 8 | createApp({ render: () => h(App, props) }) 9 | .use(plugin) 10 | .mount(el); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inertia-django-vite-vue-minimal", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "description": "A minimal, working template for Inertia + Django + Vite + Vue.", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build" 9 | }, 10 | "dependencies": { 11 | "@inertiajs/vue3": "^1.0.14", 12 | "@types/node": "^20.10.5", 13 | "@vitejs/plugin-vue": "^5.0.0", 14 | "vite": "^5.0.10", 15 | "vue": "^3.4.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | {% load django_vite %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% vite_hmr_client %} 11 | {% vite_asset 'main.ts' %} 12 | 13 | Inertia + Django + Vite + Vue minimal 14 | 15 | 16 | 17 | {% block inertia %}{% endblock %} 18 | 19 | 20 | -------------------------------------------------------------------------------- /inertia_django_vite_vue_minimal/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for inertia_django_vite_vue_minimal project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault( 15 | "DJANGO_SETTINGS_MODULE", "inertia_django_vite_vue_minimal.settings" 16 | ) 17 | 18 | application = get_asgi_application() 19 | -------------------------------------------------------------------------------- /inertia_django_vite_vue_minimal/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for inertia_django_vite_vue_minimal project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault( 15 | "DJANGO_SETTINGS_MODULE", "inertia_django_vite_vue_minimal.settings" 16 | ) 17 | 18 | application = get_wsgi_application() 19 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "path"; 3 | import vue from "@vitejs/plugin-vue"; 4 | 5 | export default defineConfig({ 6 | root: resolve("./app/static/src"), 7 | base: "/static/", 8 | plugins: [vue()], 9 | build: { 10 | outDir: resolve("./app/static/dist"), 11 | assetsDir: "", 12 | manifest: true, 13 | emptyOutDir: true, 14 | rollupOptions: { 15 | // Overwrite default .html entry to main.ts in the static directory 16 | input: resolve("./app/static/src/main.ts"), 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /app/static/src/pages/About.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /app/static/src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault( 10 | "DJANGO_SETTINGS_MODULE", "inertia_django_vite_vue_minimal.settings" 11 | ) 12 | try: 13 | from django.core.management import execute_from_command_line 14 | except ImportError as exc: 15 | raise ImportError( 16 | "Couldn't import Django. Are you sure it's installed and " 17 | "available on your PYTHONPATH environment variable? Did you " 18 | "forget to activate a virtual environment?" 19 | ) from exc 20 | execute_from_command_line(sys.argv) 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /inertia_django_vite_vue_minimal/urls.py: -------------------------------------------------------------------------------- 1 | """inertia_django_vite_vue_minimal URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | 19 | urlpatterns = [ 20 | path("admin/", admin.site.urls), 21 | path("", include("app.urls")), 22 | ] 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "beautify.language": { 3 | "html": ["django-html"] 4 | }, 5 | "emmet.includeLanguages": { 6 | "django-html": "html" 7 | }, 8 | "files.associations": { 9 | "**/templates/*.html": "django-html", 10 | "**/*.html": "html", 11 | "**/requirements{/**,*}.{txt,in}": "pip-requirements" 12 | }, 13 | "python.analysis.completeFunctionParens": true, 14 | "python.analysis.typeCheckingMode": "basic", 15 | "python.formatting.provider": "black", 16 | "python.languageServer": "Pylance", 17 | "[django-html]": { 18 | "editor.defaultFormatter": "HookyQR.beautify", 19 | "editor.tabSize": 2 20 | }, 21 | "[javascript]": { 22 | "editor.defaultFormatter": "esbenp.prettier-vscode" 23 | }, 24 | "[json]": { 25 | "editor.defaultFormatter": "esbenp.prettier-vscode" 26 | }, 27 | "[jsonc]": { 28 | "editor.defaultFormatter": "esbenp.prettier-vscode" 29 | }, 30 | "[typescript]": { 31 | "editor.defaultFormatter": "esbenp.prettier-vscode" 32 | }, 33 | "[vue]": { 34 | "editor.defaultFormatter": "esbenp.prettier-vscode" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inertia + Django + Vite + Vue minimal template 2 | 3 | A minimal, working template for Inertia + Django + Vite + Vue. 4 | 5 | ## Technologies 6 | 7 | 1. Inertia - powered by the official [Inertia.js Django Adapter](https://github.com/inertiajs/inertia-django) 8 | 2. Django v4.2 9 | 3. Vite 5 - powered by [Django Vite](https://github.com/MrBin99/django-vite) 10 | 4. Vue 3 11 | 5. TypeScript 12 | 6. [WhiteNoise](https://whitenoise.evans.io/en/stable/index.html) - to serve static files 13 | 14 | ## How to install & run 15 | 16 | 1. Download the repo. You can either: 17 | 18 | a. Clone the repo (without the git history): 19 | 20 | ```sh 21 | npx degit https://github.com/mujahidfa/inertia-django-vite-vue-minimal 22 | ``` 23 | 24 | b. Or, create a repo based on this template via the [GitHub template generator](https://github.com/mujahidfa/inertia-django-vite-vue-minimal/generate). 25 | 26 | 2. Install required Python packages. 27 | 28 | ```sh 29 | # Create and activate a virtual environment 30 | python3 -m venv .venv 31 | source .venv/bin/activate 32 | 33 | # Install required Python packages 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | 3. Install required Node.js packages. 38 | 39 | ```sh 40 | npm install 41 | ``` 42 | 43 | 4. Run the Vite dev server: 44 | 45 | ```sh 46 | npm run dev 47 | ``` 48 | 49 | 5. Run Django's default migrations: 50 | 51 | ```sh 52 | python manage.py migrate 53 | ``` 54 | 55 | 6. Run the Django dev server (in a separate terminal): 56 | 57 | ```sh 58 | python manage.py runserver 59 | ``` 60 | 61 | ## How to build for production 62 | 63 | 1. Set `DEBUG=False` in [settings.py](./inertia_django_vite_vue_minimal/settings.py). 64 | 65 | ```py 66 | # In settings.py 67 | ... 68 | DEBUG=False 69 | ... 70 | ``` 71 | 72 | 2. Build the JS/assets for production: 73 | 74 | ```sh 75 | npm run build 76 | ``` 77 | 78 | 3. Run `collectstatic`: 79 | 80 | ```sh 81 | rm -rf staticfiles/ 82 | python manage.py collectstatic 83 | ``` 84 | 85 | 4. Run the Django server: 86 | 87 | ```sh 88 | python manage.py runserver 89 | ``` 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Django # 2 | *.log 3 | *.pot 4 | *.pyc 5 | __pycache__ 6 | db.sqlite3 7 | media 8 | staticfiles/ 9 | 10 | # Backup files # 11 | *.bak 12 | 13 | # If you are using PyCharm # 14 | # User-specific stuff 15 | .idea/**/workspace.xml 16 | .idea/**/tasks.xml 17 | .idea/**/usage.statistics.xml 18 | .idea/**/dictionaries 19 | .idea/**/shelf 20 | 21 | # AWS User-specific 22 | .idea/**/aws.xml 23 | 24 | # Generated files 25 | .idea/**/contentModel.xml 26 | 27 | # Sensitive or high-churn files 28 | .idea/**/dataSources/ 29 | .idea/**/dataSources.ids 30 | .idea/**/dataSources.local.xml 31 | .idea/**/sqlDataSources.xml 32 | .idea/**/dynamic.xml 33 | .idea/**/uiDesigner.xml 34 | .idea/**/dbnavigator.xml 35 | 36 | # Gradle 37 | .idea/**/gradle.xml 38 | .idea/**/libraries 39 | 40 | # File-based project format 41 | *.iws 42 | 43 | # IntelliJ 44 | out/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Python # 50 | *.py[cod] 51 | *$py.class 52 | 53 | # Distribution / packaging 54 | .Python build/ 55 | develop-eggs/ 56 | dist/ 57 | downloads/ 58 | eggs/ 59 | .eggs/ 60 | lib/ 61 | lib64/ 62 | parts/ 63 | sdist/ 64 | var/ 65 | wheels/ 66 | *.egg-info/ 67 | .installed.cfg 68 | *.egg 69 | *.manifest 70 | *.spec 71 | 72 | # Installer logs 73 | pip-log.txt 74 | pip-delete-this-directory.txt 75 | 76 | # Unit test / coverage reports 77 | htmlcov/ 78 | .tox/ 79 | .coverage 80 | .coverage.* 81 | .cache 82 | .pytest_cache/ 83 | nosetests.xml 84 | coverage.xml 85 | *.cover 86 | .hypothesis/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # celery 95 | celerybeat-schedule.* 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # mkdocs documentation 110 | /site 111 | 112 | # mypy 113 | .mypy_cache/ 114 | 115 | # Sublime Text # 116 | *.tmlanguage.cache 117 | *.tmPreferences.cache 118 | *.stTheme.cache 119 | *.sublime-workspace 120 | *.sublime-project 121 | 122 | # sftp configuration file 123 | sftp-config.json 124 | 125 | # Package control specific files Package 126 | Control.last-run 127 | Control.ca-list 128 | Control.ca-bundle 129 | Control.system-ca-bundle 130 | GitHub.sublime-settings 131 | 132 | .history 133 | 134 | # Logs 135 | logs 136 | *.log 137 | npm-debug.log* 138 | yarn-debug.log* 139 | yarn-error.log* 140 | pnpm-debug.log* 141 | lerna-debug.log* 142 | 143 | node_modules 144 | .DS_Store 145 | dist 146 | dist-ssr 147 | coverage 148 | *.local 149 | 150 | /cypress/videos/ 151 | /cypress/screenshots/ 152 | 153 | # Editor directories and files 154 | .idea 155 | *.suo 156 | *.ntvs* 157 | *.njsproj 158 | *.sln 159 | *.sw? 160 | -------------------------------------------------------------------------------- /inertia_django_vite_vue_minimal/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for inertia_django_vite_vue_minimal project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.1.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import re 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = "django-insecure-0-s3nzb$^1yk(!-_e553a!&b(74^d1rg()vziok)h33!oz)mhv" 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = ["*"] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | "whitenoise.runserver_nostatic", 36 | "django.contrib.admin", 37 | "django.contrib.auth", 38 | "django.contrib.contenttypes", 39 | "django.contrib.sessions", 40 | "django.contrib.messages", 41 | "django.contrib.staticfiles", 42 | "django_vite", 43 | "inertia", 44 | "app", 45 | ] 46 | 47 | MIDDLEWARE = [ 48 | "django.middleware.security.SecurityMiddleware", 49 | "whitenoise.middleware.WhiteNoiseMiddleware", 50 | "django.contrib.sessions.middleware.SessionMiddleware", 51 | "django.middleware.common.CommonMiddleware", 52 | "django.middleware.csrf.CsrfViewMiddleware", 53 | "django.contrib.auth.middleware.AuthenticationMiddleware", 54 | "django.contrib.messages.middleware.MessageMiddleware", 55 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 56 | "inertia.middleware.InertiaMiddleware", 57 | ] 58 | 59 | ROOT_URLCONF = "inertia_django_vite_vue_minimal.urls" 60 | 61 | TEMPLATES = [ 62 | { 63 | "BACKEND": "django.template.backends.django.DjangoTemplates", 64 | "DIRS": [], 65 | "APP_DIRS": True, 66 | "OPTIONS": { 67 | "context_processors": [ 68 | "django.template.context_processors.debug", 69 | "django.template.context_processors.request", 70 | "django.contrib.auth.context_processors.auth", 71 | "django.contrib.messages.context_processors.messages", 72 | ], 73 | }, 74 | }, 75 | ] 76 | 77 | WSGI_APPLICATION = "inertia_django_vite_vue_minimal.wsgi.application" 78 | 79 | 80 | # Database 81 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases 82 | 83 | DATABASES = { 84 | "default": { 85 | "ENGINE": "django.db.backends.sqlite3", 86 | "NAME": BASE_DIR / "db.sqlite3", 87 | } 88 | } 89 | 90 | 91 | # Password validation 92 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators 93 | 94 | AUTH_PASSWORD_VALIDATORS = [ 95 | { 96 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 97 | }, 98 | { 99 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 100 | }, 101 | { 102 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 103 | }, 104 | { 105 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 106 | }, 107 | ] 108 | 109 | 110 | # Internationalization 111 | # https://docs.djangoproject.com/en/4.1/topics/i18n/ 112 | 113 | LANGUAGE_CODE = "en-us" 114 | 115 | TIME_ZONE = "UTC" 116 | 117 | USE_I18N = True 118 | 119 | USE_TZ = True 120 | 121 | 122 | # Default primary key field type 123 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field 124 | 125 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 126 | 127 | 128 | # django-vite settings 129 | # https://github.com/MrBin99/django-vite 130 | DJANGO_VITE_DEV_MODE = DEBUG # follow Django's dev mode 131 | 132 | # Where ViteJS assets are built. 133 | DJANGO_VITE_ASSETS_PATH = BASE_DIR / "app" / "static" / "dist" 134 | 135 | # If use HMR or not. We follow Django's DEBUG mode 136 | DJANGO_VITE_DEV_MODE = DEBUG 137 | 138 | # Vite 3 defaults to 5173. Default for django-vite is 3000, which is the default for Vite 2. 139 | DJANGO_VITE_DEV_SERVER_PORT = 5173 140 | 141 | 142 | # Static files (CSS, JavaScript, Images) 143 | # https://docs.djangoproject.com/en/4.1/howto/static-files/ 144 | 145 | STATIC_URL = "static/" 146 | 147 | # Output directory for collectstatic to put all your static files into. 148 | STATIC_ROOT = BASE_DIR / "staticfiles" 149 | 150 | # Include DJANGO_VITE_ASSETS_PATH into STATICFILES_DIRS to be copied inside 151 | # when run command python manage.py collectstatic 152 | STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH] 153 | 154 | # Inertia settings 155 | INERTIA_LAYOUT = BASE_DIR / "app" / "templates/index.html" 156 | 157 | # Vite generates files with 8 hash digits 158 | # http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_IMMUTABLE_FILE_TEST 159 | def immutable_file_test(path, url): 160 | # Match filename with 12 hex digits before the extension 161 | # e.g. app.db8f2edc0c8a.js 162 | return re.match(r"^.+\.[0-9a-f]{8,12}\..+$", url) 163 | 164 | 165 | WHITENOISE_IMMUTABLE_FILE_TEST = immutable_file_test 166 | --------------------------------------------------------------------------------