Objectives
In this tutorial, I will talk about the two solutions to add Favicon
to your Django project.
Favicon
A favicon, also known as a shortcut icon, website icon, tab icon, URL icon, or bookmark icon, is a file containing one or more small icons,[1] associated with a particular website or web page
Many people know favicon
because they can see them on the tab of the browsers, or the mobile screen.
However, Favicon
is not good enough, and some company also created some private standards (as many developers do not even know).
Apple touch icon
Apple touch icon: Similar to the Favicon, the Apple touch icon or apple-touch-icon.png is a file used for a web page icon on the Apple iPhone, iPod Touch, and iPad. When someone bookmarks your web page or adds your web page to their home screen, this icon is used.
Web app manifest
The web app manifest is a JSON file that tells the browser about your Progressive Web App and how it should behave when installed on the user's desktop or mobile device. A typical manifest file includes the app name, the icons the app should use, and the URL that should be opened when the app is launched.
This is created by Google, and you can check more details on https://web.dev/add-manifest/
Solution 1:
There are some great favicon generators available online. We can use them to help us generate icon files and HTML. And then import them to our Django project.
Create Django Project
$ mkdir favicon_project && cd favicon_project
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install django==4.1
(venv)$ django-admin startproject favicon_project .
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Now check on http://127.0.0.1:8000/
and you will see Django welcome page, then press Ctrl+C
to terminate the dev server.
Templates
Updater favicon_project/urls.py
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")), # new
path('admin/', admin.site.urls),
]
$ mkdir favicon_project/templates
Update TEMPLATES
in favicon_project/settings.py, so Django can know where to find the templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['favicon_project/templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Create favicon_project/templates/index.html
<h1>Home</h1>
(venv)$ python manage.py runserver
Now you should be able to see Home
on the http://127.0.0.1:8000/
Static
$ mkdir favicon_project/static
.
├── db.sqlite3
├── favicon_project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── static # new
│ ├── templates
│ │ └── index.html
│ ├── urls.py
│ └── wsgi.py
└── manage.py
Update favicon_project/settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [
str(BASE_DIR / 'favicon_project' / 'static'), # new
]
- Here we create
favicon_project/static
directory. - We will later put favicons in the above directory so Django can server the files.
Generate Favicon
Here, we use https://realfavicongenerator.net/ to help us generate Favicon. You can also use other 3-party services as you like.
First, click link above and upload our original favicon image.
And then, check the image on different devices, and update settings if we need.
In this case, I set #ffffff
as background color for Favicon for iOS
because the black background does not look good with the orange gear icon
.
After we confirm, we wil wait some seconds, the favicon package and the HTML code will be available.
Import Favicons to Django
In the downloaded package, we can see
.
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── mstile-144x144.png
├── mstile-150x150.png
├── mstile-310x150.png
├── mstile-310x310.png
├── mstile-70x70.png
├── safari-pinned-tab.svg
└── site.webmanifest
Copy all the files to favicon_project/static directory.
.
├── Pipfile
├── Pipfile.lock
├── db.sqlite3
├── favicon_project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── static
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── mstile-144x144.png
│ │ ├── mstile-150x150.png
│ │ ├── mstile-310x150.png
│ │ ├── mstile-310x310.png
│ │ ├── mstile-70x70.png
│ │ ├── safari-pinned-tab.svg
│ │ └── site.webmanifest
│ ├── templates
│ │ └── index.html
│ ├── urls.py
│ └── wsgi.py
└── manage.py
And then copy the HTML from realfavicongenerator.net
to Django template.
{% load static %}
<!doctype html>
<html lang="en">
<head>
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'apple-touch-icon.png' %}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'favicon-32x32.png' %}">
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'favicon-16x16.png' %}">
<link rel="manifest" href="{% static 'site.webmanifest' %}">
<link rel="mask-icon" href="{% static 'safari-pinned-tab.svg' %}" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
</head>
<body>
<h1>Home</h1>
</body>
</html>
Notes:
- Here we update the
href
from/apple-touch-icon.png
to{% static 'apple-touch-icon.png' %}
href="/static/apple-touch-icon.png"
can also work.
Update site.webmanifest
If we check the site.webmanifest, we can see:
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Notes:
- To know more about
web app manifest
, please check Add a web app manifest - We can delete the leading slash of the
src
value to make it work.
Solution 2:
The above solution contains some manual operations
.
Now the frontend community has some good packages, and we will use them to help us get this job done.
Create Django Project
$ mkdir favicon_project && cd favicon_project
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install django==4.1
(venv)$ django-admin startproject favicon_project .
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Now check on http://127.0.0.1:8000/
and you will see Django welcome page, then press Ctrl+C
to terminate the dev server.
Import python-webpack-boilerplate
python-webpack-boilerplate
can let you play with modern frontend tech in Django, within minutes.
(venv)$ pip install python-webpack-boilerplate==1.0.0
Add webpack_boilerplate
to the INSTALLED_APPS
in favicon_project/settings.py
Let's run Django command to create frontend project from the templates
$ python manage.py webpack_init
# here we use the default values
project_slug [frontend]:
run_npm_command_at_root [n]:
Now you should see frontend
directory is created.
├── db.sqlite3
├── favicon_project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── frontend
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── src
│ │ ├── application
│ │ │ ├── app.js
│ │ │ └── app2.js
│ │ ├── components
│ │ │ └── sidebar.js
│ │ └── styles
│ │ └── index.scss
│ ├── vendors
│ │ └── images
│ │ ├── sample.jpg
│ │ └── webpack.png
│ └── webpack
│ ├── webpack.common.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── webpack.config.watch.js
└── manage.py
Config frontend project
If you have no nodejs installed, please install it first by using below links
$ node -v
v16.13.1
$ npm -v
8.1.2
$ cd frontend
# install dependency packages
$ npm install
# run webpack in watch mode
$ npm run watch
Add code below to Django settings favicon_project/settings.py
STATICFILES_DIRS = [
BASE_DIR / "frontend/build",
]
WEBPACK_LOADER = {
'MANIFEST_FILE': BASE_DIR / "frontend/build/manifest.json",
}
- We add the above
frontend/build
toSTATICFILES_DIRS
so Django can find the static assets (img, font and others) - We add
MANIFEST_FILE
location to theWEBPACK_LOADER
so our custom loader can help us load JS and CSS.
Templates
Updater favicon_project/urls.py
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")), # new
path('admin/', admin.site.urls),
]
$ mkdir favicon_project/templates
Update TEMPLATES in favicon_project/settings.py, so Django can know where to find the templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['favicon_project/templates'], # new
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Load webpack bundle file
Create favicon_project/templates/index.html
{% load webpack_loader static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Index</title>
{% stylesheet_pack 'app' %}
</head>
<body>
<h1>Home</h1>
{% javascript_pack 'app' %}
</body>
</html>
- We
load webpack_loader
at the top of the template - We use
stylesheet_pack
andjavascript_pack
to load CSS and JS bundle files to Django
# please make sure `npm run watch` is still running
(venv)$ python manage.py runserver
Now you should be able to see Home
on the http://127.0.0.1:8000/
favicons package
Next, we will use favicons to help us generate different sizes of favicons and their associated files.
$ cd frontend
$ npm install -D [email protected] [email protected] [email protected]
Create frontend/src/index.html, leave it as empty file.
Put the original svg file to frontend/vendors/images/favicon.svg, PNG
and JPG
also ok.
Edit frontend/webpack/webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
plugins: [
...
new FaviconsWebpackPlugin({
// Your source logo (required)
logo: './vendors/images/favicon.svg',
// Favicons configuration options. Read more on: https://github.com/evilebottnawi/favicons#usage
favicons: {
appName: 'AccordBox', // Your application's name. `string`
icons: {
favicons: true, // Create regular favicons. `boolean`
android: true, // Create Android homescreen icon. `boolean` or `{ offset, background }`
appleIcon: true, // Create Apple touch icons. `boolean` or `{ offset, background }`
appleStartup: false, // Create Apple startup images. `boolean` or `{ offset, background }`
coast: false, // Create Opera Coast icon. `boolean` or `{ offset, background }`
firefox: false, // Create Firefox OS icons. `boolean` or `{ offset, background }`
windows: false, // Create Windows 8 tile icons. `boolean` or `{ background }`
yandex: false // Create Yandex browser icon. `boolean` or `{ background }`
}
},
}),
new HtmlWebpackPlugin()
]
Notes:
- We import
HtmlWebpackPlugin
andFaviconsWebpackPlugin
at the top - And then we add two plugins, one is
FaviconsWebpackPlugin
and the other isHtmlWebpackPlugin
- As you can see, we add some config to the
FaviconsWebpackPlugin
to define how favicon is generated.
$ cd frontend
$ npm run build
# now we can see the generated images under build/assets
├── assets
│ ├── android-chrome-144x144.png
│ ├── android-chrome-192x192.png
│ ├── android-chrome-256x256.png
│ ├── android-chrome-36x36.png
│ ├── android-chrome-384x384.png
│ ├── android-chrome-48x48.png
│ ├── android-chrome-512x512.png
│ ├── android-chrome-72x72.png
│ ├── android-chrome-96x96.png
│ ├── apple-touch-icon-1024x1024.png
│ ├── apple-touch-icon-114x114.png
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-144x144.png
│ ├── apple-touch-icon-152x152.png
│ ├── apple-touch-icon-167x167.png
│ ├── apple-touch-icon-180x180.png
│ ├── apple-touch-icon-57x57.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-72x72.png
│ ├── apple-touch-icon-76x76.png
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-48x48.png
│ ├── favicon.ico
│ └── manifest.json
Notes:
- As you can see, the favicons are generated to the
build/assets
directory. - Since we already add
BASE_DIR / "frontend/build",
to theSTATICFILES_DIRS
, so the files inassets
can be served as normal static files by Django (orwhitenoise
in the production)
If we check frontend/build/index.html and format
the HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="shortcut icon" href="/static/assets/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="/static/assets/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/assets/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="48x48" href="/static/assets/favicon-48x48.png">
<link rel="manifest" href="/static/assets/manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#fff">
<meta name="application-name" content="AccordBox">
<link rel="apple-touch-icon" sizes="57x57" href="/static/assets/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/static/assets/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/static/assets/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/static/assets/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/static/assets/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/static/assets/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/static/assets/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/static/assets/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="167x167" href="/static/assets/apple-touch-icon-167x167.png">
<link rel="apple-touch-icon" sizes="180x180" href="/static/assets/apple-touch-icon-180x180.png">
<link rel="apple-touch-icon" sizes="1024x1024" href="/static/assets/apple-touch-icon-1024x1024.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="AccordBox">
<script defer="defer" src="/static/js/runtime.589add24.js"></script>
<script defer="defer" src="/static/js/577.87ba1327.js"></script>
<script defer="defer" src="/static/js/app.992787b3.js"></script>
<script defer="defer" src="/static/js/app2.6d31e388.js"></script>
<link href="/static/css/app.56d0d2493713d97f0c40.css" rel="stylesheet">
</head>
<body></body>
</html>
As you can see, this is very similar with the HTML we download from the above realfavicongenerator.net
We can copy the HTML of icons to the Django template, and it should work well.
Notes:
- By default, only
npm run build
(production build) will generate the favicon. - We can generate favicons when we deploy our project.
Which One is Better
- If you need a simple solution and do not want to touch the NPM stuff, then solution 1 is good option.
- If you need a solution which can give you more control, then the NPM package can help you better solve the problem here.
How to Update Favicon
Some devices might cache the favicons, in some cases, it makes it a little hard.
Some people recommend adding querystring to the URL, however, based on my experience, this method might fail in some cases, and it is not recommended.
Here I can tell you another way to solve this problem.
You can put favicons
and manifest.json
in a specific directory such as assets-VERSION
directory.
Then if you rename the directory, the cache image will not be used because the whole URL has changed.
Make Favicon Work with Both Light/Dark Theme
I recommend you to take a look at Building an adaptive favicon
Want Fewer Icons?
Please read How to Favicon: Six files that fit most needs