Two white canvas Flask sites: one super minimal, the other featuring a database

Flask + Bootstrap 5 starter websites

Rinaldo Nani
Variance Digital
Published in
14 min readSep 28, 2022


Minimal yet scalable website templates made with Flask — featuring lots of goodies

Need a scalable and “empty” Flask template to start your project from scratch? We have made two of them: one with the bare minimum, which we call “Minimal”; the other with added code for a lightweight database connection, which we call “Minimal + DB”.

We use these templates whenever we are asked to build new web applications: a website for a customer, a REST API or a web app (or the back-end for a Flutter app).

Figure 1 shows a screenshot of our up-and-running “Minimal” demo site, the one with no database; you can find its Flask source code in this GitHub repository.

Figure 1 — Homepage of our “Minimal” website

As you can see, the template includes a complete responsive navbar menu (with the highlighted selected item), a minimalistic footer (which docks in the correct position even when the contents are short!), and a set of usual institutional pages. And the indispensable site’s favicon.

Furthermore, the website is divided into sections. Each section corresponds to a Flask Blueprint, a neat way of organizing the site’s pages in separate modules. The role of blueprints will be further explained in the next section.

Both our website templates use Bootstrap 5 to ensure the resulting site is responsive. Furthermore, Bootstrap 5 is used to make it fast with:

  • buttons, cards, and other layout components;
  • sticky navbar;
  • modal popups and the custom cookie banner;
  • error alerts.

The “Minimal + DB” homepage is shown below: you may play with our demo site, which uses a tiny PostgreSQL database (with a table of cat pictures); the complete code is in this GitHub public repository.

Figure 2 — “Minimal+Db” homepage

1. The way the Flask projects are organized

Figure 3 shows the folder structure of our two template sites. This specific way of arranging files and folders is quite common for scalable Flask sites and has many advantages over many ‘hallo-world’ naive examples.

Note: the “yellow” highlighted elements are present in the “Minimal + DB” feline example site but absent in the more basic “Minimal” no-database site.

1.1 All python code is wrapped in one package
The most relevant thing about the structure of Figure 3 is that most of the Python code is wrapped in a Python package. In fact, immediately under the site’s main folder, there is just one subfolder: the package folder (usually called the same name as the top root folder; for the “Minimal+DB” demo site, the package folder is called minimaldb — you have guessed it!).

The package folder must contain the compulsory file, which typically holds the code for initializing and configuring the Flask application.

Figure 3. The folder structure of the “Minimal+DB” site

In this “package” style of this Flask app structure, all the essential Python code of the site is wrapped inside the package folder. All files outside the package folder (e.g. .gitignore,,,, etc.) have a purpose that is collateral to the site’s aspect and functionality. See section 1.5 to learn more about these files.

1.2 Each site section is a blueprint
Each site section is tidily implemented in a dedicated Flask Blueprint; in Figure 3, blueprints are defined inside the dark red files named bl_[*].py.

For example, all Python code that deals with the home page and other institutional pages (like “about” and “privacy-notice”) is defined in the “bl_home” blueprint and is contained in the file. As another example, consider the photo album of the “Minimal + DB” site: all relevant pages for the photo album are defined in the “bl_photoalbum” blueprint, and the code lies in the file. Long story short: the code for each section is implemented in the Python file named after its corresponding blueprint.

1.3 Page templates are orderly placed into subfolders
HTML templates, which are the schemes used to build the site's dynamic web pages, are gathered in the template folder and subfolders.

Figure 3 shows that every blueprint/section of the site has a corresponding subfolder inside the template folder, the latter subfolder containing the HTML templates which are specific to the related section (as an example, in Figure 3, the bl_home subfolder is shown, with its HTML template pages).

1.4 Static files are all in one place
The static folder gathers all static files orderly: all site’s images, CSS styles and javascript files are placed in dedicated subfolders. The static folder also contains robots.txt and sitemap.xml, which the Flask app serves with a special trick (which will be explained in section 4.6 below).

1.5 The site is ready for online deploy
The fact that all the site’s code is wrapped in a single Python package makes the site easy to publish online on platforms like Heroku, AWS or Azure. We are writing an article about how to deploy our Flask sites. Stay tuned!

