Chapter 4: Django – Global Static Files
Global static files — the CSS, JavaScript, images, favicons, fonts, etc. that should appear on every single page of your app.
Most beginners start by putting everything inside one app’s static/ folder (e.g. polls/static/polls/css/style.css) — which works… until you add a second app, or want a site-wide footer logo, or a global navbar style, or a favicon that works everywhere.
Today we’re going to fix that forever — step by step — like I’m sitting next to you, creating folders, writing files, editing settings, and testing together.
We will make your whole site (not just /polls/) look beautiful, consistent, and modern using project-level global static files.
Step 1 – Understand the Two Kinds of Static Files
| Type | Location example | Purpose / When to use | Served from (dev) | Served from (prod) |
|---|---|---|---|---|
| App-specific | polls/static/polls/css/polls.css | Styles / JS / images only relevant to polls pages | app’s static/ folder | collectstatic → STATIC_ROOT |
| Global / Project-level | static/css/global.css | Site-wide: base layout, fonts, navbar, footer, logo, favicon | STATICFILES_DIRS | collectstatic → STATIC_ROOT |
Golden rule 2026:
- Use app-specific for things that only make sense in one app (polls/static/polls/css/vote-button.css)
- Use global/project-level for everything that appears on every page (static/css/base-layout.css, static/images/logo.png, static/js/site-analytics.js)
Step 2 – Create the Global Static Folder (If Not Already Done)
In project root (next to manage.py):
|
0 1 2 3 4 5 6 7 8 9 10 |
mkdir -p static/css mkdir -p static/js mkdir -p static/images mkdir -p static/fonts # optional – custom fonts later mkdir -p static/icons # favicons, apple-touch-icon, etc. |
Now your structure looks like:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
mysite-project/ ├── manage.py ├── static/ ← GLOBAL static files (new!) │ ├── css/ │ │ └── global.css │ ├── js/ │ │ └── site.js │ ├── images/ │ │ └── logo.png │ └── icons/ │ └── favicon.ico ├── polls/ │ └── static/ │ └── polls/ ← app-specific │ ├── css/ │ └── images/ └── ... |
Step 3 – Tell Django Where to Find Global Static Files
Open mysite/settings.py
Add or update:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # ── Static files settings ──────────────────────────────────────────────────── STATIC_URL = '/static/' # Where Django looks for static files during development (besides app folders) STATICFILES_DIRS = [ BASE_DIR / 'static', # ← this line adds your global folder ] # Where collectstatic puts everything in production STATIC_ROOT = BASE_DIR / 'staticfiles' # Recommended: compression + cache-busting filenames (with WhiteNoise) STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' |
Important — STATICFILES_DIRS is the key line that makes Django search your static/ folder.
Step 4 – Create Real Global Static Files
1. Global CSS – static/css/global.css
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
/* Site-wide base styles – every page inherits these */ :root { --primary: #006400; /* dark green – Telangana / Hyderabad feel */ --primary-dark: #004d00; --light-bg: #f8f9fa; --text: #2d2d2d; --gray: #6c757d; --danger: #dc3545; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; background: var(--light-bg); color: var(--text); line-height: 1.6; } .container { max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; } header { background: var(--primary); color: white; padding: 1.5rem 0; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.15); } header img { height: 50px; vertical-align: middle; margin-right: 1rem; } .btn { display: inline-block; padding: 0.8rem 1.6rem; background: var(--primary); color: white; border-radius: 6px; text-decoration: none; font-weight: 500; transition: background 0.2s; } .btn:hover { background: var(--primary-dark); } .card { background: white; padding: 1.8rem; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); margin-bottom: 1.5rem; } |
2. Global JavaScript – static/js/site.js
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Site-wide JavaScript – runs on every page document.addEventListener("DOMContentLoaded", function() { console.log("Global site.js loaded – ready for action! 🚀"); // Example: smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener("click", function(e) { e.preventDefault(); document.querySelector(this.getAttribute("href")).scrollIntoView({ behavior: "smooth" }); }); }); }); |
3. Logo & Favicon – static/images/logo.png and static/icons/favicon.ico
(You can download any small logo PNG and favicon ICO or use placeholder tools online)
Step 5 – Link Everything in base.html
In templates/base.html:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{% block title %}Hyderabad Polls{% endblock %}</title> <!-- Favicon --> <link rel="icon" href="{% static 'icons/favicon.ico' %}" type="image/x-icon"> <!-- Global CSS --> <link rel="stylesheet" href="{% static 'css/global.css' %}"> <!-- App-specific CSS (optional conditional) --> {% if request.resolver_match.app_name == 'polls' %} <link rel="stylesheet" href="{% static 'polls/css/polls.css' %}"> {% endif %} <!-- Page-specific CSS --> {% block extra_css %}{% endblock %} </head> <body> <header class="container"> <img src="{% static 'images/logo.png' %}" alt="Polls Logo" height="50"> <h1>Hyderabad Polls 2026</h1> </header> <main class="container"> {% block content %}{% endblock %} </main> <!-- Global JS --> <script src="{% static 'js/site.js' %}"></script> <!-- Page-specific JS --> {% block extra_js %}{% endblock %} </body> </html> |
Step 6 – Test It All
- Save everything
- Restart server if needed: python manage.py runserver
- Open browser → visit /polls/ or /
- Check:
- Logo appears in header
- Global styles applied (background, fonts, button look)
- Favicon in browser tab
- Console → “Global site.js loaded…” message
- Dev tools → Network tab → all static files 200 OK
If 404:
- Forgot {% load static %} → add at top of template
- Wrong path → check exactly static/css/global.css (no typos)
- Server not restarted after adding files
Step 7 – Production (When You Deploy)
Run once before deploy:
|
0 1 2 3 4 5 6 |
python manage.py collectstatic --noinput |
→ All files copied to staticfiles/
Then:
- DEBUG = False
- ALLOWED_HOSTS correct
- WhiteNoise middleware + storage set (see previous lesson)
- Deploy → static files served automatically
Your Quick Task Right Now
- Create static/css/global.css with the code above
- Add {% load static %} + CSS link + logo to base.html
- Restart server → check if header looks nicer
- Add a small JS file → include it → see console message
- (Optional) Add favicon → refresh → see browser tab icon
Tell me what feels next:
- “Done! Now show me how to use Bootstrap 5 / Tailwind locally or CDN”
- “How to organize fonts / custom icons / favicons properly?”
- “What happens if I have conflicting filenames (global vs app-specific)?”
- “Got 404 / style not applying – here’s error/screenshot”
- Or finally ready for Django Forms + Voting + POST + F() + results page
You now have global static files working beautifully — your entire site now shares one consistent look.
You’re doing really well — let’s keep building! 🚀🇮🇳
