Flask is a small and powerful web framework for Python. It’s easy to learn and easy to use, so you can build your web app in no time.
In this article, I’ll show you how to build a simple website that contains two static pages with a small amount of dynamic content. While Flask can be used to build complex, database-driven sites, starting with mostly static pages will be useful to introduce a workflow that we can then generalize to make more complex pages in the future. Once you’re done, you’ll be able to use this sequence of steps to launch your next Flask app.
Installation of flask
Before we get started, we need to install Flask. Because systems vary, things can sporadically go wrong during these steps. If they do, as we all do, just Google the error message or leave a comment describing the problem.
Install Virtualenv
Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work.
We use virtualenv to install Flask. Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work. Suppose you come across a new Python library that you want to try out. Installing the entire system runs the risk of corrupting other libraries that you may have installed. Instead, use virtualenv to create a sandbox where you can install and use the library without affecting the rest of your system. You can continue to use this sandbox for ongoing development work, or you can simply delete it when you’re done using it. Either way, your system stays organized and clutter-free.
It is possible that your system already has virtualenv. View the command line and try running:
If you see a version number, you’re good to go and you can skip to this “Install Bottle” section. If the command was not found, use pip
to install virtualenv you should already have pip installed. If you’re running Linux or Mac OS X, the following should work for you:
Install pip
1 |
sudo apt-get install python3-pip |
Install Virtualenv
1 |
$ sudo pip install virtualenv |
If you don’t have any of these commands installed, there are several tutorials online that will show you how to install it on your system. If you are running Windows, install virualenv with pip as follows:
Install the flask
After installation virtualenv
you can create a new isolated development environment, like this:
Here, virtualenv
creates a folder, bottle/, and sets up a clean copy of Python inside for you to use. It also installs the handy package manager, pip
.
Go into your newly created development environment and activate it so you can start working in it.
1 |
$ cd flaskapp |
2 |
$ . bin/activate |
Now you can safely install Flask:
Setting up the project structure
Let’s create a few folders and files inside bottle/ to keep our web app organized.
1 |
. |
2 |
. |
3 |
├── app |
4 |
│ ├── static |
5 |
│ │ ├── css |
6 |
│ │ ├── img |
7 |
│ │ └── js |
8 |
│ ├── templates |
9 |
│ ├── routes.py |
10 |
│ └── README.md |
Within bottle/create a folder, app/, to contain all your files. Inside app/create a folder static/; this is where we put our web app’s images, CSS and JavaScript files, so create folders for each of them as shown above. In addition, you must create another folder, templates/, to store the app’s web templates. Create an empty Python file routes.py for the application logic, such as URL routing.
And no project is complete without a useful description, so create one README.md file too.
Now we know where to place our project’s assets, but how does everything fit together? Let’s take a look at the chart below to see the big picture:



