Gravatar is a widely used service for associating profile images with email addresses. If your application doesn't store avatars locally, Gravatar offers a reliable fallback based on the user's email hash. Here's a minimal and idiomatic Elixir module to generate Gravatar URLs.

The helper module

defmodule AvatarHelper do
  @moduledoc """
  Generates a Gravatar URL based on a user's email.
  """

  @gravatar_base_url "https://secure.gravatar.com/avatar"
  @default_gravatar_id "00000000000000000000000000000000"

  @doc """
  Returns the Gravatar URL for the given object.

  ## Options

    * `:size` – image size in pixels (default: 180)
    * `:default` – default Gravatar image type if email is missing (default: `"mp"`)

  ## Examples

      iex> AvatarHelper.avatar_url(%{email: "user@example.com"})
      "https://secure.gravatar.com/avatar/23463b99b62a72f26ed677cc556c44e8?s=180&d=mp"
  """
  def avatar_url(object, opts \\ []) do
    size = Keyword.get(opts, :size, 180)
    default_image = Keyword.get(opts, :default, "mp")
    params = "?s=#{size}&d=#{default_image}"

    gravatar_id =
      case Map.get(object, :email) do
        email when is_binary(email) and email != "" -> md5_hash(email)
        _ -> @default_gravatar_id
      end

    "#{@gravatar_base_url}/#{gravatar_id}#{params}"
  end

  defp md5_hash(email) do
    email
    |> String.downcase()
    |> then(&:crypto.hash(:md5, &1))
    |> Base.encode16(case: :lower)
  end
end

Why this approach?

  • No local storage checks – It’s purely Gravatar-based.
  • Fallback behavior – If no valid email is present, it uses a default blank hash.
  • Customizable – You can pass options like size (:size) and default image type (:default), such as "identicon", "retro", or "mp".

Usage in Phoenix

Use this in a template or LiveView like this:

<img src={AvatarHelper.avatar_path(@user)} alt="User avatar" />

This keeps your app simple and your user avatars globally consistent.