Adding Favicon and Manifest Files to a Phoenix app

Note: This guide is based on Phoenix 1.7

You have a brand new Phoenix app in the works and have some favicon assets ready. For example, uploading an image to a service like Favicon Generator will get you a handful of files:

  • android-chrome-192x192.png
  • android-chrome-512x512.png
  • apple-touch-icon.png
  • favicon-16x16.png
  • favicon-32x32.png
  • favicon.ico
  • site.webmanifest (I rename this to manifest.json)

Next you plop them into your_app/priv/static/ and add links into your_app/lib/your_app_web/components/layouts/root.html.heex.

<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <link rel="apple-touch-icon" sizes="180x180" href={~p"/apple-touch-icon.png"} />
    <link rel="icon" type="image/png" sizes="32x32" href={~p"/favicon-32x32.png"} />
    <link rel="icon" type="image/png" sizes="16x16" href={~p"/favicon-16x16.png"} />
    <link rel="manifest" href={~p"/manifest.json"} />
  </head>
  <body>
    <%= @inner_content %>
  </body>
</html>

But then you immediately get hit with errors. First, trying to GET a resource /favicon-32x32.png will give you a router error that there is no GET route defined:

[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /favicon-32x32.png (YourAppWeb.Router)

Second, the compiler will give you a warning too:

warning: no route path for YourAppWeb.Router matches "/favicon-32x32.png"

That compiler warning comes curtesy of the new verified routes sigil ~p"/favicon-32x32.png", which checks the validity of routes at compile time.

Ok, so recall that when a request hits a Phoenix endpoint, it passes through a pipeline of Plugs. If you open up lib/your_app_web/endpoint.ex, you can see the Plug that handles static assets:

...
  # Serve at "/" the static files from "priv/static" directory.
  #
  # You should set gzip to true if you are running phx.digest
  # when deploying your static files in production.
  plug Plug.Static,
    at: "/",
    from: :personal,
    gzip: false,
    only: PersonalWeb.static_paths()
...

In your IDE, highlight static_paths() and go to the implementation. That will open up lib/your_app_web.ex.

  def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)

Add the new assets to the static_paths function.


  def static_paths, do: ~w(assets fonts images favicon.ico robots.txt manifest.json apple-touch-icon.png favicon-32x32.png favicon-16x16.png)

That’s all there is to it!