Quick Facts
- Category: Education & Careers
- Published: 2026-05-01 04:10:48
- How to Set Up and Use Docker Offload for Seamless Container Development Anywhere
- From TACO to NACHO: How Wall Street's Slang for Trump's Moves Became a Market Menu
- How to Refresh Your Desktop with Community-Created Monthly Wallpapers (A Step-by-Step Guide)
- Chipotle Hires Burger King Marketing Star Fernando Machado to Reverse Sales Slump
- Mid-Week Green Deals Roundup: Ride1Up Prodigy V2 at New Low, Anker SOLIX Flash Sale, Jackery Mother's Day Deals, and More
Flash messages are a core part of any web application's user experience, providing instant feedback after actions like creating, updating, or deleting records. In Phoenix, flash messages come built-in, but many developers overlook how they're rendered on the page. This article dives into the mechanics of flash messages in Phoenix, using a simple book-tracking app as a real-world example. By the end, you'll know how to eliminate unwanted empty alerts, customize flash keys, and keep your HTML clean and semantic. Let's explore seven key insights that will help you take full control of flash messages in your Phoenix projects.
1. Flash Messages Are Part of the Session
In Phoenix, flash messages are stored in the session and are available only for the next request. They are typically used in conjunction with the fetch_flash plug, which is included in the :browser pipeline by default. When you define a controller action that sets a flash message (e.g., conn |> put_flash(:info, "Book created!")), the message is saved to the session. On the subsequent page load, the message is displayed and then automatically removed. This means flash messages are ephemeral — they appear exactly once, which is perfect for success or error notifications.

2. The Default Layout Always Renders Flash Tags
When you generate a new Phoenix app, the default layout file (lib/your_app_web/layout/app.html.heex) includes HTML elements for flash messages, even when no flash is present. For example, you'll see something like:
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
This always renders the <p> tags. When there's no flash, the tags are empty but still occupy space in the DOM. This can lead to visual gaps or misalignment if not handled properly. Many beginners find this behavior confusing — they expect the tags to disappear when empty, but Phoenix intentionally keeps them for consistency.
3. Empty Flash Tags Cause Unwanted UI Gaps
Because the layout always includes flash tags, empty alerts will still take up vertical space (depending on your CSS). This can push other elements down, creating an awkward layout shift. For example, in the Phoenix default scaffold, the flash messages are placed inside the <header> or directly after the navigation. If you inspect the page after a fresh load without any flash, you'll see empty <p> elements. This is not just a cosmetic issue — it can affect accessibility by leaving empty landmark roles (like role="alert") that screen readers might announce. The good news is there are straightforward solutions to handle this.
4. Use CSS :empty to Hide Empty Alerts Efficiently
The simplest way to hide empty flash message tags is with a single line of CSS. Phoenix's default stylesheet (assets/css/phoenix.css) often includes this rule:
.alert:empty {
display: none;
}
The :empty pseudo-class targets elements that have no child nodes — including text content. So if get_flash returns an empty string, the <p> tag is empty and will be hidden. This approach works instantly without modifying any Elixir code. However, note that it only hides the element; the HTML is still present. For most use cases, this is perfectly fine and keeps your templates consistent. It's also a good performance optimization because you avoid conditional rendering logic on the server side.
5. Alternatively, Conditionally Render Flash in Layouts
If you prefer a cleaner HTML output (no empty tags at all), you can modify the layout to render flash messages only when they exist. Replace the hardcoded <p> with a conditional:

<%= if flash = get_flash(@conn) do %>
<% {key, msg} = List.first(flash) %>
<p class="alert alert-<%= key %>" role="alert"><%= msg %></p>
<% end %>
This approach loops over the flash map (which typically contains only one key-value pair) and renders a single tag. You'll need to handle multiple flash keys if you want to show both info and error simultaneously (though that's rare). The benefit is zero empty DOM nodes, which can improve accessibility and reduce clutter. The downside is slightly more template logic.
6. Customize Flash Keys for Different Types
Phoenix's put_flash/3 accepts any atom as the key. The standard convention uses :info and :error, but you can add custom keys like :success, :warning, or :notice. To style them, simply add the corresponding CSS classes (e.g., .alert-success). In your controller:
put_flash(conn, :success, "Book added!")
Then in the layout, map each key to a separate <p> tag with the appropriate class. This gives you fine-grained control over styling and semantics. Remember to update your CSS to handle the new classes. Custom keys make your flash messages more expressive and align with common UI patterns like Bootstrap's alert variants.
7. Testing Flash Messages Ensures Reliability
When you rely on flash messages for user feedback, it's crucial to test that they appear correctly. In Phoenix, you can use ConnCase and assert the flash is set after a controller action. For example:
test "creates a book and shows flash" do
conn = post(conn, Routes.book_path(conn, :create), book: %{title: "Test"})
assert get_flash(conn, :info) == "Book created successfully."
end
You can also integration test using Phoenix.ConnTest or with LiveViewTest if you use LiveView. Testing ensures that your flash messages are not accidentally omitted or overwritten. It also catches regressions when you refactor your layout or controller logic. Make flash testing a standard part of your suite, especially for CRUD operations.
By understanding these seven aspects of flash messages in Phoenix, you'll be able to implement clean, accessible, and reliable notifications. Whether you choose the CSS-only fix or conditional rendering, the key is to handle empty cases gracefully. Start with the simple CSS approach and gradually adopt custom keys and testing as your app grows. Flash messages may be small, but they have a big impact on user experience — so give them the attention they deserve!