diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1735376b2..91a6add5d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -730,10 +730,6 @@ desktop/views/components/drive.vue:
     upload: "ファイルをアップロード"
     url-upload: "URLからアップロード"
 
-desktop/views/components/media-image.vue:
-  sensitive: "閲覧注意"
-  click-to-show: "クリックして表示"
-
 desktop/views/components/media-video.vue:
   sensitive: "閲覧注意"
   click-to-show: "クリックして表示"
@@ -980,6 +976,10 @@ desktop/views/components/settings.2fa.vue:
   failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
   info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
 
+common/views/components/media-image.vue:
+  sensitive: "閲覧注意"
+  click-to-show: "クリックして表示"
+
 common/views/components/api-settings.vue:
   intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
   caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
@@ -1493,10 +1493,6 @@ mobile/views/components/drive.file-detail.vue:
   mark-as-sensitive: "閲覧注意に設定"
   unmark-as-sensitive: "閲覧注意を解除"
 
-mobile/views/components/media-image.vue:
-  sensitive: "閲覧注意"
-  click-to-show: "クリックして表示"
-
 mobile/views/components/media-video.vue:
   sensitive: "閲覧注意"
   click-to-show: "クリックして表示"
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/common/views/components/media-image.vue
similarity index 84%
rename from src/client/app/mobile/views/components/media-image.vue
rename to src/client/app/common/views/components/media-image.vue
index dbb275b51..01187465f 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/common/views/components/media-image.vue
@@ -5,16 +5,21 @@
 		<span>{{ $t('click-to-show') }}</span>
 	</div>
 </div>
-<a class="gqnyydlzavusgskkfvwvjiattxdzsqlf" v-else :href="image.url" target="_blank" :style="style" :title="image.name" @click.prevent="onClick"></a>
+<a class="gqnyydlzavusgskkfvwvjiattxdzsqlf" v-else
+	:href="image.url"
+	:style="style"
+	:title="image.name"
+	@click.prevent="onClick"
+></a>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
 import i18n from '../../../i18n';
-import ImageViewer from '../../../common/views/components/image-viewer.vue';
+import ImageViewer from './image-viewer.vue';
 
 export default Vue.extend({
-	i18n: i18n('mobile/views/components/media-image.vue'),
+	i18n: i18n('common/views/components/media-image.vue'),
 	props: {
 		image: {
 			type: Object,
@@ -58,6 +63,7 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 .gqnyydlzavusgskkfvwvjiattxdzsqlf
 	display block
+	cursor zoom-in
 	overflow hidden
 	width 100%
 	height 100%
diff --git a/src/client/app/common/views/components/media-list.vue b/src/client/app/common/views/components/media-list.vue
index d83d6f85c..b0a14be0d 100644
--- a/src/client/app/common/views/components/media-list.vue
+++ b/src/client/app/common/views/components/media-list.vue
@@ -7,7 +7,7 @@
 		<div :data-count="mediaList.filter(media => previewable(media)).length" ref="grid">
 			<template v-for="media in mediaList">
 				<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')"/>
-				<mk-media-image :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
+				<x-image :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
 			</template>
 		</div>
 	</div>
@@ -17,10 +17,12 @@
 <script lang="ts">
 import Vue from 'vue';
 import XBanner from './media-banner.vue';
+import XImage from './media-image.vue';
 
 export default Vue.extend({
 	components: {
-		XBanner
+		XBanner,
+		XImage
 	},
 	props: {
 		mediaList: {
diff --git a/src/client/app/desktop/views/components/index.ts b/src/client/app/desktop/views/components/index.ts
index 8ea2b602d..2edc8117a 100644
--- a/src/client/app/desktop/views/components/index.ts
+++ b/src/client/app/desktop/views/components/index.ts
@@ -9,7 +9,6 @@ import subNoteContent from './sub-note-content.vue';
 import window from './window.vue';
 import noteFormWindow from './post-form-window.vue';
 import renoteFormWindow from './renote-form-window.vue';
-import mediaImage from './media-image.vue';
 import mediaVideo from './media-video.vue';
 import notifications from './notifications.vue';
 import noteForm from './post-form.vue';
@@ -32,7 +31,6 @@ Vue.component('mk-sub-note-content', subNoteContent);
 Vue.component('mk-window', window);
 Vue.component('mk-post-form-window', noteFormWindow);
 Vue.component('mk-renote-form-window', renoteFormWindow);
-Vue.component('mk-media-image', mediaImage);
 Vue.component('mk-media-video', mediaVideo);
 Vue.component('mk-notifications', notifications);
 Vue.component('mk-post-form', noteForm);
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
deleted file mode 100644
index 446e50093..000000000
--- a/src/client/app/desktop/views/components/media-image.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
-<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
-	<div>
-		<b><fa icon="exclamation-triangle"/> {{ $t('sensitive') }}</b>
-		<span>{{ $t('click-to-show') }}</span>
-	</div>
-</div>
-<a class="lcjomzwbohoelkxsnuqjiaccdbdfiazy" v-else
-	:href="image.url"
-	@click.prevent="onClick"
-	:style="style"
-	:title="image.name"
-></a>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import i18n from '../../../i18n';
-import ImageViewer from '../../../common/views/components/image-viewer.vue';
-
-export default Vue.extend({
-	i18n: i18n('desktop/views/components/media-image.vue'),
-	props: {
-		image: {
-			type: Object,
-			required: true
-		},
-		raw: {
-			default: false
-		}
-	},
-	data() {
-		return {
-			hide: true
-		};
-	},
-	computed: {
-		style(): any {
-			return {
-				'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
-				'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.thumbnailUrl})`
-			};
-		}
-	},
-	methods: {
-		onClick() {
-			this.$root.new(ImageViewer, {
-				image: this.image
-			});
-		}
-	}
-});
-</script>
-
-<style lang="stylus" scoped>
-.lcjomzwbohoelkxsnuqjiaccdbdfiazy
-	display block
-	cursor zoom-in
-	overflow hidden
-	width 100%
-	height 100%
-	background-position center
-	background-size contain
-	background-repeat no-repeat
-
-.ldwbgwstjsdgcjruamauqdrffetqudry
-	display flex
-	justify-content center
-	align-items center
-	background #111
-	color #fff
-
-	> div
-		display table-cell
-		text-align center
-		font-size 12px
-
-		> *
-			display block
-
-</style>
diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts
index 9a410e827..351aaea9f 100644
--- a/src/client/app/mobile/views/components/index.ts
+++ b/src/client/app/mobile/views/components/index.ts
@@ -3,7 +3,6 @@ import Vue from 'vue';
 import ui from './ui.vue';
 import note from './note.vue';
 import notes from './notes.vue';
-import mediaImage from './media-image.vue';
 import mediaVideo from './media-video.vue';
 import notePreview from './note-preview.vue';
 import subNoteContent from './sub-note-content.vue';
@@ -24,7 +23,6 @@ import postForm from './post-form.vue';
 Vue.component('mk-ui', ui);
 Vue.component('mk-note', note);
 Vue.component('mk-notes', notes);
-Vue.component('mk-media-image', mediaImage);
 Vue.component('mk-media-video', mediaVideo);
 Vue.component('mk-note-preview', notePreview);
 Vue.component('mk-sub-note-content', subNoteContent);