diff --git a/config/config.exs b/config/config.exs
index 4a3f784..1532e6a 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -51,4 +51,6 @@ config :tailwind,
     cd: Path.expand("../assets", __DIR__)
   ]
 
+config :flop, repo: Core.Repo
+
 import_config "#{config_env()}.exs"
diff --git a/lib/core/posts.ex b/lib/core/posts.ex
index 5a00dc6..7594db5 100644
--- a/lib/core/posts.ex
+++ b/lib/core/posts.ex
@@ -37,6 +37,13 @@ defmodule Core.Posts do
     |> Core.Repo.all()
   end
 
+  def list_published_posts(kind, params \\ %{}) do
+    Post.Query.base()
+    |> Post.Query.published()
+    |> Post.Query.where_kind(kind)
+    |> Flop.validate_and_run(params, for: Schema.Post)
+  end
+
   def publish_date(%Schema.Post{published_at: nil}), do: nil
 
   def publish_date(%Schema.Post{published_at: published_at}) do
diff --git a/lib/schema/post.ex b/lib/schema/post.ex
index 4e54648..033c38c 100644
--- a/lib/schema/post.ex
+++ b/lib/schema/post.ex
@@ -2,6 +2,20 @@ defmodule Schema.Post do
   @moduledoc false
   use Schema
 
+  @derive {
+    Flop.Schema,
+    filterable: [],
+    sortable: [:published_at, :id],
+    pagination_types: [:first, :last],
+    default_order: %{
+      order_by: [:published_at, :id],
+      order_directions: [:desc, :desc]
+    },
+    default_pagination_type: :first,
+    default_limit: 20,
+    max_limit: 50
+  }
+
   @post_kinds ~w[status blog]a
 
   schema "posts" do
