diff --git a/lib/sloane_sh/assets/image.ex b/lib/sloane_sh/assets/image.ex
index e69de29..5dae625 100644
--- a/lib/sloane_sh/assets/image.ex
+++ b/lib/sloane_sh/assets/image.ex
@@ -0,0 +1,19 @@
+defmodule SloaneSH.Assets.Image do
+  alias SloaneSH.Asset
+  alias SloaneSH.OutputDirs
+
+  @behaviour Asset
+
+  @impl Asset
+  def extensions(_cfg), do: ~w[.jpg .jpeg .png .webp .gif]
+
+  @impl Asset
+  def attrs(_cfg, _path, _data) do
+    {:ok, %{}}
+  end
+
+  @impl Asset
+  def render(cfg, _ctx, path, data, _attrs) do
+    {:ok, [{OutputDirs.image(cfg, path), data}]}
+  end
+end
diff --git a/lib/sloane_sh/build.ex b/lib/sloane_sh/build.ex
index ce4abee..f8a7d97 100644
--- a/lib/sloane_sh/build.ex
+++ b/lib/sloane_sh/build.ex
@@ -4,7 +4,7 @@ defmodule SloaneSH.Build do
   alias SloaneSH.Context
 
   def run(%Context{} = ctx) do
-    assets = ctx.posts ++ ctx.pages
+    assets = ctx.posts ++ ctx.pages ++ ctx.images
 
     File.mkdir_p!(ctx.config.output_dir)
 
diff --git a/lib/sloane_sh/config.ex b/lib/sloane_sh/config.ex
index 7c59a48..a616b2e 100644
--- a/lib/sloane_sh/config.ex
+++ b/lib/sloane_sh/config.ex
@@ -9,6 +9,7 @@ defmodule SloaneSH.Config do
   typedstruct do
     field :pages_dir, String.t(), enforce: true
     field :posts_dir, String.t(), enforce: true
+    field :images_dir, String.t(), enforce: true
     field :output_dir, String.t(), enforce: true
   end
 
@@ -18,6 +19,7 @@ defmodule SloaneSH.Config do
     %Config{
       pages_dir: Path.join(priv, "site/pages"),
       posts_dir: Path.join(priv, "site/posts"),
+      images_dir: Path.join(priv, "site/images"),
       output_dir: Path.join(priv, "output")
     }
   end
diff --git a/lib/sloane_sh/context.ex b/lib/sloane_sh/context.ex
index 9404f45..a006a0f 100644
--- a/lib/sloane_sh/context.ex
+++ b/lib/sloane_sh/context.ex
@@ -10,19 +10,22 @@ defmodule SloaneSH.Context do
   alias SloaneSH.Asset
   alias SloaneSH.Assets.Page
   alias SloaneSH.Assets.Post
+  alias SloaneSH.Assets.Image
   alias __MODULE__
 
   typedstruct do
     field :config, Config.t(), enforce: true
     field :pages, [Asset.t()], default: []
     field :posts, [Asset.t()], default: []
+    field :images, [Asset.t()], default: []
   end
 
   def new(cfg \\ Config.default()) do
     pages = load_assets(cfg, Page, cfg.pages_dir)
     posts = load_assets(cfg, Post, cfg.posts_dir)
+    images = load_assets(cfg, Image, cfg.images_dir)
 
-    %Context{config: cfg, pages: pages, posts: posts}
+    %Context{config: cfg, pages: pages, posts: posts, images: images}
   end
 
   defp load_assets(cfg, mod, src_dir) do
diff --git a/lib/sloane_sh/output_dirs.ex b/lib/sloane_sh/output_dirs.ex
index 6caf30f..a311f6f 100644
--- a/lib/sloane_sh/output_dirs.ex
+++ b/lib/sloane_sh/output_dirs.ex
@@ -12,6 +12,13 @@ defmodule SloaneSH.OutputDirs do
     cfg.output_dir |> Path.join(path) |> prettify_html_path()
   end
 
+  def image(cfg, src) do
+    path = Path.relative_to(src, cfg.images_dir)
+    path = Path.join("assets/images", path)
+
+    cfg.output_dir |> Path.join(path)
+  end
+
   def prettify_html_path(path) do
     file = Path.basename(path)
     [without_extension | _] = String.split(file, ".", parts: 2)
diff --git a/assets/img/favicon-32x32.png b/priv/site/images/favicon-32x32.png
similarity index 100%
rename from assets/img/favicon-32x32.png
rename to priv/site/images/favicon-32x32.png