1.5 What about the files in the root folder?
Each one of the files placed outside of the site’s package folder has a specific use.

  • is the startup file used to run the site locally: see section 7 for further info.
  • reqiurements.txt contains the list of packages needed to run the site; see section 7 for instructions on installing these packages.
  •, Procfile and are needed when the site is deployed on AWS, Heroku or Azure. If the site is deployed on Heroku, the file runtime.txt is used to tell Heroku which version of Python should be used to run the Flask site.
  • and are two markdown text files: the first contains a description of the site, and the latter is the license for the terms of use of our code.
  • If Git is used for version control, the file .gitignore tells Git which files should be excluded from commits.

2. How the web pages are structured

Each HTML page of our sites is derived (or “extended”, as in Flask jargon) from our base.html template.
For example, consider the home page index.html, which is in the folder /minimal/templates/home (and is managed by the blueprint “bl_home”). The page has its specific content placed in two named blocks, metatags and contents:

Code sample 1 — Structure of index.html

Both named blocks, metatags and contents, are defined in the base.html file, which looks like this:

Code sample 2— Structure of base.html template

As one can see, the base.html code “frames” all those elements identical on every page: the navbar, the footer, the CSS and javascript references, the modal popup, the cookie banner etc.

Figure 4 shows the index.html page, with its specific content placed in the metatatagsand contents blocks while extending the base.html template.

Figure 4 — The index.html page extends the base.html template

The base.html code thus includes an HTML file for each common element: e.g. it includes the incl_navbar.html file, which in turn contains the HTML tags of the navbar.

All this explains why the files base.html, incl_cookiealert.html, incl_footer.html, incl_modal.html, and incl_navbar, are all placed in the templates subfolder (see Figure 3). Still, these files are placed outside the folders holding the templates of the sites’ pages: the reason is that base.html (plus the “includes”) participates in the composition of every single web page of the site.

Each page of the website is obtained by calling a dedicated route, and each route has its associated function (called “view” by Flask enthusiasts), as expected for Flask-style sites. For example, the index() and about() pages are defined by the routes / and /about, respectively, as in the following code:

3. When a database is what you need

If your site needs a database to store and retrieve data, you are in good company: a considerable percentage of online sites are backed up by a relational database.

Adding database functionality to our “Minimal” site is easy. Figure 4 shows the architecture of the “Minimal+DB” Flask application: once again, the highlighted elements are those that make “Minimal+DB” different from the “Minimal” project.

Figure 5 — Minimal+DB architecture

The tiny database holding the data for the “Minimal+DB” site is a PostgreSQL instance. For this demo, the database contains just one table: tbl_images; the table structure is defined by the SQL code below, which speaks by itself.

CREATE TABLE minimaldb.tbl_image (
img_id bigint NOT NULL,
img_name character varying(124),
img_caption character varying(255),
img_filename character varying(255) NOT NULL,
img_tstamp timestamp with time zone DEFAULT now() NOT NULL,
img_onair boolean DEFAULT true NOT NULL, <-- show/hide image
img_seqno integer DEFAULT 0, <-- for image order
img_is_in_hp boolean DEFAULT false <-- true if image is in home

The script files (in the/minimal/database scripts folder) contain the commands needed to create a complete instance of our demo PostgreSQL database. See section 7 for further details.

To access the PostgreSQL database from Python, we use the psycopg2 package. psycopg2 is a dependency of the “Minimal+DB” Flask project and must be installed to make everything work smoothly. Section 7 describes how to install psycopg2 locally.

Not just a naming convention. The name of all our PostgreSQL fields and tables are strictly lowercase. This is because the Python library psycopg2, which is used to access PostgreSQL, has a particularly awkward behaviour with mixed uppercase and lowercase names.

psycopg2 enables the Flask app to connect to the database and fetch data via SQL queries, i.e. using code similar to the snipped below.

Code sample 6 — Getting all images for the “Minimal+DB” page

To ORM or not to ORM? When discussing Flask and relational databases, using SQLAlchemy or other ORM (Object Relational Mapper) tools is strongly recommended. The main idea behind these tools is to bridge the “low level” database functions — which deal with queries, tables, records, views etc. — to the “high level” object representation of the data.

But to achieve this, one has to a) install the ORM package and b) deal with the additional software “layer” of the chosen ORM tool (along with its best practices).