diff --git a/lib/web/components/core_components.ex b/lib/web/components/core_components.ex
index a90fa48..52e8986 100644
--- a/lib/web/components/core_components.ex
+++ b/lib/web/components/core_components.ex
@@ -211,4 +211,44 @@ defmodule Web.CoreComponents do
     </div>
     """
   end
+
+  @doc """
+  Renders pagination controls for navigating through a paginated list.
+
+  ## Examples
+
+      <.pagination meta={@meta} path={~p"/blog"} schema={Schema.Post} />
+  """
+  attr :meta, :map, required: true, doc: "the pagination metadata from Flop"
+  attr :path, :string, required: true, doc: "the base path for pagination links"
+  attr :schema, :atom, required: true, doc: "the schema module for Flop.Phoenix.build_path"
+  attr :class, :string, default: "mt-4", doc: "additional CSS classes"
+
+  def pagination(assigns) do
+    ~H"""
+    <div class={["flex justify-between", @class]}>
+      <%= if @meta.has_previous_page? do %>
+        <.link
+          navigate={Flop.Phoenix.build_path(@path, Flop.to_previous_cursor(@meta), for: @schema)}
+          class="text-gray-500 hover:text-gray-800"
+        >
+          Newer posts
+        </.link>
+      <% else %>
+        <div></div>
+      <% end %>
+
+      <%= if @meta.has_next_page? do %>
+        <.link
+          navigate={Flop.Phoenix.build_path(@path, Flop.to_next_cursor(@meta), for: @schema)}
+          class="text-gray-500 hover:text-gray-800"
+        >
+          Older posts
+        </.link>
+      <% else %>
+        <div></div>
+      <% end %>
+    </div>
+    """
+  end
 end
diff --git a/lib/web/controllers/blog_controller.ex b/lib/web/controllers/blog_controller.ex
index 3c039ee..56214f6 100644
--- a/lib/web/controllers/blog_controller.ex
+++ b/lib/web/controllers/blog_controller.ex
@@ -1,8 +1,8 @@
 defmodule Web.BlogController do
   use Web, :controller
 
-  def index(conn, _params) do
-    blogs = Core.Posts.get_published_recent_posts(:blog)
+  def index(conn, params) do
+    {:ok, {blogs, meta}} = Core.Posts.list_published_posts(:blog, params)
 
     blogs_by_year =
       blogs
@@ -12,7 +12,7 @@ defmodule Web.BlogController do
       end)
       |> Enum.sort_by(fn {year, _} -> year end, :desc)
 
-    render(conn, :index, blogs_by_year: blogs_by_year)
+    render(conn, :index, blogs_by_year: blogs_by_year, meta: meta)
   end
 
   def show(conn, %{"year" => year, "month" => month, "day" => day, "slug" => slug}) do
diff --git a/lib/web/controllers/blog_html/index.html.heex b/lib/web/controllers/blog_html/index.html.heex
index 1671f0c..4c06abb 100644
--- a/lib/web/controllers/blog_html/index.html.heex
+++ b/lib/web/controllers/blog_html/index.html.heex
@@ -26,5 +26,7 @@
         </div>
       <% end %>
     </div>
+
+    <.pagination meta={@meta} path={~p"/blog"} schema={Schema.Post} class="my-2" />
   </div>
 </div>
diff --git a/lib/web/controllers/status_controller.ex b/lib/web/controllers/status_controller.ex
index e16afca..b03795b 100644
--- a/lib/web/controllers/status_controller.ex
+++ b/lib/web/controllers/status_controller.ex
@@ -1,9 +1,9 @@
 defmodule Web.StatusController do
   use Web, :controller
 
-  def index(conn, _params) do
-    statuses = Core.Posts.get_published_recent_posts(:status)
-    render(conn, :index, statuses: statuses)
+  def index(conn, params) do
+    {:ok, {statuses, meta}} = Core.Posts.list_published_posts(:status, params)
+    render(conn, :index, statuses: statuses, meta: meta)
   end
 
   def show(conn, %{"status_id" => status_id}) do
diff --git a/lib/web/controllers/status_html/index.html.heex b/lib/web/controllers/status_html/index.html.heex
index 5817bf5..f276573 100644
--- a/lib/web/controllers/status_html/index.html.heex
+++ b/lib/web/controllers/status_html/index.html.heex
@@ -8,4 +8,6 @@
       <.status_entry status={status} />
     <% end %>
   </div>
+
+  <.pagination meta={@meta} path={~p"/microblog"} schema={Schema.Post} class="my-2" />
 </div>
diff --git a/mix.exs b/mix.exs
index 25ede0d..18337c0 100644
--- a/mix.exs
+++ b/mix.exs
@@ -62,6 +62,8 @@ defmodule SlaonelyButSurely.MixProject do
       {:slugify, "~> 1.3"},
       {:timex, "~> 3.7"},
       {:mdex, "~> 0.5.0"},
+      {:flop, "~> 0.26.1"},
+      {:flop_phoenix, "~> 0.24.1"},
 
       # Added dev and/or test dependencies
       {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
diff --git a/mix.lock b/mix.lock
index f23d922..04eb2af 100644
--- a/mix.lock
+++ b/mix.lock
@@ -19,6 +19,8 @@
   "faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"},
   "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
   "floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
+  "flop": {:hex, :flop, "0.26.1", "f0e9c6895cf876f667e9ff1c0398e53df87087fcd82d9cea8989332b9c0e1358", [:mix], [{:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "5fcab8a1ee78111159fc4752dc9823862343b6d6bd527ff947ec1e1c27018485"},
+  "flop_phoenix": {:hex, :flop_phoenix, "0.24.1", "0eee8721e984cd9cbbfc90357c355fcf5c57da9e0617159f432d35843d01b671", [:mix], [{:flop, ">= 0.23.0 and < 0.27.0", [hex: :flop, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "543c8eb70a29c0255b778df855f0de303290f88159fc3e008ce0ac4ace48e6ea"},
   "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"},
   "hackney": {:hex, :hackney, "1.22.0", "4efc68df70322d4d2e3d2744e9bd191a39a0cb8d08c35379a08d9fb0f040d595", [:rebar3], [{:certifi, "~> 2.14.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "628569e451820950382be3d3e6481d7c59997e606c7823bddb4ce5d10812dfcb"},
   "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},