When working with redirects, internal links, or LiveView navigation, you often want to turn an absolute URL into a relative one. Given a URL like:
https://example.com/some/path?page=2#section
the goal is to end up with:
/some/path?page=2#section
The non-idiomatic approach
A common first attempt is to manually concatenate path, query, and fragment after parsing the URL. While this works, it pushes URL semantics into string logic and is easy to get wrong.
Elixir’s standard library gives us a cleaner option.
The idiomatic solution using URI
The URI module is designed so that parsing and serialization are inverse operations. The trick is to parse the URL, drop the parts you don’t need, and convert it back to a string.
url = "https://example.com/some/path?page=2#section"
relative =
url
|> URI.parse()
|> Map.take([:path, :query, :fragment])
|> then(&struct(URI, &1))
|> URI.to_string()
This produces:
"/some/path?page=2#section"
Why this approach works well
- No manual handling of
?or# - Correct behavior when
queryorfragmentis missing - Clear intent: keep only the parts relevant for a relative URL
- Uses only Elixir’s standard library
A small helper function
If you need this in more than one place, wrapping it in a helper keeps your codebase tidy:
def relative_url(url) do
url
|> URI.parse()
|> Map.take([:path, :query, :fragment])
|> then(&struct(URI, &1))
|> URI.to_string()
end
This pattern keeps URL handling declarative and avoids brittle string manipulation, which is exactly what the URI module is there for.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.