Sometimes we feel that going straight to the point is the easiest and most efficient way: therefore, we use psycopg2 database commands directly, without any ORM tool.

But if you want to use an ORM, feel free to add SQLAlchemy to a copy of our templates and modify our code for your Object Oriented Flask projects :). Follow me on Medium and keep in touch; I’m curious.

In line 5 of Code sample 6, the db variable receives an instance of the database object, which is then used to work with tables and records. The function fetching the database object (every time the user asks for a page!) is get_db(); it is defined in the crucial /minimaldb/ file, shown below.

Code sample 7— Functions that open and close database connections.

Depending on the location of the database instance, the code in the get_db() function creates a connection to the database and returns the database object. But it also makes a clever move: it assigns the database connection object to the global g._database. In this way, if the get_db() function happens to be called several times during the web page request process, the taxing psychopg2.connect() operation will be executed only once: from the second time onwards, the database connection object will be retrieved from g._database global variable.

The last lines of our crucial /minimaldb/ Python file involve the close_db() function. This function closes the database connection (if it is open) and clears the global g Flask variable, popping off its g._database property.

Note that the close_db() function is called every time a requested web page is sent back to the browser; in fact, close_db() is registered using the app.teardown_appcontext() handler and is thus triggered at the end of each response operation.

But where does this enforcing happen, via the init_app(app) call? If one looks in the file, one will find the call:

def create_app():
from . import db
db.init_app(app) #<- Here it is!

Mystery solved.

4. Other goodies

Both “Minimal” and “Minimal+DB” sites come with an exciting set of features. They are responsive, lightweight and SEO-friendly; they display a navbar, a footer, breadcrumb navigation, ready-to-use popups and a full-fledged cookie banner.

4.1 Lightweight footprint
Our “Minimal” sites get awesome scores in Google’s PageSpeed Insight performance monitor.

Figure 6 — PageSpeed performance of “Minimal” Flask site

Try the PageSpeed tool live for the complete report.

4.2 Cookie banner
The base.html template, from which all the site’s pages are derived, includes the file incl_cookie_alert.html; the latter file contains the HTML code for the modal cookie banner.

Any page delivered by our Flask applications can potentially display the cookie banner, which may happen if the global g.showCookieAlert is set to true for any reason.

Here is the snippet in incl_cookie_alert.html that spawns the banner, a mix of Jinja and Javascript:

