How to serve HTML pages using Flask and Jinja

How can we serve dynamic web pages with a Flask app?

This article is also available on Medium.

As web developers, we now have lots of languages to choose from to make our frontends and backends. For example, when making a server, you can write it in PHP, Go, Javascript (with nodejs) or even Python!

The nice thing with creating your backend in Python is that you get direct access to all the packages that have been created by its large community over the years. For example, you’ll be able to use top-notch machine learning libs like Tensorflow or Pytorch and visualise your science data stuff in a flash 🙂

But now – web is also about rendering things in the browser, right? Sure, you’ve got REST APIs that just defined a set of endpoints to call, but then backends usually serve websites with HTML contents! Only Python is not really known for its graphics rendering capabilities… so how do you show up web pages when your backend is written in Python?

The truth is: it depends on the web framework you’re using. And nowadays, there are plenty to choose from; even restricting our selection only to the ones written in Python, you have Flask, FastAPI, Django, Sanic, Hug

So, today, I want to show you one possible method, when you’re working with the Flask package.

What is Flask?

Flask is a web microframework, that was developed by Armin Ronacher among many other great Python tools. It is a very lightweight solution that allows you to quickly setup simple servers with one-page files. It relies a lot on modularity: over the years, the creator and its community have coded up a lot of Flask plugins that extend its functionalities and make it easier to achieve common tasks: user auth, connection to specific databases, WebSockets management…

It is a great choice for beginners, because Flask is easy to learn, quick to prototype and it has grown a very large ecosystem since its creation in 2004. So there are numerous forums, tutorials and posts on the net which makes it pretty simple to find solutions to the most common problems!

Also, for more experienced programmers, Flask is nice because, since its highly customisable, it is very un-opinionated, too. Some of us believe that the tool shouldn’t impose a workflow and that you should remain free to organise your project as you want; others praise on opinionated software for its rigour, its clear goals and its quick-of-use for common problems. In that regard, Flask is quite liberating because you just have a skeleton to work from, there is no built-in architecture to adapt your project too. This means that Flask projects can be drastically different depending on the goal of your app and the plugins you’re using and that you can always create a very optimised environment that is specific to your project.

Finally, Flask has spread so widely that, today, lots of companies use it. So having some background with this technology might ease your on-boarding in a new team and make it easier to learn the rest of the workflow.

Rendering inline HTML with Flask

Okay so let’s start by writing up a super simple Flask application. We will simply draw inspiration from the Flask quickstart docs, but follow the recommended project architecture.

So, let’s create a new folder for our project and, inside this project, create a subfolder called my_app. This directory will contain the main index of our project as well as our HTML pages.

In this my_app folder, we can add a file and fill it with the most basic Flask app code:

This script creates a Flask app that is named after the file itself and defines just one entrypoint: the one for the root URL (/). The associated callback function, also called a “view“, returns a simple HTML element that will be rendered on the page as is.

Now, we can run this app by defining a little environment variable:

export FLASK_APP=my_app

We basically set the Flask app name to be the same as our subfolder so that the flask tool can find the associated app (the one defined in the file). And so, now, we can simply run it with:

flask run

You will see in your console the URL that the tool is serving your Flask app on (it’s usually by default) – if you go to it, you will see our very basic root page:

We’ve just created a little Python web server and used it to render some HTML!

Note that if you want your server to hot reload, i.e. to refresh as soon as you save some changes in your files, you can also export another environment variable:

export FLASK_ENV=development

Upgrade #1: Creating a basic template for decoupling

This is nice – we’ve quickly setup a little project that allows us to show some HTML 🙂

But let’s be honest: using inline HTML like this will soon get a bit cumbersome. What would be better is to separate the definition of our HTML page from the Flask app code so that it doesn’t clutter everything.

And thankfully, Flask has a built-in solution for that: templates.

The idea is that, rather than returning an HTML string directly, we’ll use Flask’s render_template() method and pass it the path to a HTML page to display.

Let’s start by reproducing our “hello world” page, but using this technique. We simply need to create a new folder inside of our my_app directory that we call templates (that’s the default folder name Flask will look up when trying to render a HTML) and, in it, add a index.html file:

Now, we want our Flask app to use this HTML template instead of returning an HTML string. This is pretty straight-forward – we just have to add an import and change the contents of the view for our / route:

If you refresh your page, you should get exactly the same result as before, except that now it’s neatly separated in its own file under the hood 😉

But of course, now that we’ve decoupled the HTML template from the server code, it’s way easier to make more elaborate pages! Here is an example I made using the Bootstrap library:

The corresponding code is available as a Github gist over here 🙂

Upgrade #2: Making our page dynamic with Jinja!

We know have an idea of how we can easily create static HTML pages and prepare them into separate files so as to not to crowd our server code with this info.

The last step is to see how we can make dynamic web pages, meaning how we can automatically create HTML derived from our server data.

To do this, we can’t just use HTML anymore. We’ll have to introduce another intermediary tool: Jinja.

Jinja is a fast and powerful Python templating engine. The idea is that Jinja can interpret some special placeholders (that look quite like Python code) and replace them with auto-generated HTML content. These placeholders can read data that you send from your Flask app and use it either directly in DOM elements to display some values, or into if-checks and for-loops to create more complex structures.

Suppose you are making the website of a a little grocery shop. So, you have a list of products that each have a name, the current number of items in store and the individual price. We can pass those in our Flask app to the template (in the variable products):

Note: Jinja is the templating engine used by Flask by default, so it’s automatically included your workflow when you call render_template(): this means we don’t have to import anything else in our server to use Jinja!

And, on the other end, we just need to create a little template with Jinja placeholders:

Note that you can name your file index.html (it will still interpret it with the Jinja engine) or rename it to index.jinja, but then remember to change the server code where you call render_template()!

If we refresh our page, we’ll get our list filled up automatically. And what’s really handy is that adding or removing a product is as easy as changing one line in our Python code: we’ve successfully created a dynamic web page! 🙂

Similarly, we can have little if statements to check for specific boolean conditions:

This will show the prices that are below $1 in red (those are the “low prices”) and the ones that are above in blue (those are the “high prices”):

Another great thing with templating engines is that they allow you to combine your templates into a hierarchy of layouts; so, this way, you can create a base layout for all your pages to inherit from and then fill in the different parts of this layout template with your page-specific content.

For example, you can create a base layout, like this:

Here, we have defined two “title” and “content” blocks: both of those need to be filled by the child template that inherits from this layout template; something like this:

As you can see, our index.html file doesn’t actually know anything about the overall layout of the page. We make it “extend” the base template – and then it just fills in the various blocks with its specific info, but it doesn’t care how this info is then structured by the base.html template.

This gives us a simple web page that looks like this – we’re still on our root URL /, so we’re calling the index.html page:

This is great for sharing layouts and reducing redundancy in your project: you only write your layout once, and if you update it, all the pages get properly changed to match the new style! 😉


Flask is a really great Python lib for whenever you want to quickly prototype a web server, and even do some HTML rendering. It uses the Jinja templating engine natively which allows us to use dynamic HTML templates and to directly inject our Python data into the web page.

If you want something a bit more fancy, with lots of pages and plenty of built-in features, though, you should probably take a look at Django that has more ready-made functionalities like secure user auth or database connections… 😉

What about you: do you use Python for web development? Which framework is your favorite? 🙂

Leave a Reply

Your email address will not be published.