- A user issues a request for a domain’s root URL
/
to go to its website -
routes.py maps the URL
/
to a Python function - The Python function finds a web template that resides in templates/ folder.
- A web template will look i static/ folder for any images, CSS, or JavaScript files it needs when rendering to HTML
- Rendered HTML is sent back to routes.py
- routes.py sends the HTML back to the browser
We start with a request from a web browser. A user enters a URL in the address bar. The request hits routes.py, which has code that maps the URL to a function. The function finds a template in templates/ folder, renders it to HTML and sends it back to the browser. The function can optionally retrieve records from a database and then pass that information to a web template, but since we’re mostly dealing with static pages in this article, we’ll skip interacting with a database for now.
Now that we know our way around the project structure we’re setting up, let’s get down to creating a home page for our web app.
Creating a home page
When writing a web app with a few pages, it quickly becomes annoying to write the same HTML boilerplate over and over for each page. Furthermore, what if you need to add a new element to your app, such as a new CSS file? You will have to go into each page and add it. This is time consuming and error prone. Wouldn’t it be nice if, instead of repeatedly writing the same HTML boilerplate, you could just define your page layout once and then use that layout to create new pages with their own content? That’s exactly what web templates do!
Web templates are simply text files that contain variables and control flow statements (
if..else
,for
etc.), and ends with a.html
or.xml
expansion.
The variables are replaced with your content when the web template is evaluated. Web templates remove repetition, separate content from design and make your application easier to maintain. In other, simpler words, web templates are great and you should use them! Flask uses the Jinja2 templating engine; let’s see how to use it.
As a first step, we will define our page layout in a skeleton HTML document layout.html and put it inside templates/ folder:
app/templates/layout.html
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
<head>
|
4 |
<title>Flask App</title> |
5 |
</head>
|
6 |
<body>
|
7 |
|
8 |
<header>
|
9 |
<div class="container"> |
10 |
<h1 class="logo">Flask App</h1> |
11 |
</div>
|
12 |
</header>
|
13 |
|
14 |
<div class="container"> |
15 |
{% block content %} |
16 |
{% endblock %} |
17 |
</div>
|
18 |
|
19 |
</body>
|
20 |
</html>
|
This is simply a plain HTML file…but what happens to it {% block content %}{% endblock %}
part? To answer this, let’s create another file home.html:
app/templates/home.html
1 |
{% extends "layout.html" %} |
2 |
{% block content %} |
3 |
<div class="jumbo"> |
4 |
<h2>Welcome to the Flask app<h2> |
5 |
<h3>This is the home page for the Flask app<h3> |
6 |
</div>
|
7 |
{% endblock %} |
The file layout.html defines an empty block, named content
which a child template can fill. The file home.html is a child template that inherits the markup from layout.html and fills the “content” block with its own text. In other words, layout.html defines all the common elements of your site, while each child template customizes it with its own content.
That all sounds great, but how do we actually see this page? How can we enter a URL into the browser and “visit” home.html? Let’s refer back to the figure above. We just made the template home.html and placed it in templates/ folder. Now we need to map a URL to it so we can see it in the browser. Let’s open up routes.py and do this:
app/routes.py
1 |
from flask import Flask, render_template |
2 |
app = Flask(__name__) |
3 |
|
4 |
@app.route('/') |
5 |
def home(): |
6 |
return render_template('home.html') |
7 |
|
8 |
if __name__ == '__main__': |
9 |
app.run(debug=True) |
That’s what it’s for routes.py. What did we do?
- First. we imported the Flask class and a function
render_template
. - Next, we created a new instance of the Flask class.
- We then mapped the URL
/
to the functionhome()
. Now, when someone visits this URL, the function willhome()
will perform. - The function
home()
using the Flask functionrender_template()
to reproduce home.html template we just made from templates/ folder to the browser. - Finally, we use
run()
to run our app on a local server. We put itdebug
flag totrue
so we can see any relevant error messages if something goes wrong, and so the local server automatically reloads after we make changes to the code.
We are finally ready to see the fruits of our labor. Return to the command line and type:
Visit http://localhost:5000/ in your favorite web browser.



When we visited http://localhost:5000/, routes.py had code in it that mapped the URL /
to the Python function home()
. home()
found the web template home.html in templates/ folder, rendered it to HTML and sent it back to the browser, giving us the screen above.
Pretty nice, but this website is kind of boring, isn’t it? Let’s make it look better by adding some CSS. Create a file, main.cssinside static/css/and add these rules:
static/css/main.css
1 |
body { |
2 |
margin: 0; |
3 |
padding: 0; |
4 |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
5 |
color: #444; |
6 |
}
|
7 |
/*
|
8 |
* Create dark grey header with a white logo
|
9 |
*/
|
10 |
|
11 |
header { |
12 |
background-color: #2B2B2B; |
13 |
height: 35px; |
14 |
width: 100%; |
15 |
opacity: .9; |
16 |
margin-bottom: 10px; |
17 |
}
|
18 |
header h1.logo { |
19 |
margin: 0; |
20 |
font-size: 1.7em; |
21 |
color: #fff; |
22 |
text-transform: uppercase; |
23 |
float: left; |
24 |
}
|
25 |
header h1.logo:hover { |
26 |
color: #fff; |
27 |
text-decoration: none; |
28 |
}
|
29 |
/*
|
30 |
* Center the body content
|
31 |
*/
|
32 |
|
33 |
.container { |
34 |
width: 940px; |
35 |
margin: 0 auto; |
36 |
}
|
37 |
div.jumbo { |
38 |
padding: 10px 0 30px 0; |
39 |
background-color: #eeeeee; |
40 |
-webkit-border-radius: 6px; |
41 |
-moz-border-radius: 6px; |
42 |
border-radius: 6px; |
43 |
}
|
44 |
h2 { |
45 |
font-size: 3em; |
46 |
margin-top: 40px; |
47 |
text-align: center; |
48 |
letter-spacing: -2px; |
49 |
}
|
50 |
h3 { |
51 |
font-size: 1.7em; |
52 |
font-weight: 100; |
53 |
margin-top: 30px; |
54 |
text-align: center; |
55 |
letter-spacing: -1px; |
56 |
color: #999; |
57 |
}
|
Add this stylesheet to the skeleton file layout.html so the styling applies to all its child templates by adding this line to its
-element:
1 |
<link rel="stylesheet" href="{{ url_for('static', filename="css/main.css") }}">; |
We use the Flask function, url_for
to generate a URL path to main.css from static folder. After adding this line, layout.html should now look like this:
app/templates/layout.html
1 |
<!DOCTYPE html>
|
2 |
<html>
|
3 |
<head>
|
4 |
<title>Flask</title> |
5 |
<strong><link rel="stylesheet" href="{{ url_for('static', filename="css/main.css") }}"></strong> |
6 |
</head>
|
7 |
<body>
|
8 |
<header>
|
9 |
<div class="container"> |
10 |
<h1 class="logo">Flask App</h1> |
11 |
</div>
|
12 |
</header>
|
13 |
|
14 |
<div class="container"> |
15 |
{% block content %} |
16 |
{% endblock %} |
17 |
</div>
|
18 |
</body>
|
19 |
</html>
|
Let’s switch back to the browser and refresh the page to see the result of the CSS.



