fteschke
How do you create responsive images in your Phoenix apps?
How do you create responsive images with phoenix?
I’d love to merge my separate 11ty/netlify landing page into the Phoenix app.
Responsive images are the last big stumbling block for me. Most JAMStack frameworks have good out-of-the-box solutions, e.g. next.js and gatsby.
It would be great to have an easy to use, performant solution for Phoenix.
That doesn’t impact page loading speeds/page rank.
Here’s my best solution so far. Resized using Image at compile time.
Stored in priv/static/resized, served and cached via normal Plug.Static setup.
Would love to get some feedback and to hear about how you guys handle this.
Usage:
use ResponsiveImage
~H(<img src={src("input.jpg", 300)} />)
~H(<img srcset={srcset("input.jpg", [300, 600, 900])} src={...} sizes="50vw" />)
Module:
defmodule ResponsiveImage do
defmacro __using__(_opts) do
quote do
import unquote(__MODULE__)
Module.register_attribute(__MODULE__, :image, accumulate: true)
@before_compile unquote(__MODULE__)
end
end
defmacro src(path, width) do
Module.put_attribute(__CALLER__.module, :image, {path, width})
quote do
unquote(img_src(path, width))
end
end
defmacro srcset(path, widths) do
for width <- widths, do: Module.put_attribute(__CALLER__.module, :image, {path, width})
quote do
unquote(widths |> Enum.map(&"#{img_src(path, &1)} #{&1}w") |> Enum.join(", "))
end
end
defmacro __before_compile__(_env) do
{duration, _} =
:timer.tc(fn ->
ResponsiveImage.resize(Module.get_attribute(__CALLER__.module, :image))
end)
IO.puts("Image resize took #{duration / 1_000_000}s")
end
def resize(attr) when is_list(attr) do
for {path, width} <- attr, do: resize(path, width)
end
def resize(path, width) do
out_path = out_path(path, width)
if not File.exists?(out_path) do
IO.puts("Writing #{out_path}")
out_path |> Path.dirname() |> File.mkdir_p!()
path
|> in_path()
|> Image.open!()
|> then(&Image.resize!(&1, width / Image.width(&1)))
|> Image.write!(out_path)
end
end
defp without_ext(path), do: "#{Path.dirname(path)}/#{Path.basename(path, Path.extname(path))}"
defp in_path(path), do: "priv/static/images/#{path}"
defp img_src(path, width), do: "/resized/#{without_ext(path)}_#{width}.avif"
defp out_path(path, width), do: "priv/static#{img_src(path, width)}"
end
Most Liked
LostKobrakai
I use a mix compiler to resize images:
https://github.com/LostKobrakai/kobrakai_elixir/blob/main/lib/mix/tasks/images.compile.ex
I also have played with on demand resizing behind a cdn using image in a plug:
https://github.com/LostKobrakai/kobrakai_elixir/blob/main/lib/kobrakai_web/plugs/image_plug.ex
Last Post!
fteschke
Popular in Discussions
Other popular topics
Latest Phoenix Threads
Chat & Discussions>Discussions
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









