Static files (CSS, JavaScript, images, fonts) are essential components of modern web applications. Django provides a robust system for managing, serving, and optimizing static files across development and production environments.
Static files are assets that don't change based on user requests or application state:
Static Files:
Media Files:
# settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/' # URL prefix for static files
# Additional locations of static files
STATICFILES_DIRS = [
BASE_DIR / 'static', # Project-level static files
BASE_DIR / 'assets', # Alternative static directory
]
# Directory where collected static files will be stored
STATIC_ROOT = BASE_DIR / 'staticfiles' # For production
# Static files finders
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]
myproject/
├── static/ # Project-level static files
│ ├── css/
│ │ ├── main.css
│ │ ├── bootstrap.min.css
│ │ └── components/
│ │ ├── navbar.css
│ │ └── footer.css
│ ├── js/
│ │ ├── main.js
│ │ ├── jquery.min.js
│ │ └── components/
│ │ ├── carousel.js
│ │ └── modal.js
│ ├── images/
│ │ ├── logo.png
│ │ ├── favicon.ico
│ │ └── backgrounds/
│ │ ├── hero.jpg
│ │ └── pattern.png
│ └── fonts/
│ ├── custom-font.woff2
│ └── icons.ttf
├── blog/
│ └── static/
│ └── blog/ # App-specific static files
│ ├── css/
│ │ └── blog.css
│ ├── js/
│ │ └── blog.js
│ └── images/
│ └── blog-icon.png
└── staticfiles/ # Collected static files (production)
<!-- Load static tag at the top of templates -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>My Django Site</title>
<!-- CSS files -->
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/blog.css' %}">
<!-- Favicon -->
<link rel="icon" href="{% static 'images/favicon.ico' %}">
</head>
<body>
<!-- Content here -->
<!-- JavaScript files -->
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/main.js' %}"></script>
</body>
</html>
CSS Stylesheets:
{% load static %}
<!-- Main stylesheet -->
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<!-- App-specific styles -->
<link rel="stylesheet" href="{% static 'blog/css/blog.css' %}">
<!-- Conditional CSS -->
{% if user.is_authenticated %}
<link rel="stylesheet" href="{% static 'css/authenticated.css' %}">
{% endif %}
<!-- Media queries -->
<link rel="stylesheet" href="{% static 'css/mobile.css' %}" media="(max-width: 768px)">
<link rel="stylesheet" href="{% static 'css/print.css' %}" media="print">
JavaScript Files:
{% load static %}
<!-- Core JavaScript -->
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/main.js' %}"></script>
<!-- Conditional JavaScript -->
{% if debug %}
<script src="{% static 'js/debug.js' %}"></script>
{% endif %}
<!-- Async loading -->
<script src="{% static 'js/analytics.js' %}" async></script>
<!-- Deferred loading -->
<script src="{% static 'js/non-critical.js' %}" defer></script>
Images:
{% load static %}
<!-- Logo -->
<img src="{% static 'images/logo.png' %}" alt="Company Logo" class="logo">
<!-- Background images in CSS -->
<div class="hero" style="background-image: url('{% static 'images/hero-bg.jpg' %}');">
<h1>Welcome</h1>
</div>
<!-- Responsive images -->
<img src="{% static 'images/hero-mobile.jpg' %}"
srcset="{% static 'images/hero-mobile.jpg' %} 480w,
{% static 'images/hero-tablet.jpg' %} 768w,
{% static 'images/hero-desktop.jpg' %} 1200w"
sizes="(max-width: 480px) 480px,
(max-width: 768px) 768px,
1200px"
alt="Hero Image">
<!-- SVG icons -->
<img src="{% static 'images/icons/search.svg' %}" alt="Search" class="icon">
Dynamic Static File Paths:
{% load static %}
<!-- Using variables -->
{% with icon_name='user-profile' %}
<img src="{% static 'images/icons/'|add:icon_name|add:'.svg' %}" alt="User Profile">
{% endwith %}
<!-- Loop through static files -->
{% for social in social_networks %}
<a href="{{ social.url }}">
<img src="{% static 'images/social/'|add:social.name|add:'.png' %}"
alt="{{ social.name }}">
</a>
{% endfor %}
Inline CSS with Static URLs:
{% load static %}
<style>
.custom-bg {
background-image: url('{% static "images/pattern.png" %}');
background-repeat: repeat;
}
.logo::before {
content: url('{% static "images/logo-small.svg" %}');
}
</style>
Main CSS Structure:
/* static/css/main.css */
/* CSS Reset/Normalize */
@import url('normalize.css');
/* Base styles */
@import url('base.css');
/* Layout components */
@import url('components/header.css');
@import url('components/navigation.css');
@import url('components/footer.css');
/* Utility classes */
@import url('utilities.css');
/* Responsive styles */
@import url('responsive.css');
Component-Based CSS:
/* static/css/components/card.css */
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-header {
border-bottom: 1px solid #eee;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.card-title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
.card-body {
line-height: 1.6;
}
.card-footer {
border-top: 1px solid #eee;
padding-top: 0.5rem;
margin-top: 1rem;
text-align: right;
}
CSS Variables and Custom Properties:
/* static/css/variables.css */
:root {
/* Colors */
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
/* Typography */
--font-family-base: 'Helvetica Neue', Arial, sans-serif;
--font-size-base: 1rem;
--line-height-base: 1.5;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 3rem;
/* Breakpoints */
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
}
/* Usage */
.btn-primary {
background-color: var(--primary-color);
padding: var(--spacing-sm) var(--spacing-md);
font-family: var(--font-family-base);
}
/* static/css/responsive.css */
/* Mobile First Approach */
.container {
width: 100%;
padding: 0 var(--spacing-md);
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
/* Tablet */
@media (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
.grid {
grid-template-columns: repeat(2, 1fr);
}
.grid-3 {
grid-template-columns: repeat(3, 1fr);
}
}
/* Desktop */
@media (min-width: 992px) {
.container {
max-width: 970px;
}
.grid {
grid-template-columns: repeat(3, 1fr);
}
.grid-4 {
grid-template-columns: repeat(4, 1fr);
}
}
/* Large Desktop */
@media (min-width: 1200px) {
.container {
max-width: 1170px;
}
}
// static/js/main.js
// Namespace for application
window.MyApp = window.MyApp || {};
// Utility functions
MyApp.utils = {
// CSRF token helper
getCsrfToken: function() {
return document.querySelector('[name=csrfmiddlewaretoken]').value;
},
// AJAX helper
ajax: function(url, options) {
const defaults = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCsrfToken()
}
};
return fetch(url, Object.assign(defaults, options));
},
// DOM ready helper
ready: function(fn) {
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
};
// Initialize application
MyApp.utils.ready(function() {
console.log('Application initialized');
// Initialize components
MyApp.navigation.init();
MyApp.forms.init();
});
Component-Based JavaScript:
// static/js/components/navigation.js
MyApp.navigation = {
init: function() {
this.bindEvents();
this.setActiveNavItem();
},
bindEvents: function() {
// Mobile menu toggle
const mobileToggle = document.querySelector('.mobile-menu-toggle');
const mobileMenu = document.querySelector('.mobile-menu');
if (mobileToggle && mobileMenu) {
mobileToggle.addEventListener('click', function() {
mobileMenu.classList.toggle('active');
});
}
// Dropdown menus
const dropdowns = document.querySelectorAll('.dropdown');
dropdowns.forEach(function(dropdown) {
const toggle = dropdown.querySelector('.dropdown-toggle');
const menu = dropdown.querySelector('.dropdown-menu');
toggle.addEventListener('click', function(e) {
e.preventDefault();
menu.classList.toggle('show');
});
});
},
setActiveNavItem: function() {
const currentPath = window.location.pathname;
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(function(link) {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
}
};
// static/js/components/forms.js
MyApp.forms = {
init: function() {
this.bindAjaxForms();
this.bindFormValidation();
},
bindAjaxForms: function() {
const ajaxForms = document.querySelectorAll('.ajax-form');
ajaxForms.forEach(function(form) {
form.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(form);
const url = form.action || window.location.href;
MyApp.utils.ajax(url, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
MyApp.forms.showMessage('Success!', 'success');
form.reset();
} else {
MyApp.forms.showErrors(data.errors);
}
})
.catch(error => {
MyApp.forms.showMessage('An error occurred', 'error');
});
});
});
},
showMessage: function(message, type) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type}`;
alertDiv.textContent = message;
const container = document.querySelector('.messages-container');
if (container) {
container.appendChild(alertDiv);
// Auto-remove after 5 seconds
setTimeout(function() {
alertDiv.remove();
}, 5000);
}
},
showErrors: function(errors) {
Object.keys(errors).forEach(function(field) {
const fieldElement = document.querySelector(`[name="${field}"]`);
if (fieldElement) {
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error';
errorDiv.textContent = errors[field][0];
fieldElement.parentNode.appendChild(errorDiv);
}
});
}
};
{% load static %}
<!-- Responsive image with multiple sources -->
<picture>
<source media="(max-width: 480px)"
srcset="{% static 'images/hero-mobile.webp' %} 1x,
{% static 'images/hero-mobile@2x.webp' %} 2x">
<source media="(max-width: 768px)"
srcset="{% static 'images/hero-tablet.webp' %} 1x,
{% static 'images/hero-tablet@2x.webp' %} 2x">
<source srcset="{% static 'images/hero-desktop.webp' %} 1x,
{% static 'images/hero-desktop@2x.webp' %} 2x">
<img src="{% static 'images/hero-desktop.jpg' %}"
alt="Hero Image"
class="hero-image">
</picture>
<!-- Simple responsive image -->
<img src="{% static 'images/product.jpg' %}"
srcset="{% static 'images/product-480.jpg' %} 480w,
{% static 'images/product-768.jpg' %} 768w,
{% static 'images/product-1200.jpg' %} 1200w"
sizes="(max-width: 480px) 480px,
(max-width: 768px) 768px,
1200px"
alt="Product Image"
loading="lazy">
/* Responsive background images */
.hero-section {
background-image: url('../images/hero-mobile.jpg');
background-size: cover;
background-position: center;
min-height: 300px;
}
@media (min-width: 768px) {
.hero-section {
background-image: url('../images/hero-tablet.jpg');
min-height: 400px;
}
}
@media (min-width: 1200px) {
.hero-section {
background-image: url('../images/hero-desktop.jpg');
min-height: 500px;
}
}
/* High DPI displays */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.hero-section {
background-image: url('../images/hero-desktop@2x.jpg');
}
}
/* static/css/fonts.css */
/* Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
/* Custom fonts */
@font-face {
font-family: 'CustomFont';
src: url('../fonts/custom-font.woff2') format('woff2'),
url('../fonts/custom-font.woff') format('woff'),
url('../fonts/custom-font.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Font usage */
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.custom-heading {
font-family: 'CustomFont', serif;
}
{% load static %}
<!-- Font Awesome -->
<link rel="stylesheet" href="{% static 'css/fontawesome.min.css' %}">
<!-- Custom icon font -->
<link rel="stylesheet" href="{% static 'fonts/icons.css' %}">
<!-- Usage -->
<i class="fas fa-user"></i>
<i class="icon-custom-search"></i>
# settings/development.py
# Serve static files during development
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
# Debug mode serves static files automatically
DEBUG = True
# Additional static file directories for development
STATICFILES_DIRS = [
BASE_DIR / 'static',
BASE_DIR / 'node_modules', # For npm packages
]
# settings/production.py
# Optimized static file storage
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# CDN integration
STATIC_URL = 'https://cdn.example.com/static/'
# Compressed static files
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Collect all static files for production
python manage.py collectstatic
# Collect with no input prompts
python manage.py collectstatic --noinput
# Clear existing files first
python manage.py collectstatic --clear --noinput
# settings.py with WhiteNoise
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # Add this
# ... other middleware
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# WhiteNoise settings
WHITENOISE_USE_FINDERS = True
WHITENOISE_AUTOREFRESH = True # Development only
WHITENOISE_MAX_AGE = 31536000 # 1 year cache
{% load static %}
<!-- Development -->
{% if debug %}
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/main.js' %}"></script>
{% else %}
<!-- Production - minified files -->
<link rel="stylesheet" href="{% static 'css/main.min.css' %}">
<script src="{% static 'js/main.min.js' %}"></script>
{% endif %}
# settings.py
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# Custom storage with longer cache times
class CustomStaticFilesStorage(ManifestStaticFilesStorage):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.max_age = 31536000 # 1 year
Django's static files system provides powerful tools for managing assets efficiently across development and production environments. Proper organization, optimization, and caching strategies ensure fast-loading, maintainable web applications.
Built-in Template Tags and Filters
Django provides a comprehensive set of built-in template tags and filters that handle common presentation tasks. This guide covers all major built-in functionality with practical examples and use cases.
Working with Media Files
Media files are user-uploaded content that changes dynamically based on user interactions. Unlike static files, media files are not part of your application code and require special handling for security, storage, and processing.