diff --git a/lemmy-translations b/lemmy-translations
index b4c63029e..62c841802 160000
--- a/lemmy-translations
+++ b/lemmy-translations
@@ -1 +1 @@
-Subproject commit b4c63029e598c022a04fc21acb45855645bd6794
+Subproject commit 62c8418021bc39543c87b4ae3dcf2419d13f61e0
diff --git a/src/shared/components/common/loading-skeleton.tsx b/src/shared/components/common/loading-skeleton.tsx
new file mode 100644
index 000000000..ab146b8a9
--- /dev/null
+++ b/src/shared/components/common/loading-skeleton.tsx
@@ -0,0 +1,118 @@
+import { Component } from "inferno";
+
+interface LoadingSkeletonProps {
+  itemCount?: number;
+}
+
+interface LoadingSkeletonLineProps {
+  size: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
+}
+
+class LoadingSkeletonLine extends Component<LoadingSkeletonLineProps, any> {
+  render() {
+    const className = "placeholder placeholder-lg col-" + this.props.size;
+    return (
+      <p className="placeholder-glow m-0">
+        <span className={`${className} h-100`} />
+      </p>
+    );
+  }
+}
+
+export class PostsLoadingSkeleton extends Component<LoadingSkeletonProps, any> {
+  render() {
+    return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
+      <PostsLoadingSkeletonItem key={index} />
+    ));
+  }
+}
+
+class PostThumbnailLoadingSkeleton extends Component<any, any> {
+  render() {
+    return (
+      <div className="thumbnail rounded d-flex justify-content-center placeholder-glow">
+        <span className="placeholder placeholder-lg h-100 w-100 rounded" />
+      </div>
+    );
+  }
+}
+
+class PostsLoadingSkeletonItem extends Component<any, any> {
+  render() {
+    return (
+      <div className="my-3">
+        <div className="col flex-grow-1">
+          <div className="row">
+            <div className="col flex-grow-0 order-last order-sm-first">
+              <PostThumbnailLoadingSkeleton />
+            </div>
+            <div className="col flex-grow-1">
+              <LoadingSkeletonLine size={12} />
+              <LoadingSkeletonLine size={8} />
+              <LoadingSkeletonLine size={4} />
+            </div>
+          </div>
+        </div>
+      </div>
+    );
+  }
+}
+
+export class TrendingCommunitiesLoadingSkeleton extends Component<
+  LoadingSkeletonProps,
+  any
+> {
+  render() {
+    return (
+      <div className="mb-2">
+        {Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
+          <TrendingCommunitiesLoadingSkeletonItem key={index} />
+        ))}
+      </div>
+    );
+  }
+}
+
+class TrendingCommunitiesLoadingSkeletonItem extends Component<any, any> {
+  render() {
+    return (
+      <div className="col flex-grow-1 mt-2 ps-2 pe-4">
+        <div className="row">
+          <div className="col flex-grow-0 pe-0">
+            <div className="d-flex placeholder-glow img-icon">
+              <span className="placeholder placeholder-lg w-100 h-100 rounded-circle" />
+            </div>
+          </div>
+          <div className="col flex-grow-1 pe-0">
+            <LoadingSkeletonLine size={12} />
+          </div>
+        </div>
+      </div>
+    );
+  }
+}
+
+export class CommentsLoadingSkeleton extends Component<any, any> {
+  render() {
+    return Array.from({ length: this.props.itemCount ?? 10 }, (_, index) => (
+      <CommentsLoadingSkeletonItem key={index} />
+    ));
+  }
+}
+
+class CommentsLoadingSkeletonItem extends Component<any, any> {
+  render() {
+    return (
+      <div className="col flex-grow-1 my-2 p-2">
+        <div className="row">
+          <div className="col flex-grow-1">
+            <LoadingSkeletonLine size={6} />
+            <LoadingSkeletonLine size={12} />
+            <LoadingSkeletonLine size={7} />
+            <LoadingSkeletonLine size={4} />
+          </div>
+        </div>
+      </div>
+    );
+  }
+}
diff --git a/src/shared/components/community/community.tsx b/src/shared/components/community/community.tsx
index 195de1fec..8f9c87648 100644
--- a/src/shared/components/community/community.tsx
+++ b/src/shared/components/community/community.tsx
@@ -100,6 +100,10 @@ import { PostListings } from "../post/post-listings";
 import { CommunityLink } from "./community-link";
 import { PaginatorCursor } from "../common/paginator-cursor";
 import { getHttpBaseInternal } from "../../utils/env";
+import {
+  CommentsLoadingSkeleton,
+  PostsLoadingSkeleton,
+} from "../common/loading-skeleton";
 import { Sidebar } from "./sidebar";
 import { IRoutePropsWithFetch } from "../../routes";
 
