Flashing messages with Flask

Often in websites and web applications we want to temporarily show users a message when something happens. Here's some examples of messages:

  • Their username or password was incorrect when trying to log in.
  • They searched for something, but no results were found.
  • They attempted a purchase, but it failed for some reason.

You can see what I mean. In many situations, one thing or another will happen in our application that we must notify the user of. These are commonly known as alerts, errors, or messages.

Because it is so common, Flask comes with functionality that simplifies the process of showing and hiding these messages. It's called "flashing".

I've written a small Flask application to show you what we'll be learning in this blog post. If you want to use it to code along, or just to check out the final code, visit this GitHub repository.

How to flash messages

In order to flash messages, you must have a Flask endpoint, and the flash import:

from flask import flash, render_template

...

@app.route("/")
def home():
    flash("This is a flashed message.")
    return render_template("home.html")

The surprising thing to many is that calling flash() doesn't make your message show up on your template. It just adds the message to the message store.

Once a message has been flashed, it is added to the store and kept there until it is consumed.

Within your template, you must decide whether to consume the message store or not.

But remember, when you do, you'll be consuming the entire message store. That means if you called flash() many times, all of those messages will be given to you in the template.

Let's take a look at the template that could use the message store:

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    {% for message in get_flashed_messages() %}
        <p>{{ message }}</p>
    {% endfor %}
  </body>
</html>

Now your website would just show the text "This is a flashed message.".

Flashing multiple messages

If instead you decided to call flash() more than once:

@app.route("/")
def home():
    flash("This is a flashed message.")
    flash("Another message.")
    return render_template("home.html")

Then your template would show the two messages as separate paragraphs. Again, when you call get_flashed_messages() in your template, all messages in the store are retrieved. Then, they are deleted from the message store.

Sample scenario: login error

Let's say we've got the following code, that allows users to log in:

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        user = find_user(username)

        if user and password == user.password:
            return redirect("profile")
        else:
            flash("incorrect username or password.")
    return render_template("login.html")

Let you imagination fill in the find_user function, and imagine it retrieves user data from a database. If you're interested in finding out how to add login and signup to your Flask apps though, we've got another blog post that covers that.

You can see in that code that if the user doesn't exist, or if the password is not correct, we're flashing a message. Afterwards, we render the login.html template.

That means that our login.html template should be able to handle flashed messages if we want to show them.

<!DOCTYPE html>
<html>
  <head>
    <style>
      .alert-error {
        padding: 8px;
        background-color: red;
        color: white;
      }
    </style>
  </head>
  <body>
    <h1>Flashing messages</h1>
    {% for message in get_flashed_messages() %}
      <div class="alert-error">Error: {{ message }}</div>
    {% endfor %}
  </body>
</html>

Remember, if the login.html template did not consume the message store but did add messages to it, the messages would be kept in the store until another template consumed it.

Flashing messages with categories

Sometimes we may want to flash different types of message. For example, a message that says "no results found" may be classed as an informational message, whereas one that says "incorrect username or password" could be called an error.

You may want to style these differently in your website as well, so being able to differentiate them is important.

That's why flash() has an optional second argument: category.

You can do this: flash("No results found", "info").

Then, in your templates, you can access the category by using the with_categories=True argument. Note the category is the first element of the tuple returned, followed by the message:

{% for category, message in get_flashed_messages(with_categories=True) %}
  <div class="alert-{{category}}">Error: {{ message }}</div>
{% endfor %}

But what we've also done, somewhat surreptitiously, is we've passed the category to the HTML class: alert-{{category}} instead of what we had earlier, which was alert-error.

This means that in our CSS, we could now have this:

.alert-error {
  background-color: red;
  color: white;
}

.alert-info {
  background-color: yellow;
  color: black;
}

This greatly simplifies showing different types of alert for different categories of message.

A styling code improvement

Since this has been a short blog post, I'll also give you a tip to structure your styling code a bit better when dealing with alerts.

If you wanted to add more styling to your alerts, you could do something like this initially:

.alert-error {
  padding: 12px;
  border-radius: 3px;
  font-size: 1.2rem;
  margin-bottom: 16px;
  border: 2px solid darkred;
  background-color: red;
  color: white;
}

.alert-info {
  padding: 12px;
  border-radius: 3px;
  font-size: 1.2rem;
  margin-bottom: 16px;
  border: 2px solid orange;
  background-color: yellow;
  color: black;
}

But notice that while the alerts have different colours, they also have a lot of duplicated styling.

In CSS, you can usually extract this to another class, and apply two classes in the HTML instead of just one. Doing this would be better:

.alert {
  padding: 12px;
  border-radius: 3px;
  font-size: 1.2rem;
  margin-bottom: 16px;
  border-width: 2px;
  border-style: solid;
}

.alert-error {
  border-color: darkred;
  background-color: red;
  color: white;
}

.alert-info {
  border-color: orange;
  background-color: yellow;
  color: black;
}

Then, in the HTML, you'd apply both the alert and the appropriate alert class type to the element:

{% for category, message in get_flashed_messages(with_categories=True) %}
  <div class="alert alert-{{category}}">Error: {{ message }}</div>
{% endfor %}

That reduces duplication in your CSS code, and also makes it a bit clearer what the class is for! The alert class defines the properties of all alerts, and the alert-* classes define properties specific to a certain type of alert.

An added benefit is that now if you wanted to add another piece of styling to all alerts, you only have to do that in one place.

You can apply this in more places in CSS than just here, so if you have duplication in your CSS code, see if you can't extract the duplication into a share class!

Wrapping up

That's really all there is to flashing messages in Flask! It's quite straightforward, but makes our life very easy.

If we didn't have message flashing, usually we'd need to pass arguments to our templates in order to tell them whether a message should be shown. That would be messier and more complicated, so it's great that Flask comes with this functionality!

If you're keen to learn more about Flask and web development, check out our Web Developer Bootcamp with Flask and Python. In it, we develop a few websites using Python, Flask, MongoDB, and HTML/CSS!

Thanks for reading, and I'll see you next time!