Serving Static Files with Flask

Setting Up Flask

Flask is a great choice for building web applications in a modular way using Python. Unlike Django and other analogues like Ruby on Rails, Flask is a micro-framework. This means it includes only what is necessary to do core web development, leaving the bulk of choices beyond that minimal subset to you.

This approach has a huge advantage in keeping your code and workflow simple, particularly on smaller projects. Here we will show you how to serve static files such as JS, CSS, and images using Flask.

Make sure you have Python 3 installed. You can use pyenv for this, which helps you select Python versions. Follow this guide to set up Python 3 using pyenv. If you prefer virtualenv instead, be sure to check out the documentation, but just make sure you have a Python 3 environment active.

Before we can start serving static files with Flask, we need to install it and get a simple app going. To do this, install Flask using the command:

$ pip install flask

Now we'll create a basic Flask app that serves a landing page on which we will display the classic text, "Hello World".

$ mkdir serving_static

In this new directory, create a new file which creates the Flask application and runs it. In this file, we create a Flask route where we will display a welcome message using a Flask template.

# serve.py

from flask import Flask  
from flask import render_template

# creates a Flask application, named app
app = Flask(__name__)

# a route where we will display a welcome message via an HTML template
@app.route("/")
def hello():  
    message = "Hello, World"
    return render_template('index.html', message=message)

# run the application
if __name__ == "__main__":  
    app.run(debug=True)

Now let's create the template to display our message. Create an HTML file in the location "serving_static/templates/index.html" with the following HTML code. Note the message Python variable passed in from the serve.py file above.

<!-- templates/index.html -->  
<html>  
  <head>
    <title>Flask Shop</title>
  </head>
  <body>
    <h1>{{message}}</h1>
  </body>
</html>  

We are now ready to run the application. Back in the "serving_static" root directory, run the application using the command:

$ python serve.py

If the server started up correctly, you will get a message that it started and a URL to view the application. Open up this URL in your browser, and you should now see our "Hello, World" message displayed in the browser.

Hello World

Flask creates application routes using decorators such as the one seen in serve.py above. A decorator such as @app.route("/") creates a new route at the provided path. The function definition below it contains the application logic that will run when a request is received at that URL.

Serving Static Files Using a Static Directory

The most common static files you will want to serve in a web application are your application's CSS files for styling the application, as well as JavaScript files that add dynamic behavior to the page. In this section we will see how to serve both CSS and JavaScript files by adding them to the simple application we created above.

Static files in Flask have a special route. All application URLs that begin with "/static", by convention, are served from a folder located at "/static" inside your application's root folder.

This means if we create a "/static" folder inside our main "serving_static" folder, we will be able to serve static files such as CSS, JS, images, and other assets by simply placing them inside that "/static" folder.

Now let's change the welcome message in our application's index page to introduce "The Flask Shop", a fictional shop where visitors can buy Flask books.

First, we want the index page template to show the visitor the name of the shop, as well as a list of books that are on sale. Update your HTML template at "servingstatic/templates/index.html" with these changes to look as follows.

<!-- templates/index.html -->  
<html>  
  <head>
    <title>Flask Shop</title>
    <link rel="stylesheet" href="/static/style.css">
  </head>
  <body>
    <h1>{{message}}</h1>

    <h3>On sale this week alone:</h3>
    <ol>
      <li>Flask By Example</li>
      <li>Uncluttered Flask</li>
      <li>Flask From First Principles</li>
    </ol>

    <script src="/static/scripts.js" charset="utf-8"></script>
  </body>
</html>  

You will notice that the title of the page is now "Flask Shop", and we have included a list of Flask books for sale. The visitor should be able to see on the page this list of books when they visit the home page of our shop application.

Take a look at the head section of the HTML template again. You will notice that now we are linking to a new file, specifically a CSS stylesheet named "style.css". The path, "/static/style.css" shows that this file is located in our "/static" folder.

Remember that Flask serves files placed in "/static" automatically as static files, instead of trying to run these files as Python source files.

Let us now create a static folder at "serving_static/static" to contain all our static files.

Inside this static folder, let's create the new file "style.css", and add the following CSS rules to add some style to our shop front application.

/* static/style.css */

h1 {  
    color: navajowhite;
    font-variant-caps: all-small-caps;
    font-size: 46px;
}

h3 {  
  color: white;
  font-size: 36px;
}

li {  
  color: red;
  font-size: 50px;
}

body {  
    background: firebrick;
}

With these style rules defined, our application will shed the plain white background we saw in our "Hello, World" example for a more colorful "firebrick" red background. We are also defining distinctive white styling for the text on the page.

Now let us actually update the message we are displaying on the page. Go back into "serving_static/serve.py" and update the message variable to be the name of the shop. Find the hello() function and update the message variable as follows:

# serve.py

...

# a route where we will display a welcome message via an HTML template
@app.route("/")
def hello():  
    message = "The Flask Shop"
    return render_template('index.html', message=message)

...    

Now, restart your server, and run python serve.py again. Then visit the application URL at localhost:5000 and you should see our list of Flask books.

The Flask Shop

Notice that the style of the application is now being picked up from our CSS file which is being served from the "/static" directory by our Flask app.

If you look again at our "serving_static/templates/index.html" template, before the closing </body> tag, we are inserting a JavaScript script tag.

The URL of this script is "/static/scripts.js". This is another static file that will be served by Flask from our "/static" folder.

