diff --git a/services/frontend-leptos/frontend/src/features/messages/components/image_grid.rs b/services/frontend-leptos/frontend/src/features/messages/components/image_grid.rs
new file mode 100644
index 0000000..55cca3f
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/components/image_grid.rs
@@ -0,0 +1,8 @@
+use leptos::prelude::*;
+
+#[component]
+pub fn ImageGrid() -> impl IntoView {
+ view! {
+
"Image Grid (placeholder)"
+ }
+}
diff --git a/services/frontend-leptos/frontend/src/features/messages/components/message_card.rs b/services/frontend-leptos/frontend/src/features/messages/components/message_card.rs
new file mode 100644
index 0000000..a38b482
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/components/message_card.rs
@@ -0,0 +1,8 @@
+use leptos::prelude::*;
+
+#[component]
+pub fn MessageCard() -> impl IntoView {
+ view! {
+ "Message Card (placeholder)"
+ }
+}
diff --git a/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs b/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs
new file mode 100644
index 0000000..057e1d4
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/components/message_feed.rs
@@ -0,0 +1,8 @@
+use leptos::prelude::*;
+
+#[component]
+pub fn MessageFeed() -> impl IntoView {
+ view! {
+ "Message Feed (placeholder)"
+ }
+}
diff --git a/services/frontend-leptos/frontend/src/features/messages/components/mod.rs b/services/frontend-leptos/frontend/src/features/messages/components/mod.rs
new file mode 100644
index 0000000..644a7cd
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/components/mod.rs
@@ -0,0 +1,3 @@
+pub mod message_feed;
+pub mod message_card;
+pub mod image_grid;
diff --git a/services/frontend-leptos/frontend/src/features/messages/hooks/mod.rs b/services/frontend-leptos/frontend/src/features/messages/hooks/mod.rs
new file mode 100644
index 0000000..4533575
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/hooks/mod.rs
@@ -0,0 +1 @@
+pub mod use_messages;
diff --git a/services/frontend-leptos/frontend/src/features/messages/hooks/use_messages.rs b/services/frontend-leptos/frontend/src/features/messages/hooks/use_messages.rs
new file mode 100644
index 0000000..4bbac14
--- /dev/null
+++ b/services/frontend-leptos/frontend/src/features/messages/hooks/use_messages.rs
@@ -0,0 +1,201 @@
+use leptos::prelude::*;
+use shared_types::message::{MessageRecord, PageResult};
+use crate::api::messages::{get_messages, reanalyze_message, reanalyze_batch};
+use std::collections::HashMap;
+use std::sync::Arc;
+use wasm_bindgen_futures::spawn_local;
+
+/// Merges current messages with incoming messages, deduplicating by ID and sorting
+pub fn merge_messages(current: &[MessageRecord], incoming: &[MessageRecord]) -> Vec {
+ let mut by_id: HashMap = current.iter().map(|m| (m.id.clone(), m.clone())).collect();
+ for msg in incoming {
+ by_id.insert(msg.id.clone(), msg.clone());
+ }
+ let mut merged: Vec = by_id.into_values().collect();
+ merged.sort_by(|a, b| b.created_at.cmp(&a.created_at).then_with(|| b.id.cmp(&a.id)));
+ merged
+}
+
+/// Callback type for fetch_messages
+pub type FetchMessagesCallback = Arc;
+/// Callback type for load_more
+pub type LoadMoreCallback = Arc;
+/// Callback type for reanalyze
+pub type ReanalyzeCallback = Arc;
+/// Callback type for reanalyze_all_errors
+pub type ReanalyzeAllErrorsCallback = Arc;
+
+/// State returned by use_messages hook
+#[derive(Clone)]
+pub struct MessagesState {
+ /// Current list of messages
+ pub messages: RwSignal>,
+ /// Whether the initial fetch is in progress
+ pub loading: ReadSignal,
+ /// Whether we're loading more messages
+ pub loading_more: RwSignal,
+ /// Pagination cursor for next page
+ pub cursor: RwSignal