@@ -435,11 +439,7 @@ export class Community extends Component<CommunityRouteProps, State> {
     if (dataType === DataType.Post) {
       switch (this.state.postsRes.state) {
         case "loading":
-          return (
-            <h5>
-              <Spinner large />
-            </h5>
-          );
+          return <PostsLoadingSkeleton />;
         case "success":
           return (
             <PostListings
@@ -472,11 +472,7 @@ export class Community extends Component<CommunityRouteProps, State> {
     } else {
       switch (this.state.commentsRes.state) {
         case "loading":
-          return (
-            <h5>
-              <Spinner large />
-            </h5>
-          );
+          return <CommentsLoadingSkeleton />;
         case "success":
           return (
             <CommentNodes
diff --git a/src/shared/components/home/home.tsx b/src/shared/components/home/home.tsx
index 3843a2806..56309da29 100644
--- a/src/shared/components/home/home.tsx
+++ b/src/shared/components/home/home.tsx
@@ -92,7 +92,7 @@ import { toast } from "../../toast";
 import { CommentNodes } from "../comment/comment-nodes";
 import { DataTypeSelect } from "../common/data-type-select";
 import { HtmlTags } from "../common/html-tags";
-import { Icon, Spinner } from "../common/icon";
+import { Icon } from "../common/icon";
 import { ListingTypeSelect } from "../common/listing-type-select";
 import { SortSelect } from "../common/sort-select";
 import { CommunityLink } from "../community/community-link";
@@ -100,6 +100,11 @@ import { PostListings } from "../post/post-listings";
 import { SiteSidebar } from "./site-sidebar";
 import { PaginatorCursor } from "../common/paginator-cursor";
 import { getHttpBaseInternal } from "../../utils/env";
+import {
+  CommentsLoadingSkeleton,
+  PostsLoadingSkeleton,
+  TrendingCommunitiesLoadingSkeleton,
+} from "../common/loading-skeleton";
 import { RouteComponentProps } from "inferno-router/dist/Route";
 import { IRoutePropsWithFetch } from "../../routes";
 
@@ -531,11 +536,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
   trendingCommunities() {
     switch (this.state.trendingCommunitiesRes?.state) {
       case "loading":
-        return (
-          <h5>
-            <Spinner large />
-          </h5>
-        );
+        return <TrendingCommunitiesLoadingSkeleton itemCount={5} />;
       case "success": {
         const trending = this.state.trendingCommunitiesRes.data.communities;
         return (
@@ -704,11 +705,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
         case "empty":
           return <div style="min-height: 20000px;"></div>;
         case "loading":
-          return (
-            <h5>
-              <Spinner large />
-            </h5>
-          );
+          return <PostsLoadingSkeleton />;
         case "success": {
           const posts = this.state.postsRes.data.posts;
           return (
@@ -744,11 +741,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
     } else {
       switch (this.state.commentsRes.state) {
         case "loading":
-          return (
-            <h5>
-              <Spinner large />
-            </h5>
-          );
+          return <CommentsLoadingSkeleton />;
         case "success": {
           const comments = this.state.commentsRes.data.comments;
           return (
diff --git a/src/shared/components/person/inbox.tsx b/src/shared/components/person/inbox.tsx
index 26b8a7e09..239e33407 100644
--- a/src/shared/components/person/inbox.tsx
+++ b/src/shared/components/person/inbox.tsx
@@ -80,6 +80,7 @@ import { Icon, Spinner } from "../common/icon";
 import { Paginator } from "../common/paginator";
 import { PrivateMessage } from "../private_message/private-message";
 import { getHttpBaseInternal } from "../../utils/env";
+import { CommentsLoadingSkeleton } from "../common/loading-skeleton";
 import { RouteComponentProps } from "inferno-router/dist/Route";
 import { IRoutePropsWithFetch } from "../../routes";
 
@@ -583,11 +584,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
       this.state.mentionsRes.state === "loading" ||
       this.state.messagesRes.state === "loading"
     ) {
-      return (
-        <h1 className="h4">
-          <Spinner large />
-        </h1>
-      );
+      return <CommentsLoadingSkeleton />;
     } else {
       return (
         <div>{this.buildCombined().map(r => this.renderReplyType(r))}</div>
@@ -598,11 +595,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
   replies() {
     switch (this.state.repliesRes.state) {
       case "loading":
-        return (
-          <h1 className="h4">
-            <Spinner large />
-          </h1>
-        );
+        return <CommentsLoadingSkeleton />;
       case "success": {
         const replies = this.state.repliesRes.data.replies;
         return (
@@ -645,11 +638,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
   mentions() {
     switch (this.state.mentionsRes.state) {
       case "loading":
-        return (
-          <h1 className="h4">
-            <Spinner large />
-          </h1>
-        );
+        return <CommentsLoadingSkeleton />;
       case "success": {
         const mentions = this.state.mentionsRes.data.mentions;
         return (
@@ -695,11 +684,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
   messages() {
     switch (this.state.messagesRes.state) {
       case "loading":
-        return (
-          <h1 className="h4">
-            <Spinner large />
-          </h1>
-        );
+        return <CommentsLoadingSkeleton />;
       case "success": {
         const messages = this.state.messagesRes.data.private_messages;
         return (