Let us now create this JavaScript file at "serving_static/static/scripts.js". The contents will be some JavaScript code to dynamically change the background color of our shop application every second. This creates a dramatic effect to draw attention to the limited time sale in our fictional Flask shop.

// scripts.js

// a couple of different backgrounds to style the shop
var background1 = 'black';  
var background2 = 'firebrick';

// this lets us toggle the background state
var color = true;

// every 1 second, switch the background color, alternating between the two styles
setInterval(function () {  
  document.body.style.backgroundColor = (color ? background1 : background2)
  color = !color;
}, 1000);

Now, stop your server and re-run python serve.py once again. When you visit our application in the browser, the page background should flash and change every 1 second from a shade of red, this:

Red background

to a shade of black, like this:

Black background

The setInterval function in our JavaScript code changes the background every second in a continuous time loop.

We are now serving JavaScript and CSS files from our "/static" folder to style and enhance the behavior of our application.

If you have additional CSS or JavaScript files, you can add them to the "/static" folder in the same way and reference them as we have done above.

Serving JavaScript Files

Another common use case when it comes to serving static files in web apps is serving third party libraries such as Backbone.js, Vue.js, Bootstrap or React.

Any kind of library you want to include can be served in almost the same way that we saw above from our "/static" directory.

As an example, let us see how to include the Backbone.js JavaScript library in our application.

A good idea when serving third party libraries is to locate them inside a special lib or vendor directory inside the "/static" folder. You can name this third-party folder whatever you like.

The key, however, is to put external libraries in their own folder where you can easily upgrade versions or do other management separate from your own application code.

With this in mind, create a new folder at "serving_static/static/lib". Since we are including Backbone.js, download the single-file version of Backbone.js here, and then place it inside the new lib folder inside of our "/static" folder.

Backbone.js has a hard dependency on Underscore.js, another JavaScript library, which provides many essential utilities. So download the latest version of Underscore.js here and place it next to Backbone.js in your "/static/lib" folder.

Now we can make use of Backbone.js by including it in our page template and calling its functions.

Back in your template, in the file "serving_static/templates/index.html", find the </ol> closing tag. After it, on a new line, create a new heading element which we will manipulate with Backbone.js.

In addition, add new script tags before the closing </body> tag. In these tags, we include Underscore.js and Backbone.js, as well as write some code to update the DOM using Backbone code.

The updated part of the index.html template should look as follows.

<!-- templates/index.html -->

...
<ol>  
      <li>Flask By Example</li>
      <li>Uncluttered Flask</li>
      <li>Flask From First Principles</li>
</ol>

    <h4 id="version" style="color:white;">Backbone</h4>

    <script src="/static/scripts.js" charset="utf-8"></script>
    <script src="/static/lib/underscore.js" charset="utf-8"></script>
    <script src="/static/lib/backbone.js" charset="utf-8"></script>
    <script type="text/javascript">
      document.getElementById('version').innerHTML = "Proudly built with Backbone.js version " + Backbone.VERSION;
    </script>
  </body>
  ...

Our last <script> tag above is making use of Backbone.js. If you restart the server now, you should see that the text on the page at the bottom shows the correct Backbone.js version which we are using.

Backbone version

If we were using Vue.js, React, or any other library, we could add it and serve its static files in the same way, making it available to our application.

Serving Images and Other File Types

Other file types, such as images and even .txt and .pdf files can be served similarly to the JS and CSS files we showed earlier. Just place them in "/static" and link to them.

Let's see a code example of serving images. Here are two images of related books: book1 and book2. Download them and place them in a new folder at "serving_static/static/images".

Now we can add a "Related" books section to our index template, where we will display the images of these books.

Update your index.html template with the following:

<!-- templates/index.html -->

...

<h4 id="version" style="color:white;">Backbone</h4>

<h3>Related</h3>  
<img src="/static/images/book1.png" alt="Related Book 1" width="20%" height="auto">  
<img src="/static/images/book2.jpg" alt="Related Book 2" width="20%" height="auto">

...

Now when you restart the server and visit the application in the browser, you will see images of the related books.

related books

Preparing Static Files with a Build System

One of the biggest things missing from our setup right now is minification of static assets, concatenation of JavaScript, CSS, as well as other optimizations to make the application faster. In addition, using preprocessors and compilers such as Sass, Coffeescript and Babel needs to be handled manually in our current setup.

Using a build system such as Webpack, Gulp, Brunch, or Browserify helps you automate this process.

If you're interested in going exploring this, here is the Brunch guide to add Brunch as a build system for your project.

Or you can go through the documentation of your preferred build system to get acquainted with how to set it up and integrate it into your Flask application.

Serving Static Files in Production

Running your code in production is a bit different from a development environment. In production, depending on your application, you may encounter much higher volumes of traffic, which can take a toll on your server.

In production, it is recommended to serve static files with a server such as nginx to keep the load on your Python web server as light as possible. If you serve a large number of files then this will speed up your application.

In addition, you will want to use a production grade Python web server rather than the built in Flask server. Some good options are Gunicorn, Gevent and Apache with mod_wsgi.

Conclusion

In this article we saw how you can easily serve static assets using Flask. You can serve JavaScript, CSS, images as well as other static files. This can be done in a variety of ways, with the simplest being to use the "/static" directory, which Flask will use to serve files to the client.

Author image
Tendai Mutunhire started out doing Java development for large corporations, then taught startup teams how to code at the MEST incubator, and is in the process of launching a few startups of his own.