diff --git a/frontend/src/app.rs b/frontend/src/app.rs
index 272a09d..f51a095 100644
--- a/frontend/src/app.rs
+++ b/frontend/src/app.rs
@@ -1,4 +1,5 @@
use crate::components::layout::protected::Protected;
+use crate::components::ui::skeleton::Skeleton;
use crate::components::torrent::table::TorrentTable;
use crate::components::auth::login::Login;
use crate::components::auth::setup::Setup;
@@ -146,33 +147,33 @@ fn InnerApp() -> impl IntoView {
// Sidebar skeleton
// Main content skeleton
diff --git a/frontend/src/components/ui/mod.rs b/frontend/src/components/ui/mod.rs
index 7abfe82..90441c1 100644
--- a/frontend/src/components/ui/mod.rs
+++ b/frontend/src/components/ui/mod.rs
@@ -14,6 +14,7 @@ pub mod select;
pub mod separator;
pub mod sheet;
pub mod sidenav;
+pub mod skeleton;
pub mod svg_icon;
pub mod table;
pub mod theme_toggle;
diff --git a/frontend/src/components/ui/skeleton.rs b/frontend/src/components/ui/skeleton.rs
new file mode 100644
index 0000000..75d7f7d
--- /dev/null
+++ b/frontend/src/components/ui/skeleton.rs
@@ -0,0 +1,13 @@
+use leptos::prelude::*;
+use tw_merge::tw_merge;
+
+#[component]
+pub fn Skeleton(
+ #[prop(optional, into)] class: String,
+) -> impl IntoView {
+ let class = tw_merge!(
+ "animate-pulse rounded-md bg-muted",
+ class
+ );
+ view! { }
+}