diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs
index e5c6ce7ac6..efd566806d 100644
--- a/src/subcommand/server.rs
+++ b/src/subcommand/server.rs
@@ -3,7 +3,7 @@ use super::*;
 use {
   self::{
     deserialize_ordinal_from_str::DeserializeOrdinalFromStr,
-    templates::{ordinal::OrdinalHtml, root::RootHtml, Content},
+    templates::{block::BlockHtml, ordinal::OrdinalHtml, root::RootHtml, Content},
     tls_acceptor::TlsAcceptor,
   },
   clap::ArgGroup,
@@ -250,22 +250,7 @@ impl Server {
     index: extract::Extension<Arc<Index>>,
   ) -> impl IntoResponse {
     match index.block_with_hash(hash) {
-      Ok(Some(block)) => (
-        StatusCode::OK,
-        Html(format!(
-          "<ul>\n{}</ul>",
-          block
-            .txdata
-            .iter()
-            .enumerate()
-            .map(|(i, tx)| format!(
-              "  <li>{i} - <a href='/tx/{}'>{}</a></li>\n",
-              tx.txid(),
-              tx.txid()
-            ))
-            .collect::<String>()
-        )),
-      ),
+      Ok(Some(block)) => BlockHtml::new(block).page().into_response(),
       Ok(None) => (
         StatusCode::NOT_FOUND,
         Html(
@@ -274,7 +259,8 @@ impl Server {
             .unwrap_or_default()
             .to_string(),
         ),
-      ),
+      )
+        .into_response(),
       Err(error) => {
         eprintln!("Error serving request for block with hash {hash}: {error}");
         (
@@ -286,6 +272,7 @@ impl Server {
               .to_string(),
           ),
         )
+          .into_response()
       }
     }
   }
diff --git a/src/subcommand/server/templates.rs b/src/subcommand/server/templates.rs
index 1be0d72d26..553b9b54df 100644
--- a/src/subcommand/server/templates.rs
+++ b/src/subcommand/server/templates.rs
@@ -1,5 +1,6 @@
 use {super::*, boilerplate::Display};
 
+pub(crate) mod block;
 pub(crate) mod ordinal;
 pub(crate) mod root;
 
diff --git a/src/subcommand/server/templates/block.rs b/src/subcommand/server/templates/block.rs
new file mode 100644
index 0000000000..1545775ad9
--- /dev/null
+++ b/src/subcommand/server/templates/block.rs
@@ -0,0 +1,51 @@
+use super::*;
+
+#[derive(Display)]
+pub(crate) struct BlockHtml {
+  hash: BlockHash,
+  txids: Vec<Txid>,
+}
+
+impl BlockHtml {
+  pub(crate) fn new(block: Block) -> Self {
+    Self {
+      hash: block.header.block_hash(),
+      txids: block.txdata.iter().map(Transaction::txid).collect(),
+    }
+  }
+}
+
+impl Content for BlockHtml {
+  fn title(&self) -> String {
+    format!("Block {}", self.hash)
+  }
+
+  fn page(self) -> PageHtml {
+    PageHtml {
+      content: Box::new(self),
+    }
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  use {super::*, pretty_assertions::assert_eq, unindent::Unindent};
+
+  #[test]
+  fn block_html() {
+    assert_eq!(
+      BlockHtml::new(bitcoin::blockdata::constants::genesis_block(
+        Network::Bitcoin
+      ))
+      .to_string(),
+      "
+        <h1>Block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f</h1>
+        <h2>Transactions</h2>
+        <ul>
+          <li><a href=/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b>4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b</a></li>
+        </ul>
+      "
+      .unindent()
+    );
+  }
+}
diff --git a/templates/block.html b/templates/block.html
new file mode 100644
index 0000000000..55116e7788
--- /dev/null
+++ b/templates/block.html
@@ -0,0 +1,7 @@
+<h1>Block {{ self.hash }}</h1>
+<h2>Transactions</h2>
+<ul>
+%% for txid in &self.txids {
+  <li><a href=/tx/{{txid}}>{{txid}}</a></li>
+%% }
+</ul>
diff --git a/tests/server.rs b/tests/server.rs
index c83d911ce6..1abaae0d9e 100644
--- a/tests/server.rs
+++ b/tests/server.rs
@@ -150,7 +150,7 @@ fn root() {
 }
 
 #[test]
-fn transactions() {
+fn block() {
   let mut state = State::new();
 
   state.blocks(101);
@@ -168,9 +168,11 @@ fn transactions() {
   state.request_regex(
     &format!("block/{}", blocks[0]),
     200,
-    ".*<ul>
-  <li>0 - <a href='/tx/[[:xdigit:]]{64}'>[[:xdigit:]]{64}</a></li>
-  <li>1 - <a href='/tx/[[:xdigit:]]{64}'>[[:xdigit:]]{64}</a></li>
+    ".*<h1>Block [[:xdigit:]]{64}</h1>
+<h2>Transactions</h2>
+<ul>
+  <li><a href=/tx/[[:xdigit:]]{64}>[[:xdigit:]]{64}</a></li>
+  <li><a href=/tx/[[:xdigit:]]{64}>[[:xdigit:]]{64}</a></li>
 </ul>.*",
   );
 }