Manipulating URLs is a common task when building web applications, and sometimes you need to update a specific query parameter—like changing the page in a paginated list or adding a filter. In Elixir, you can do this cleanly using the URI module.

Here's a utility function that takes a URL, a query parameter key, and a new value, and returns the updated URL:

defmodule UrlHelper do
  def update_query_param(url, key, new_value) do
    uri = URI.parse(url)

    updated_query_string =
      URI.decode_query(uri.query || "")
      |> Map.put(key, value)
      |> URI.encode_query()

    %{uri | query: updated_query_string}
    |> URI.to_string()
  end
end

Example usage

UrlHelper.update_query_param("https://example.com/search?q=elixir&page=2", "page", "3")
# => "https://example.com/search?q=elixir&page=3"

UrlHelper.update_query_param("https://example.com", "lang", "en")
# => "https://example.com?lang=en"

How it works • URI.parse/1 splits the URL into its components. • URI.decode_query/1 converts the query string into a map. • Map.put/3 updates or inserts the desired key-value pair. • URI.encode_query/1 serializes the updated map back into a query string. • URI.to_string/1 reconstructs the full URL.

This approach keeps your URL manipulation declarative and composable. It works equally well for URLs with no query string and will preserve existing parts like the path or fragment.

If you need to handle multiple values for the same key (e.g., ?tags=elixir&tags=phoenix), you’ll need to work with lists and keyword lists instead of a map—but for most use cases, the above pattern is sufficient.