Django static images not showing on Vercel

I'm deploying my Django project to Vercel, and everything works fine locally but after deployment, images from the static folder are not showing.

Project structure:

datafam/
  ├── settings.py
  ├── wsgi.py
static/
  └── teams/
      └── image/
          ├── Abu Sofian.webp
          ├── Crystal Andrea Dsouza.webp
templates/
  └── teams/
      └── index.html
staticfiles/          
vercel.json
requirements.txt

file vercel.json:

{
    "builds": [
        {
            "src": "datafam/wsgi.py",
            "use": "@vercel/python",
            "config": {
                "maxLambdaSize": "100mb",
                "runtime": "python3.12"
            }
        }
    ],
    "routes": [
        {
            "src": "/(.*)",
            "dest": "datafam/wsgi.py"
        }
    ]
}

What I’m trying to achieve

I just want my static images (under /static/teams/image/) to be correctly served after deploying to Vercel —
exactly the same way Django serves them locally using {% static %} in templates.

file index.html:

{% extends "base.html" %}
{% load static %}

{% block head_title %}
{{title}}
{% endblock head_title %}

{% block content %}


<section class="dark:bg-neutral-900 bg-white py-20" >
    <div class="container mx-auto px-4 text-center">
        <p class="text-4xl md:text-5xl font-extrabold dark:text-gray-100 text-gray-800">Team Us</p>
        <p class="mt-16 text-lg text-gray-600 dark:text-gray-400 max-w-4xl mx-auto">
            Meet the passionate and dedicated individuals who form the core of our community. Our team is committed to fostering a collaborative and supportive environment for all data enthusiasts.
        </p>
    </div>

    {# Mengubah container untuk menggunakan flex-wrap dan gap responsif #}
    <div class="container mx-auto flex flex-wrap justify-center gap-6 pt-10 md:pt-20 px-4"> 
        {% for member in team_members %}
            {# Menghapus logika baris kondisional, flex-wrap akan menanganinya secara otomatis #}
            <div class="border border-gray-600 dark:border-gray-200 shadow-2xs bg-[#06ABC3] w-full sm:w-80 md:w-72 lg:w-96 h-60 rounded-xl flex items-center p-4 max-w-md"> {# Mengubah lebar menjadi responsif dan lebih besar pada layar besar #}
                <div class="w-[120px] h-[120px] overflow-hidden rounded-xl dark:border-neutral-700 dark:shadow-neutral-700/70 shrink-0"> 
                    <img class="h-full w-full object-cover " src="{% static member.image %}" alt="{{ member.name }}" />
                </div>


                <div class="ml-4 grow flex flex-col justify-center"> 
                    <p class="text-[#FFE41A] text-lg font-bold text-left truncate">{{ member.name }}</p> 
                    <p class=" text-white font-semibold text-left ">{{ member.position }}</p> 
                    <div class="flex gap-x-2 mt-2"> 
                        {% if member.linkedin_url %}
                            <a href="{{ member.linkedin_url }}" target="_blank">
                                <svg class="w-8 h-8  dark:fill-[#23272F] fill-white hover:cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">{{ linkedin_path }}</svg>
                            </a>
                        {% endif %}
                        {% if member.github_url %}
                            <a href="{{ member.github_url }}" target="_blank">
                                <svg class="w-8 h-8  dark:fill-[#23272F] fill-white hover:cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">{{ github_path }}</svg>
                            </a>

                        {% endif %}
                    </div>
                </div>
            </div>
        {% endfor %}
    </div> 
</section>

<section class="dark:bg-[#181B20] bg-[#e0e0e0] py-200">
{% endblock content %}

file views.py:

from django.shortcuts import render
from django.utils.html import format_html

# Create your views here.


def index(request):
    # Definisikan HANYA tag <path> untuk LinkedIn
    linkedin_path_html = """
    <path d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32zm5 170.2l66.5 0 0 213.8-66.5 0 0-213.8zm71.7-67.7a38.5 38.5 0 1 1 -77 0 38.5 38.5 0 1 1 77 0zM317.9 416l0-104c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9l0 105.8-66.4 0 0-213.8 63.7 0 0 29.2 .9 0c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9l0 117.2-66.4 0z"/>
    """

    # Definisikan HANYA tag <path> untuk GitHub
    github_path_html = """
    <path d="M448 96c0-35.3-28.7-64-64-64L64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-320zM265.8 407.7c0-1.8 0-6 .1-11.6 .1-11.4 .1-28.8 .1-43.7 0-15.6-5.2-25.5-11.3-30.7 37-4.1 76-9.2 76-73.1 0-18.2-6.5-27.3-17.1-39 1.7-4.3 7.4-22-1.7-45-13.9-4.3-45.7 17.9-45.7 17.9-26.6-7.5-56.6-7.5-83.2 0 0 0-31.8-22.2-45.7-17.9-9.1 22.9-3.5 40.6-1.7 45-10.6 11.7-15.6 20.8-15.6 39 0 63.6 37.3 69 74.3 73.1-4.8 4.3-9.1 11.7-10.6 22.3-9.5 4.3-33.8 11.7-48.3-13.9-9.1-15.8-25.5-17.1-25.5-17.1-16.2-.2-1.1 10.2-1.1 10.2 10.8 5 18.4 24.2 18.4 24.2 9.7 29.7 56.1 19.7 56.1 19.7 0 9 .1 21.7 .1 30.6 0 4.8 .1 8.6 .1 10 0 4.3-3 9.5-11.5 8-66-22.1-112.2-84.9-112.2-158.3 0-91.8 70.2-161.5 162-161.5S388 165.6 388 257.4c.1 73.4-44.7 136.3-110.7 158.3-8.4 1.5-11.5-3.7-11.5-8zm-90.5-54.8c-.2-1.5 1.1-2.8 3-3.2 1.9-.2 3.7 .6 3.9 1.9 .3 1.3-1 2.6-3 3-1.9 .4-3.7-.4-3.9-1.7zm-9.1 3.2c-2.2 .2-3.7-.9-3.7-2.4 0-1.3 1.5-2.4 3.5-2.4 1.9-.2 3.7 .9 3.7 2.4 0 1.3-1.5 2.4-3.5 2.4zm-14.3-2.2c-1.9-.4-3.2-1.9-2.8-3.2s2.4-1.9 4.1-1.5c2 .6 3.3 2.1 2.8 3.4-.4 1.3-2.4 1.9-4.1 1.3zm-12.5-7.3c-1.5-1.3-1.9-3.2-.9-4.1 .9-1.1 2.8-.9 4.3 .6 1.3 1.3 1.8 3.3 .9 4.1-.9 1.1-2.8 .9-4.3-.6zm-8.5-10c-1.1-1.5-1.1-3.2 0-3.9 1.1-.9 2.8-.2 3.7 1.3 1.1 1.5 1.1 3.3 0 4.1-.9 .6-2.6 0-3.7-1.5zm-6.3-8.8c-1.1-1.3-1.3-2.8-.4-3.5 .9-.9 2.4-.4 3.5 .6 1.1 1.3 1.3 2.8 .4 3.5-.9 .9-2.4 .4-3.5-.6zm-6-6.4c-1.3-.6-1.9-1.7-1.5-2.6 .4-.6 1.5-.9 2.8-.4 1.3 .7 1.9 1.8 1.5 2.6-.4 .9-1.7 1.1-2.8 .4z"/>
    """

    context = {
        'title': 'Teams - DataFam',
        'subtitle': 'welcome teams',
        'join_button': 'Join Now',
        'nav': [
            ['/', 'home'],
            ['/teams', 'teams'],
            ['/story', 'story'],
        ],
        'linkedin_path': format_html(linkedin_path_html),
        'github_path': format_html(github_path_html),
        'team_members': [
            {
                'name': 'Crystal Andrea Dsouza',
                'image': 'teams/image/Crystal Andrea Dsouza.webp',
                'position': 'Founder',
                'linkedin_url': 'https://www.linkedin.com/in/crystal-andrea-dsouza-641196286/',
                'github_url': None,
            },
            {
                'name': 'Abu Sofian',
                'image': 'teams/image/Abu Sofian.webp',
                'position': 'Web Developer',
                'linkedin_url': 'https://www.linkedin.com/in/abusofianid/',
                'github_url': 'https://github.com/abusofianid',  # Contoh URL kosong
            },
            {
                'name': 'Gyanankur Baruah',
                'image': 'teams/image/Gyanankur Baruah.webp',
                'position': 'Moderator & Admin',
                'linkedin_url': 'https://www.linkedin.com/in/gyanankur-baruah-797205338/',
                'github_url': None,
            },
            {
                'name': 'Khushi Parikh',
                'image': 'teams/image/Khushi Parikh.webp',
                'position': 'Moderator & Admin',
                'linkedin_url': 'https://www.linkedin.com/in/khushi-parikh-612670299/',
                'github_url': None,
            },
            {
                'name': 'Manav Shah',
                'image': 'teams/image/Manav Shah.webp',
                'position': 'Moderator & Admin',
                'linkedin_url': 'https://www.linkedin.com/in/manavjshah/',
                'github_url': None,
            },
            {
                'name': 'Anushka Kadu',
                'image': 'teams/image/Anushka Kadu.webp',
                'position': 'Moderator & Admin',
                'linkedin_url': 'https://www.linkedin.com/in/anushka-kadu-a711b4242/',
                'github_url': None,
            },
            {
                'name': 'Varun S Gowda',
                'image': 'teams/image/Varun S Gowda.webp',
                'position': 'Moderator & Admin',
                'linkedin_url': 'https://www.linkedin.com/in/varun-s-b4842b29b/',
                'github_url': None,
            },
        ]
    }
    return render(request, 'teams/index.html', context)

Environment

  • Python 3.12

  • Django 5.x

How can I make Django serve static images correctly on Vercel?

Vercel doesn’t automatically serve Django static files — it only runs your app as a serverless function.
To display static images (like those under /static/), you need to either serve them as static assets or use a CDN/static hosting solution.

Option 1 — Let Vercel serve static files directly

Add this to your vercel.json before the Python build:

{
  "builds": [
    {
      "src": "datafam/wsgi.py",
      "use": "@vercel/python",
      "config": {
        "maxLambdaSize": "100mb",
        "runtime": "python3.12"
      }
    },
    {
      "src": "static/**/*",
      "use": "@vercel/static"
    }
  ],
  "routes": [
    { "src": "/static/(.*)", "dest": "/static/$1" },
    { "src": "/(.*)", "dest": "datafam/wsgi.py" }
  ]
}

In your Django settings.py:

DEBUG = False
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

Then run:

python manage.py collectstatic

Commit the generated static files (/static or /staticfiles) to your repo.
Vercel will then serve them automatically.

Option 2 — Use an external static host

Upload static assets to Cloudinary, AWS S3, or Google Cloud Storage, and set in settings.py:

STATIC_URL = 'https://cdn.yourdomain.com/static/'

This is cleaner and more production-ready.

Why this happens:

Vercel runs Django in a serverless environment,
so there’s no persistent filesystem or collectstatic stage during deployment.
That’s why Django can’t serve static files like it does locally with runserver.

Use whitenoise to serve static files from a local folder. then in settings make it like this:

STATIC_ROOT = BASE_DIR / "staticfiles"

Images(static files) which work locally don't serve in production. since in production the Django development server is not used for serving static files instead use external source like cloudinary, S3, cloud storage.

And point DEFAULT_FILE_STORAGE to external url(whichever you use)

because locally Django serves from

DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'

and remember to run the collectstatic command, which gathers all static files from your app's static/ directories and any directories in STATICFILES_DIRS and copies them to the directory specified by the STATIC_ROOT setting.

Вернуться на верх