Flashing Messages

Reviewed & published by Brayan K

Flashing is Flask's built-in way to show a user a short, one-time message — such as "Login successful" or "Form has errors" — on the next page they visit, then automatically clear it so it never appears twice.

Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll set a secret_key, call flash() to queue messages, and read them back with get_flashed_messages() to give users instant, friendly feedback.

What You'll Learn in This Lesson

1 What Is a Flash Message?

A flash message is a one-time notification you store during one request and display on the next page the user loads. After it's shown once, Flask throws it away automatically.

This solves a classic problem: after a user submits a form you usually redirect them to a fresh page. A normal variable would be lost across that redirect — but a flashed message survives exactly one hop, making it perfect for "Saved!" or "Invalid password" banners.

🧩 Flashing relies on the session:

2 Queuing a Message with flash()

Call flash("your message") inside a route to queue a notification. Then redirect to another page — the message waits in the session until that page reads it.

The example below flashes a confirmation after a fake "save", then redirects home. Start the server with flask run to see the banner appear once.

# app.py — flash a message after saving
from flask import Flask, flash, redirect, url_for, get_flashed_messages

app = Flask(__name__)
app.secret_key = "change-me-in-production"  # required for flashing

@app.route("/save")
def save():
    # ... save the data ...
    flash("Profile saved successfully!")   # queue a message
    return redirect(url_for("home"))       # send the user onward

@app.route("/")
def home():
    messages = get_flashed_messages()
    return "Home. Messages: " + str(messages)

# Simulate the flow without a live server.
queued = []
def flash_sim(msg):
    queued.append(msg)

flash_sim("Profile saved successfully!")
print("Flashed messages waiting:", queued)
print("After display they are cleared.")
queued.clear()
print("Now:", queued)

# Run live with flask run, then visit /save

# ✅ Expected output:
# Flashed messages waiting: ['Profile saved successfully!']
# After display they are cleared.
# Now: []

Notice we redirect with url_for("home") after flashing. This redirect-then-show pattern prevents the form from being resubmitted if the user refreshes.

3 Reading Messages & Using Categories

get_flashed_messages() returns the pending messages and clears them. You usually call it in a Jinja template, but it works in routes too. Add a second argument to flash() to give each message a category like "success" or "error".

Read categories back with get_flashed_messages(with_categories=True), which returns pairs you can use to color the banner. Here's a typical Jinja loop you'd place in your base template:

{% with messages = get_flashed_messages(with_categories=true) %}
  {% if messages %}
    {% for category, message in messages %}
      <div class="alert alert-{{ category }}">{{ message }}</div>
    {% endfor %}
  {% endif %}
{% endwith %}
# app.py — flash with categories, then read them back
from flask import Flask, flash, get_flashed_messages, redirect, url_for

app = Flask(__name__)
app.secret_key = "change-me"

@app.route("/login", methods=["POST"])
def login():
    password = "wrong"  # pretend this came from the form
    if password == "correct":
        flash("Welcome back!", "success")
    else:
        flash("Invalid password.", "error")
    return redirect(url_for("home"))

@app.route("/")
def home():
    # with_categories=True returns (category, message) pairs
    return str(get_flashed_messages(with_categories=True))

# Simulate categorized messages without a server.
store = []
def flash_sim(msg, category="message"):
    store.append((category, msg))

flash_sim("Welcome back!", "success")
flash_sim("Your trial ends soon.", "warning")
for category, message in store:
    print(f"[{category}] {message}")

# Run live with flask run, then POST to /login

# ✅ Expected output:
# [success] Welcome back!
# [warning] Your trial ends soon.

🎯 Your Turn: Fill in the Blanks

Complete the app so it sets a secret key, flashes a success message, and reads it back. Replace each ___.

# 🎯 YOUR TURN: finish the flashing flow
from flask import Flask, flash, get_flashed_messages, redirect, url_for

app = Flask(__name__)
app.___ = "my-secret"          # the key needed for flashing

@app.route("/subscribe")
def subscribe():
    ___("You are subscribed!", "success")   # queue a categorized message
    return redirect(url_for("home"))

@app.route("/")
def home():
    msgs = ___(with_categories=True)   # read and clear the messages
    return str(msgs)

print("App configured with a secret key and flash routes")

Common Errors & Quick Reference

❌ RuntimeError: The session is unavailable because no secret key was set

You called flash() without setting app.secret_key. Add a line like app.secret_key = "change-me" near where you create the app.

❌ My flashed message appears twice

You're calling get_flashed_messages() more than once on the same page. Reading the messages clears them, so call it a single time — ideally in your base template only.

