diff --git a/packages/client/src/components/MkChannelFollowButton.vue b/packages/client/src/components/MkChannelFollowButton.vue
index 46d57b4fc6..9e275d6172 100644
--- a/packages/client/src/components/MkChannelFollowButton.vue
+++ b/packages/client/src/components/MkChannelFollowButton.vue
@@ -1,5 +1,6 @@
 <template>
-<button class="hdcaacmi _button"
+<button
+	class="hdcaacmi _button"
 	:class="{ wait, active: isFollowing, full }"
 	:disabled="wait"
 	@click="onClick"
@@ -13,7 +14,7 @@
 		</template>
 	</template>
 	<template v-else>
-		<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse ti-fw"></i>
+		<span v-if="full">{{ i18n.ts.processing }}</span><MkLoading :em="true"/>
 	</template>
 </button>
 </template>
@@ -39,12 +40,12 @@ async function onClick() {
 	try {
 		if (isFollowing.value) {
 			await os.api('channels/unfollow', {
-				channelId: props.channel.id
+				channelId: props.channel.id,
 			});
 			isFollowing.value = false;
 		} else {
 			await os.api('channels/follow', {
-				channelId: props.channel.id
+				channelId: props.channel.id,
 			});
 			isFollowing.value = true;
 		}
diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index 8a46e9cdde..e40b5d6553 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -10,7 +10,7 @@
 			<i v-else-if="type === 'warning'" class="ti ti-alert-triangle"></i>
 			<i v-else-if="type === 'info'" class="ti ti-info-circle"></i>
 			<i v-else-if="type === 'question'" class="ti ti-question-circle"></i>
-			<i v-else-if="type === 'waiting'" class="fas fa-spinner fa-pulse"></i>
+			<MkLoading v-else-if="type === 'waiting'" :em="true"/>
 		</div>
 		<header v-if="title"><Mfm :text="title"/></header>
 		<div v-if="text" class="body"><Mfm :text="text"/></div>
diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index 1099893b0d..433386f443 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -1,5 +1,6 @@
 <template>
-<button class="kpoogebi _button"
+<button
+	class="kpoogebi _button"
 	:class="{ wait, active: isFollowing || hasPendingFollowRequestFromYou, full, large }"
 	:disabled="wait"
 	@click="onClick"
@@ -8,8 +9,9 @@
 		<template v-if="hasPendingFollowRequestFromYou && user.isLocked">
 			<span v-if="full">{{ i18n.ts.followRequestPending }}</span><i class="fas fa-hourglass-half"></i>
 		</template>
-		<template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 -->
-			<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse"></i>
+		<template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked">
+			<!-- つまりリモートフォローの場合。 -->
+			<span v-if="full">{{ i18n.ts.processing }}</span><MkLoading :em="true"/>
 		</template>
 		<template v-else-if="isFollowing">
 			<span v-if="full">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i>
@@ -22,7 +24,7 @@
 		</template>
 	</template>
 	<template v-else>
-		<span v-if="full">{{ i18n.ts.processing }}</span><i class="fas fa-spinner fa-pulse ti-fw"></i>
+		<span v-if="full">{{ i18n.ts.processing }}</span><MkLoading :em="true"/>
 	</template>
 </button>
 </template>
@@ -50,9 +52,9 @@ const connection = stream.useChannel('main');
 
 if (props.user.isFollowing == null) {
 	os.api('users/show', {
-		userId: props.user.id
+		userId: props.user.id,
 	})
-	.then(onFollowChange);
+		.then(onFollowChange);
 }
 
 function onFollowChange(user: Misskey.entities.UserDetailed) {
@@ -75,17 +77,17 @@ async function onClick() {
 			if (canceled) return;
 
 			await os.api('following/delete', {
-				userId: props.user.id
+				userId: props.user.id,
 			});
 		} else {
 			if (hasPendingFollowRequestFromYou) {
 				await os.api('following/requests/cancel', {
-					userId: props.user.id
+					userId: props.user.id,
 				});
 				hasPendingFollowRequestFromYou = false;
 			} else {
 				await os.api('following/create', {
-					userId: props.user.id
+					userId: props.user.id,
 				});
 				hasPendingFollowRequestFromYou = true;
 			}
diff --git a/packages/client/src/components/MkSignup.vue b/packages/client/src/components/MkSignup.vue
index e6281b1f84..67d3bb5c70 100644
--- a/packages/client/src/components/MkSignup.vue
+++ b/packages/client/src/components/MkSignup.vue
@@ -9,7 +9,7 @@
 		<template #prefix>@</template>
 		<template #suffix>@{{ host }}</template>
 		<template #caption>
-			<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse ti-fw"></i> {{ i18n.ts.checking }}</span>
+			<span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
 			<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
 			<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
 			<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span>
@@ -22,7 +22,7 @@
 		<template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
 		<template #prefix><i class="ti ti-mail"></i></template>
 		<template #caption>
-			<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse ti-fw"></i> {{ i18n.ts.checking }}</span>
+			<span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span>
 			<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span>
 			<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
 			<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
diff --git a/packages/client/src/components/MkWaitingDialog.vue b/packages/client/src/components/MkWaitingDialog.vue
index ee86aec967..f4a9f4f22c 100644
--- a/packages/client/src/components/MkWaitingDialog.vue
+++ b/packages/client/src/components/MkWaitingDialog.vue
@@ -2,7 +2,7 @@
 <MkModal ref="modal" :prefer-type="'dialog'" :z-priority="'high'" @click="success ? done() : () => {}" @closed="emit('closed')">
 	<div class="iuyakobc" :class="{ iconOnly: (text == null) || success }">
 		<i v-if="success" class="ti ti-check icon success"></i>
-		<i v-else class="fas fa-spinner fa-pulse icon waiting"></i>
+		<MkLoading v-else class="icon waiting" :em="true"/>
 		<div v-if="text && !success" class="text">{{ text }}<MkEllipsis/></div>
 	</div>
 </MkModal>
diff --git a/packages/client/src/components/global/MkLoading.vue b/packages/client/src/components/global/MkLoading.vue
index bcf5925234..c7d787820a 100644
--- a/packages/client/src/components/global/MkLoading.vue
+++ b/packages/client/src/components/global/MkLoading.vue
@@ -1,5 +1,5 @@
 <template>
-<div :class="[$style.root, { [$style.inline]: inline, [$style.colored]: colored, [$style.mini]: mini }]">
+<div :class="[$style.root, { [$style.inline]: inline, [$style.colored]: colored, [$style.mini]: mini, [$style.em]: em }]">
 	<div :class="$style.container">
 		<svg :class="[$style.spinner, $style.bg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
 			<g transform="matrix(1.125,0,0,1.125,12,12)">
@@ -22,10 +22,12 @@ const props = withDefaults(defineProps<{
 	inline?: boolean;
 	colored?: boolean;
 	mini?: boolean;
+	em?: boolean;
 }>(), {
 	inline: false,
 	colored: true,
 	mini: false,
+	em: false,
 });
 </script>
 
@@ -60,6 +62,12 @@ const props = withDefaults(defineProps<{
 		padding: 16px;
 		--size: 32px;
 	}
+
+	&.em {
+		display: inline;
+		padding: 0;
+		--size: 1em;
+	}
 }
 
 .container {
diff --git a/packages/client/src/pages/messaging/messaging-room.form.vue b/packages/client/src/pages/messaging/messaging-room.form.vue
index ca0ebf554e..84572815c0 100644
--- a/packages/client/src/pages/messaging/messaging-room.form.vue
+++ b/packages/client/src/pages/messaging/messaging-room.form.vue
@@ -18,7 +18,7 @@
 			<button class="_button" @click="chooseFile"><i class="ti ti-photo-plus"></i></button>
 			<button class="_button" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button>
 			<button class="send _button" :disabled="!canSend || sending" :title="i18n.ts.send" @click="send">
-				<template v-if="!sending"><i class="ti ti-send"></i></template><template v-if="sending"><i class="fas fa-spinner fa-pulse ti-fw"></i></template>
+				<template v-if="!sending"><i class="ti ti-send"></i></template><template v-if="sending"><MkLoading :em="true"/></template>
 			</button>
 		</div>
 	</footer>
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 422b55e227..f0cb6bf79a 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -26,7 +26,7 @@
 			<ol v-if="registration && !registration.error">
 				<li v-if="registration.stage >= 0">
 					{{ i18n.ts.tapSecurityKey }}
-					<i v-if="registration.saving && registration.stage == 0" class="fas fa-spinner fa-pulse ti-fw"></i>
+					<MkLoading v-if="registration.saving && registration.stage == 0" :em="true"/>
 				</li>
 				<li v-if="registration.stage >= 1">
 					<MkForm :disabled="registration.stage != 1 || registration.saving">
@@ -34,7 +34,7 @@
 							<template #label>{{ i18n.ts.securityKeyName }}</template>
 						</MkInput>
 						<MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton>
-						<i v-if="registration.saving && registration.stage == 1" class="fas fa-spinner fa-pulse ti-fw"></i>
+						<MkLoading v-if="registration.saving && registration.stage == 1" :em="true"/>
 					</MkForm>
 				</li>
 			</ol>
@@ -86,11 +86,11 @@ const token = ref(null);
 function register() {
 	os.inputText({
 		title: i18n.ts.password,
-		type: 'password'
+		type: 'password',
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
 		os.api('i/2fa/register', {
-			password: password
+			password: password,
 		}).then(data => {
 			twoFactorData.value = data;
 		});
@@ -100,11 +100,11 @@ function register() {
 function unregister() {
 	os.inputText({
 		title: i18n.ts.password,
-		type: 'password'
+		type: 'password',
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
 		os.api('i/2fa/unregister', {
-			password: password
+			password: password,
 		}).then(() => {
 			usePasswordLessLogin.value = false;
 			updatePasswordLessLogin();
@@ -117,7 +117,7 @@ function unregister() {
 
 function submit() {
 	os.api('i/2fa/done', {
-		token: token.value
+		token: token.value,
 	}).then(() => {
 		os.success();
 		$i!.twoFactorEnabled = true;
@@ -137,7 +137,7 @@ function registerKey() {
 		challengeId: registration.value.challengeId,
 		// we convert each 16 bits to a string to serialise
 		clientDataJSON: stringify(registration.value.credential.response.clientDataJSON),
-		attestationObject: hexify(registration.value.credential.response.attestationObject)
+		attestationObject: hexify(registration.value.credential.response.attestationObject),
 	}).then(key => {
 		registration.value = null;
 		key.lastUsed = new Date();
@@ -148,12 +148,12 @@ function registerKey() {
 function unregisterKey(key) {
 	os.inputText({
 		title: i18n.ts.password,
-		type: 'password'
+		type: 'password',
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
 		return os.api('i/2fa/remove-key', {
 			password,
-			credentialId: key.id
+			credentialId: key.id,
 		}).then(() => {
 			usePasswordLessLogin.value = false;
 			updatePasswordLessLogin();
@@ -166,11 +166,11 @@ function unregisterKey(key) {
 function addSecurityKey() {
 	os.inputText({
 		title: i18n.ts.password,
-		type: 'password'
+		type: 'password',
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
 		os.api('i/2fa/register-key', {
-			password
+			password,
 		}).then(reg => {
 			registration.value = {
 				password,
@@ -180,7 +180,7 @@ function addSecurityKey() {
 					challenge: byteify(reg!.challenge, 'base64'),
 					rp: {
 						id: hostname,
-						name: 'Misskey'
+						name: 'Misskey',
 					},
 					user: {
 						id: byteify($i!.id, 'ascii'),
@@ -189,12 +189,12 @@ function addSecurityKey() {
 					},
 					pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
 					timeout: 60000,
-					attestation: 'direct'
+					attestation: 'direct',
 				},
-				saving: true
+				saving: true,
 			};
 			return navigator.credentials.create({
-				publicKey: registration.value.publicKeyOptions
+				publicKey: registration.value.publicKeyOptions,
 			});
 		}).then(credential => {
 			registration.value.credential = credential;
@@ -210,7 +210,7 @@ function addSecurityKey() {
 
 async function updatePasswordLessLogin() {
 	await os.api('i/2fa/password-less', {
-		value: !!usePasswordLessLogin.value
+		value: !!usePasswordLessLogin.value,
 	});
 }
 </script>
diff --git a/packages/client/src/ui/_common_/upload.vue b/packages/client/src/ui/_common_/upload.vue
index 8324e9e75e..70882bd251 100644
--- a/packages/client/src/ui/_common_/upload.vue
+++ b/packages/client/src/ui/_common_/upload.vue
@@ -4,7 +4,7 @@
 		<li v-for="ctx in uploads" :key="ctx.id">
 			<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
 			<div class="top">
-				<p class="name"><i class="fas fa-spinner fa-pulse"></i>{{ ctx.name }}</p>
+				<p class="name"><MkLoading :em="true"/>{{ ctx.name }}</p>
 				<p class="status">
 					<span v-if="ctx.progressValue === undefined" class="initing">{{ i18n.ts.waiting }}<MkEllipsis/></span>
 					<span v-if="ctx.progressValue !== undefined" class="kb">{{ String(Math.floor(ctx.progressValue / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progressMax / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>