diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2951a46c836097bdf43b56be7743dc35cdb3d929..540726ae7d74ee4a528defba5d0e103d929c2133 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: ノート検索にローカルのみ検索可能なオプションの追加
 - Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
 - Enhance: Renote自体を通報できるように
+- Enhance: データセーバーモードの強化
 - Enhance: Renoteを管理者権限で削除可能に
 - `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました
 - Playの操作を行うAPI TokenをAPIコンソールから発行できるように
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index edfd6a72e8ea0e6a50672a3d271bafb06e142d48..fbdf8777cc37782902dcd9c3ef9db85609c9eefb 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -4,34 +4,41 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
-	<a
-		:class="$style.imageContainer"
-		:href="image.url"
-		:title="image.name"
+<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click.stop="onclick">
+	<component
+		:is="disableImageLink ? 'div' : 'a'"
+		v-bind="disableImageLink ? {
+			title: image.name,
+			class: $style.imageContainer,
+		} : {
+			title: image.name,
+			class: $style.imageContainer,
+			href: image.url,
+			style: 'cursor: zoom-in;'
+		}"
 	>
 		<ImgWithBlurhash
 			:hash="image.blurhash"
 			:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
 			:forceBlurhash="hide"
-			:cover="hide"
+			:cover="hide || cover"
 			:alt="image.comment || image.name"
 			:title="image.comment || image.name"
 			:width="image.properties.width"
 			:height="image.properties.height"
 			:style="hide ? 'filter: brightness(0.5);' : null"
 		/>
-	</a>
+	</component>
 	<template v-if="hide">
 		<div :class="$style.hiddenText">
 			<div :class="$style.hiddenTextWrapper">
 				<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
 				<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
-				<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
+				<span v-if="controls" style="display: block;">{{ i18n.ts.clickToShow }}</span>
 			</div>
 		</div>
 	</template>
-	<template v-else>
+	<template v-else-if="controls">
 		<div :class="$style.indicators">
 			<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
 			<div v-if="image.comment" :class="$style.indicator">ALT</div>
@@ -54,10 +61,17 @@ import { i18n } from '@/i18n';
 import * as os from '@/os';
 import { iAmModerator } from '@/account';
 
-const props = defineProps<{
+const props = withDefaults(defineProps<{
 	image: Misskey.entities.DriveFile;
 	raw?: boolean;
-}>();
+	cover?: boolean;
+	disableImageLink?: boolean;
+	controls?: boolean;
+}>(), {
+	cover: false,
+	disableImageLink: false,
+	controls: true,
+});
 
 let hide = $ref(true);
 let darkMode: boolean = $ref(defaultStore.state.darkMode);
@@ -70,6 +84,9 @@ const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
 );
 
 function onclick() {
+	if (!props.controls) {
+		return;
+	}
 	if (hide) {
 		hide = false;
 	}
@@ -167,7 +184,6 @@ function showMenu(ev: MouseEvent) {
 
 .imageContainer {
 	display: block;
-	cursor: zoom-in;
 	overflow: hidden;
 	width: 100%;
 	height: 100%;
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index 53920da50dd0cb7ff3a2b98739c84d8ea6f78ee0..65464956be667b5d0dc0548c83fe21d15aa8ac08 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -5,7 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
-	<div v-if="page.eyeCatchingImage" class="thumbnail" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
+	<div v-if="page.eyeCatchingImage" class="thumbnail">
+		<MediaImage
+			:image="page.eyeCatchingImage"
+			:disableImageLink="true"
+			:controls="false"
+			:cover="true"
+			:class="$style.eyeCatchingImageRoot"
+		/>
+	</div>
 	<article>
 		<header>
 			<h1 :title="page.title">{{ page.title }}</h1>
@@ -23,12 +31,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import { userName } from '@/filters/user';
+import MediaImage from '@/components/MkMediaImage.vue';
 
 const props = defineProps<{
 	page: Misskey.entities.Page;
 }>();
 </script>
 
+<style module>
+.eyeCatchingImageRoot {
+	width: 100%;
+	height: 200px;
+	border-radius: var(--radius) var(--radius) 0 0;
+	overflow: hidden;
+}
+</style>
+
 <style lang="scss" scoped>
 .vhpxefrj {
 	display: block;
@@ -39,32 +57,15 @@ const props = defineProps<{
 	}
 
 	> .thumbnail {
-		width: 100%;
-		height: 200px;
-		background-position: center;
-		background-size: cover;
-		display: flex;
-		justify-content: center;
-		align-items: center;
-
-		> button {
-			font-size: 3.5em;
-			opacity: 0.7;
-
-			&:hover {
-				font-size: 4em;
-				opacity: 0.9;
-			}
-		}
-
 		& + article {
-			left: 100px;
-			width: calc(100% - 100px);
+			border-radius: 0 0 var(--radius) var(--radius);
 		}
 	}
 
 	> article {
+		background-color: var(--panel);
 		padding: 16px;
+		border-radius: var(--radius);
 
 		> header {
 			margin-bottom: 8px;
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index bac7f1e3100b58df54c529fdf42b79cc9f403205..77c62ae0b9df29a99803dc8117683fc4956f3f9e 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 <div v-else>
 	<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
-		<div v-if="thumbnail" :class="$style.thumbnail" :style="`background-image: url('${thumbnail}')`">
+		<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.enableDataSaverMode ? '' : `background-image: url('${thumbnail}')`">
 		</div>
 		<article :class="$style.body">
 			<header :class="$style.header">
@@ -260,6 +260,7 @@ onUnmounted(() => {
 	height: 100%;
 	background-position: center;
 	background-size: cover;
+	background-color: var(--bg);
 	display: flex;
 	justify-content: center;
 	align-items: center;
diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue
index 3d6417145156c4264bc8978877706bc7109610b3..80a75f02a58928681d3e35162918aa0ab65be70e 100644
--- a/packages/frontend/src/components/page/page.image.vue
+++ b/packages/frontend/src/components/page/page.image.vue
@@ -5,20 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div>
-	<ImgWithBlurhash v-if="image" style="max-width: 100%;" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :width="image.properties.width" :height="image.properties.height" :cover="false"/>
+	<MediaImage
+		v-if="image"
+		:image="image"
+		:disableImageLink="true"
+	/>
 </div>
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { ImageBlock } from './block.type';
-import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
+import MediaImage from '@/components/MkMediaImage.vue';
 
 const props = defineProps<{
 	block: ImageBlock,
 	page: Misskey.entities.Page,
 }>();
 
-const image = props.page.attachedFiles.find(x => x.id === props.block.fileId);
+const image = ref<Misskey.entities.DriveFile>(props.page.attachedFiles.find(x => x.id === props.block.fileId));
 </script>
diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue
index 265ee7146d8d3571eaf47e49b6c2459b19fd40ce..ab37ca69ad30ab3b41f695d6f0defefbe15b4306 100644
--- a/packages/frontend/src/components/page/page.vue
+++ b/packages/frontend/src/components/page/page.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }">
+<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps_s">
 	<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
 </div>
 </template>
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index efd63bafcec287ef6d54fe8b9fedf27492fd2ec6..c20bbb4793cc9f645af4605e1c0c744924ef9187 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -16,7 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				-->
 					<div class="banner">
-						<img v-if="page.eyeCatchingImageId" :src="page.eyeCatchingImage.url"/>
+						<MkMediaImage
+							v-if="page.eyeCatchingImageId"
+							:image="page.eyeCatchingImage"
+							:cover="true"
+							:disableImageLink="true"
+							class="thumbnail"
+						/>
 					</div>
 					<div class="content">
 						<XPage :page="page"/>
@@ -74,6 +80,7 @@ import XPage from '@/components/page/page.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os';
 import { url } from '@/config';
+import MkMediaImage from '@/components/MkMediaImage.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkContainer from '@/components/MkContainer.vue';
 import MkPagination from '@/components/MkPagination.vue';
@@ -204,11 +211,14 @@ definePageMetadata(computed(() => page ? {
 		}
 
 		> .banner {
-			> img {
+			> .thumbnail {
 				// TODO: 良い感じのアスペクト比で表示
 				display: block;
 				width: 100%;
-				height: 150px;
+				height: auto;
+				aspect-ratio: 3/1;
+				border-radius: var(--radius);
+				overflow: hidden;
 				object-fit: cover;
 			}
 		}