{%if g.showCookieAlert==true%}
var myModal = new bootstrap.Modal(
{backdrop: 'static', keyboard: false}

All the logic behind the cookie banner and the user’s setting is managed by a Python decorator defined in, defined as manageCookiePolicy(view): please look at how it is implemented with the use of the Flask session.

Whenever a call requires a check of the cookie policy, just put the decorator between the route and the view, as shown in this example taken from the “photoalbum” blueprint:

@bp.route('/',methods=('GET', 'POST'))
def list():
mc = set_menu("list")
images = db_get_all_images()
return render_template('photoalbum/list_of_images.html',
mc=mc, images=images)

4.3 Modal popups for errors and messages
The base.html template also includes the file incl_modal.html. Again, this means that any page can display a modal popup. In this case, the triggering mechanism relies on the native Flask flash() function.

We dedicated a little site section to show how to display modal popups for errors and messages; the section is implemented in the blueprint Python file.

4.4 Highlight selected menu item
The set_menu(...) is one of our custom functions and is used everywhere to highlight the correct menu item in the navbar; it works in a pretty simple way: this is the definition:

And here is how the menu highlighting is implemented in the incl_navbar.html file:

4.5 Nice URLs.
A small section on both sites has been made to showcase nice URLs for page routes. To construct links with nice URLs, we implemented a handy Jinja filter called slugify. Here is its definition:

from inflection import parameterizedef slugify(myvar):
return parameterize(myvar)[:80].rstrip('-')

Note the use of the parametrize() function, which is found in the inflection package (the package is included in the requirements).

The Jinja filter is registered in the file. To use it, compose the href of the link inspired by the example below:


4.6 Serve robots.txt + sitemap.xml files
These files are usually placed in the site's root folder, but for Flask projects, they must be in the static folder.

So here is the trick to deliver them upon request:

#MANAGE sitemap and robots calls
def static_from_root():
return send_from_directory('static', filename=request.path[1:])

This code is implemented in the blueprint file.

5. Does this general approach scale well?

Short answer: Yes!
First, the architecture's modularity makes adding new sections and functionality easy enough: add (and register) a new blueprint for each new site’s section, with its database-related file and template folder. And it’s done.

If you are using PostgreSQL, you can share the same database between different web applications: assign a “Schema” to each application/project to separate tables, views, functions etc., of each project.

If you are lucky and have lots of users navigating your website, you can scale your Flask application horizontally; for example, Heroku allows you to seamlessly scale up the number of processing units (called dynos) so that millions of users will not be a problem.

Examples of more significant sites:

6. A note about Bootstrap 5

We use Bootstrap 5 to rapidly add basic standard functionality to our demo sites (e.g. navbar, buttons, cards, etc.). Nevertheless, Bootstrap 5 integration for “Minimal” and “Minimal+DB” is relatively lightweight. All Bootstrap libraries and stuff are taken from Bootstrap’s CDNs, thanks to a few lines of HTML code placed in the base.html template file.

If you do not want to use Bootstrap, remove the related lines of HTML code from base.html and adapt all other HTML pages to show the desired graphics output without using Bootstrap classes.

7. How to run the Flask projects locally

First: clone the GitHub repository of the desired site (you’ll find “Minimal” here and “Minimal+DB” here). For example:

git clone
cd minimal

These commands will create the folder with the Flask project and step into its directory (with the cd command).

Each “Minimal” site has its list of Python requirements, as seen in the requirements.txt file: these are the packages needed to run the application. The requirements include the Flask-related packages (to run the project locally) and the Gunicorn package for production deployment.

It is well known that Flask comes with a small server for debugging purposes, but for online deployment, a serious WSGI HTTP Server is needed, and Gunicorn is a good choice.

In addition, the requirements of the “Minimal-DB” site include the psycopg2 package, which is needed to access the PostgreSQL database instance.

Before installing the requirements, we suggest creating a virtual environment (see the official reference). One can pick any name for the virtual environment’s folder; we decided on “venv”, — which is a common practice:

[On Mac]
python3 -m venv venv
[On Windows]
py -m venv venv

Then, activate the virtual environment:

[On Mac]
source venv/bin/activate
[On Windows]

Now install the site’s requirements:

pip install -r requirements.txt

If the site does not need a database, you can run it straight away with:

[On Mac]
[on Windows]

Instead, if the site does need its related database, you should first install PostgreSQL [if it’s not already installed on your machine]. A simple way to do this is to install pgAdmin, a free tool to manage PostgreSQL databases (see the official site).

Using pgAdmin, create a new database named my_local_db and, once completed, spawn PgAdmin’s “Query Tool” window from the my_local_db database. A copy of the database used by “Minimal+DB” can be recreated locally using the scripts in the files 1-create-database.sql and 2-fill-database.sql(placed in the \database scripts folder). Copy and paste the text of the files in the “Query Tool” window, and press the “Execute” button: first use 1-create-database.sql, and then 2-fill-database.sql.

The name "my_local_db" is used in the sample code.
See the file /minimaldb/ of the project, or Code Sample 6 above.

Once this database is set up, the “Minimal+DB” site can be started with:

[On Mac]
[On Windows]

8. FAQs — TL;DR :)

> Where can I get the complete code of the Flask Minimal sites?

  • Please feel free to follow us on Medium :) and fetch the code in these public GitHub repos: here for the “Minimal” site without database, here for the “Minimal + DB” site.

> Are these demo sites running somewhere?

  • Sure! The “Minimal” demo site is here; for the “Minimal + DB”, click here.

> How can I run the sites locally?

  • See section 7 above for complete instructions. The sample database for the “Minimal+DB” site can be created using the script files in the
    /database scripts folder.

> How to deploy (a modified version of) these sites online?

  • It’s easy to deploy our template Flask projects on Heroku, Azure or AWS Beanstalk: read the article below to see how.



Rinaldo Nani
Variance Digital

Algorithmist ▪ Software Engineer ▪ Project manager. I love maths and music + solving hard problems.