TaskCode
Set the secret keyapp.secret_key = "change-me"
Flash a messageflash("Saved!")
Flash with categoryflash("Oops", "error")
Read messagesget_flashed_messages()

🏆 Mini Challenge: Validate and Flash

Build a route that checks a username and flashes a success or error message based on the result.

# 🏆 Mini Challenge: validate then flash
from flask import Flask, flash, redirect, url_for

app = Flask(__name__)
app.secret_key = "change-me"

@app.route("/register/<username>")
def register(username):
    if not username.strip():
        flash("Username cannot be empty.", "error")
    else:
        flash(f"Welcome, {username}!", "success")
    return redirect(url_for("home"))

@app.route("/")
def home():
    return "Home page"

# Simulate the validation logic without a server.
def validate(username):
    if not username.strip():
        return ("error", "Username cannot be empty.")
    return ("success", f"Welcome, {username}!")

print(validate(""))
print(validate("Ada"))
# Run live with flask run, then visit /register/Ada

❓ Frequently Asked Questions

Lesson complete — your app talks back to users!

You can set a secret_key, queue notifications with flash(), and display them once with get_flashed_messages() — even across redirects.

Practice quiz

What is a flash message?

  • a permanent banner on every page
  • a one-time message shown on the next request then cleared
  • a logged error
  • a database record

Answer: a one-time message shown on the next request then cleared. A flashed message is stored once and displayed on the next page, then discarded.

Which function queues a flash message?

  • flash('Saved!')
  • notify('Saved!')
  • message('Saved!')
  • session.flash('Saved!')

Answer: flash('Saved!'). flash('text') stores the message in the session for the next request.

Which function reads and clears pending flash messages?

  • read_flashes()
  • session.get_messages()
  • get_flashed_messages()
  • flash.get()

Answer: get_flashed_messages(). get_flashed_messages() returns the pending messages and clears them.

Why does flashing require app.secret_key?

  • to encrypt the database
  • to name the app
  • to speed up rendering
  • the messages live in the signed session cookie

Answer: the messages live in the signed session cookie. Flash messages are stored in the session, which Flask signs using secret_key.

What error occurs if you flash without setting a secret key?

  • a RuntimeError about the session/secret key
  • a 404
  • a syntax error
  • nothing, it works

Answer: a RuntimeError about the session/secret key. flash() raises a RuntimeError because Flask cannot sign the session without a secret key.

How do you add a category to a flash message?

  • flash.category('success')
  • flash('Saved!', 'success')
  • flash('Saved!', category=True)
  • flash_category('Saved!')

Answer: flash('Saved!', 'success'). A second argument like flash('Saved!', 'success') tags the message.

How do you read messages with their categories?

  • get_flashed_messages(categories=1)
  • get_categories()
  • get_flashed_messages(with_categories=True)
  • flash.with_categories()

Answer: get_flashed_messages(with_categories=True). with_categories=True returns (category, message) pairs.

Why might a flashed message appear twice?

  • the secret key is wrong
  • the session expired
  • the message was too long
  • get_flashed_messages() was called more than once on the page

Answer: get_flashed_messages() was called more than once on the page. Reading clears the messages, so calling it twice can show them again; call it once.

What pattern is flashing especially designed to support?

  • redirect-then-show after a form submission
  • infinite scrolling
  • file uploads
  • WebSocket streaming

Answer: redirect-then-show after a form submission. Flashing survives one redirect, ideal for the post/redirect/get pattern.

Where is a flashed message most commonly displayed?

  • in the URL
  • in a Jinja template (often the base template)
  • in the console only
  • in a cookie value you read manually

Answer: in a Jinja template (often the base template). get_flashed_messages() is typically looped in a Jinja template to render banners.

Continue this course

Frequently asked questions

What is a flash message in Flask?

A flash message is a short, one-time notification — like 'Profile saved!' — that you store with flash() in one request and display on the very next page the user sees. After it's shown once, Flask discards it automatically.

Why do I need a secret key for flashing?

Flash messages are stored in the user's session, which Flask signs cryptographically to prevent tampering. That signing requires app.secret_key to be set, or flash() will raise a runtime error.

How do I show flashed messages?

Call get_flashed_messages() — in a route or, more commonly, inside a Jinja template. It returns the list of pending messages and clears them so they only appear once.

Can flash messages have categories?

Yes. Pass a second argument like flash('Saved!', 'success') and read them back with get_flashed_messages(with_categories=True). Categories let you style success, error, and warning messages differently.

Related lessons