That was better! Now when we visit http://localhost:5000/, routes.py still mapping the URL /
to the Python function home()
and home()
still finding the web template home.html in templates/ folder. But since we added the CSS file main.cssthe web template home.html looking in static/ to find this asset before rendering it to HTML and sending it back to the browser.
We have achieved a lot so far. We started with Fig. 1 by understanding how Flask works, and now we’ve seen how it all plays out by creating a home page for our web app. Let’s go ahead and create an About page.
Creating an About page
In the previous section, we created a web template home.html by extending the skeletal file layout.html. We then mapped the URL /
to home.html in routes.py so we could visit it in the browser. We finished things off by adding some styling to make it look pretty. Let’s repeat this process again to create an about page for our web app.
We start by creating a web template, about.htmland put it inside templates/ folder.
app/templates/about.html
1 |
{% extends "layout.html" %} |
2 |
|
3 |
{% block content %} |
4 |
<h2>About</h2> |
5 |
<p>This is an About page for the Intro to Flask article. Don't I look good? Oh stop, you're making me blush.</p> |
6 |
{% endblock %} |
As before with home.htmlwe extend from layout.htmland then fill in content
block with our customized content.
To visit this page in the browser, we need to associate a URL with it. Open up routes.py and add another mapping:
1 |
from flask import Flask, render_template |
2 |
|
3 |
app = Flask(__name__) |
4 |
|
5 |
@app.route('/') |
6 |
def home(): |
7 |
return render_template('home.html') |
8 |
|
9 |
@app.route('/about') |
10 |
def about(): |
11 |
return render_template('about.html') |
12 |
|
13 |
if __name__ == '__main__': |
14 |
app.run(debug=True) |
We mapped the URL /about
to the function about()
. Now we can open the browser and go to http://localhost:5000/about and check our newly created page.



Adding navigation
Most websites have links to their main pages in the document header or footer. These links are usually visible across all pages of a website. Let’s open the skeleton file, layout.html. and add these links to appear in all child templates. Let’s specifically add one <nav>
element inside <header>
element:
app/templates/layout.html
1 |
... |
2 |
<header>
|
3 |
<div class="container"> |
4 |
<h1 class="logo">Flask App</h1> |
5 |
<strong><nav>
|
6 |
<ul class="menu"> |
7 |
<li><a href="{{ url_for('home') }}">Home</a></li> |
8 |
<li><a href="{{ url_for('about') }}">About</a></li> |
9 |
</ul>
|
10 |
</nav></strong>
|
11 |
</div>
|
12 |
</header>
|
13 |
... |
Once again we use the Flask function url_for
to generate URLs.
Then add some more style rules main.css to make these new navigation elements look good:
app/static/css/main.css
1 |
...
|
2 |
/*
|
3 |
* Display navigation links inline
|
4 |
*/
|
5 |
.menu { |
6 |
float: right; |
7 |
margin-top: 8px; |
8 |
}
|
9 |
.menu li { |
10 |
display: inline; |
11 |
}
|
12 |
.menu li + li { |
13 |
margin-left: 35px; |
14 |
}
|
15 |
.menu li a { |
16 |
color: #999; |
17 |
text-decoration: none; |
18 |
}
|
Finally, open the browser and refresh http://localhost:5000/ to see our newly added navigation links.



Conclusion
In the course of this article, we built a simple web app with two, mostly static, pages. In this way, we learned a workflow that can be used to create more complex websites with dynamic content. Flask is a simple yet powerful framework that allows you to efficiently build web apps. Go ahead – check it out!
This post has been updated with contributions from Esther Vaati. Esther is a software developer and writer for Envato Tuts+.