diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index bae7a1173d..01b1dfa2c4 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1266,14 +1266,19 @@ admin/views/users.vue:
   user-not-found: "ユーザーが見つかりません"
   lookup: "照会"
   reset-password: "パスワードをリセット"
+  reset-password-confirm: "パスワードをリセットしますか?"
   password-updated: "パスワードは現在「{password}」です"
   suspend: "凍結"
+  suspend-confirm: "凍結しますか?"
   suspended: "凍結しました"
   unsuspend: "凍結の解除"
+  unsuspend-confirm: "凍結を解除しますか?"
   unsuspended: "凍結を解除しました"
   verify: "公式アカウントにする"
+  verify-confirm: "公式アカウントにしますか?"
   verified: "公式アカウントにしました"
   unverify: "公式アカウントを解除する"
+  unverify-confirm: "公式アカウントを解除しますか?"
   unverified: "公式アカウントを解除しました"
   users:
     title: "ユーザー"
diff --git a/src/client/app/admin/views/users.user.vue b/src/client/app/admin/views/users.user.vue
new file mode 100644
index 0000000000..afece18e82
--- /dev/null
+++ b/src/client/app/admin/views/users.user.vue
@@ -0,0 +1,82 @@
+<template>
+<div class="kofvwchc">
+	<div>
+		<a :href="user | userPage(null, true)">
+			<mk-avatar class="avatar" :user="user" :disable-link="true"/>
+		</a>
+	</div>
+	<div>
+		<header>
+			<b><mk-user-name :user="user"/></b>
+			<span class="username">@{{ user | acct }}</span>
+			<span class="is-admin" v-if="user.isAdmin">admin</span>
+			<span class="is-moderator" v-if="user.isModerator">moderator</span>
+			<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
+			<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
+		</header>
+		<div>
+			<span>{{ $t('users.updatedAt') }}: <mk-time :time="user.updatedAt" mode="detail"/></span>
+		</div>
+		<div>
+			<span>{{ $t('users.createdAt') }}: <mk-time :time="user.createdAt" mode="detail"/></span>
+		</div>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../i18n';
+import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
+
+export default Vue.extend({
+	i18n: i18n('admin/views/users.vue'),
+	props: ['user'],
+	data() {
+		return {
+			faSnowflake
+		};
+	},
+});
+</script>
+
+<style lang="stylus" scoped>
+.kofvwchc
+	display flex
+	padding 16px 0
+	border-top solid 1px var(--faceDivider)
+
+	> div:first-child
+		> a
+			> .avatar
+				width 64px
+				height 64px
+
+	> div:last-child
+		flex 1
+		padding-left 16px
+
+		@media (max-width 500px)
+			font-size 14px
+
+		> header
+			> .username
+				margin-left 8px
+				opacity 0.7
+
+			> .is-admin
+			> .is-moderator
+				flex-shrink 0
+				align-self center
+				margin 0 0 0 .5em
+				padding 1px 6px
+				font-size 80%
+				border-radius 3px
+				background var(--noteHeaderAdminBg)
+				color var(--noteHeaderAdminFg)
+
+			> .is-verified
+			> .is-suspended
+				margin 0 0 0 .5em
+				color #4dabf7
+</style>
diff --git a/src/client/app/admin/views/users.vue b/src/client/app/admin/views/users.vue
index 6f0f1629f1..6b829a2f8d 100644
--- a/src/client/app/admin/views/users.vue
+++ b/src/client/app/admin/views/users.vue
@@ -3,20 +3,26 @@
 	<ui-card>
 		<div slot="title"><fa :icon="faTerminal"/> {{ $t('operation') }}</div>
 		<section class="fit-top">
-			<ui-input v-model="target" type="text">
+			<ui-input class="target" v-model="target" type="text">
 				<span>{{ $t('username-or-userid') }}</span>
 			</ui-input>
-			<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
-			<ui-horizon-group>
-				<ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button>
-				<ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
-			</ui-horizon-group>
-			<ui-horizon-group>
-				<ui-button @click="suspendUser" :disabled="suspending"><fa :icon="faSnowflake"/> {{ $t('suspend') }}</ui-button>
-				<ui-button @click="unsuspendUser" :disabled="unsuspending">{{ $t('unsuspend') }}</ui-button>
-			</ui-horizon-group>
 			<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
-			<ui-textarea v-if="user" :value="user | json5" readonly tall style="margin-top:16px;"></ui-textarea>
+
+			<div class="user" v-if="user">
+				<x-user :user='user'/>
+				<div class="actions">
+					<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
+					<ui-horizon-group>
+						<ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button>
+						<ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
+					</ui-horizon-group>
+					<ui-horizon-group>
+						<ui-button @click="suspendUser" :disabled="suspending"><fa :icon="faSnowflake"/> {{ $t('suspend') }}</ui-button>
+						<ui-button @click="unsuspendUser" :disabled="unsuspending">{{ $t('unsuspend') }}</ui-button>
+					</ui-horizon-group>
+					<ui-textarea v-if="user" :value="user | json5" readonly tall style="margin-top:16px;"></ui-textarea>
+				</div>
+			</div>
 		</section>
 	</ui-card>
 
@@ -47,29 +53,7 @@
 				</ui-select>
 			</ui-horizon-group>
 			<sequential-entrance animation="entranceFromTop" delay="25">
-				<div class="kofvwchc" v-for="user in users" :key="user.id">
-					<div>
-						<a :href="user | userPage(null, true)">
-							<mk-avatar class="avatar" :user="user" :disable-link="true"/>
-						</a>
-					</div>
-					<div>
-						<header>
-							<b><mk-user-name :user="user"/></b>
-							<span class="username">@{{ user | acct }}</span>
-							<span class="is-admin" v-if="user.isAdmin">admin</span>
-							<span class="is-moderator" v-if="user.isModerator">moderator</span>
-							<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
-							<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
-						</header>
-						<div>
-							<span>{{ $t('users.updatedAt') }}: <mk-time :time="user.updatedAt" mode="detail"/></span>
-						</div>
-						<div>
-							<span>{{ $t('users.createdAt') }}: <mk-time :time="user.createdAt" mode="detail"/></span>
-						</div>
-					</div>
-				</div>
+				<x-user v-for="user in users" :user='user' :key="user.id"/>
 			</sequential-entrance>
 			<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button>
 		</section>
@@ -83,10 +67,13 @@ import i18n from '../../i18n';
 import parseAcct from "../../../../misc/acct/parse";
 import { faCertificate, faUsers, faTerminal, faSearch, faKey } from '@fortawesome/free-solid-svg-icons';
 import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
+import XUser from './users.user.vue';
 
 export default Vue.extend({
 	i18n: i18n('admin/views/users.vue'),
-
+	components: {
+		XUser
+	},
 	data() {
 		return {
 			user: null,
@@ -131,6 +118,7 @@ export default Vue.extend({
 	},
 
 	methods: {
+		/** テキストエリアのユーザーを解決する */
 		async fetchUser() {
 			try {
 				return await this.$root.api('users/show', this.target.startsWith('@') ? parseAcct(this.target) : { userId: this.target });
@@ -149,16 +137,27 @@ export default Vue.extend({
 			}
 		},
 
+		/** テキストエリアから処理対象ユーザーを設定する */
 		async showUser() {
+			this.user = null;
 			const user = await this.fetchUser();
 			this.$root.api('admin/show-user', { userId: user.id }).then(info => {
 				this.user = info;
 			});
+			this.target = '';
+		},
+
+		/** 処理対象ユーザーの情報を更新する */
+		async refreshUser() {
+			this.$root.api('admin/show-user', { userId: this.user._id }).then(info => {
+				this.user = info;
+			});
 		},
 
 		async resetPassword() {
-			const user = await this.fetchUser();
-			this.$root.api('admin/reset-password', { userId: user.id }).then(res => {
+			if (!await this.getConfirmed(this.$t('reset-password-confirm'))) return;
+
+			this.$root.api('admin/reset-password', { userId: this.user._id }).then(res => {
 				this.$root.dialog({
 					type: 'success',
 					text: this.$t('password-updated', { password: res.password })
@@ -167,11 +166,12 @@ export default Vue.extend({
 		},
 
 		async verifyUser() {
+			if (!await this.getConfirmed(this.$t('verify-confirm'))) return;
+
 			this.verifying = true;
 
 			const process = async () => {
-				const user = await this.fetchUser();
-				await this.$root.api('admin/verify-user', { userId: user.id });
+				await this.$root.api('admin/verify-user', { userId: this.user._id });
 				this.$root.dialog({
 					type: 'success',
 					text: this.$t('verified')
@@ -186,14 +186,17 @@ export default Vue.extend({
 			});
 
 			this.verifying = false;
+
+			this.refreshUser();
 		},
 
 		async unverifyUser() {
+			if (!await this.getConfirmed(this.$t('unverify-confirm'))) return;
+
 			this.unverifying = true;
 
 			const process = async () => {
-				const user = await this.fetchUser();
-				await this.$root.api('admin/unverify-user', { userId: user.id });
+				await this.$root.api('admin/unverify-user', { userId: this.user._id });
 				this.$root.dialog({
 					type: 'success',
 					text: this.$t('unverified')
@@ -208,14 +211,17 @@ export default Vue.extend({
 			});
 
 			this.unverifying = false;
+
+			this.refreshUser();
 		},
 
 		async suspendUser() {
+			if (!await this.getConfirmed(this.$t('suspend-confirm'))) return;
+
 			this.suspending = true;
 
 			const process = async () => {
-				const user = await this.fetchUser();
-				await this.$root.api('admin/suspend-user', { userId: user.id });
+				await this.$root.api('admin/suspend-user', { userId: this.user._id });
 				this.$root.dialog({
 					type: 'success',
 					text: this.$t('suspended')
@@ -230,14 +236,17 @@ export default Vue.extend({
 			});
 
 			this.suspending = false;
+
+			this.refreshUser();
 		},
 
 		async unsuspendUser() {
+			if (!await this.getConfirmed(this.$t('unsuspend-confirm'))) return;
+
 			this.unsuspending = true;
 
 			const process = async () => {
-				const user = await this.fetchUser();
-				await this.$root.api('admin/unsuspend-user', { userId: user.id });
+				await this.$root.api('admin/unsuspend-user', { userId: this.user._id });
 				this.$root.dialog({
 					type: 'success',
 					text: this.$t('unsuspended')
@@ -252,8 +261,21 @@ export default Vue.extend({
 			});
 
 			this.unsuspending = false;
+
+			this.refreshUser();
 		},
 
+		async getConfirmed(text: string): Promise<Boolean> {
+			const confirm = await this.$root.dialog({
+				type: 'warning',
+				showCancelButton: true,
+				title: 'confirm',
+				text,
+			});
+
+			return !confirm.canceled;
+		}
+
 		fetchUsers() {
 			this.$root.api('admin/show-users', {
 				state: this.state,
@@ -277,42 +299,12 @@ export default Vue.extend({
 </script>
 
 <style lang="stylus" scoped>
-.kofvwchc
-	display flex
-	padding 16px 0
-	border-top solid 1px var(--faceDivider)
+.target
+	margin-bottom 16px !important
 
-	> div:first-child
-		> a
-			> .avatar
-				width 64px
-				height 64px
+.user
+	margin-top 32px
 
-	> div:last-child
-		flex 1
-		padding-left 16px
-
-		@media (max-width 500px)
-			font-size 14px
-
-		> header
-			> .username
-				margin-left 8px
-				opacity 0.7
-
-			> .is-admin
-			> .is-moderator
-				flex-shrink 0
-				align-self center
-				margin 0 0 0 .5em
-				padding 1px 6px
-				font-size 80%
-				border-radius 3px
-				background var(--noteHeaderAdminBg)
-				color var(--noteHeaderAdminFg)
-
-			> .is-verified
-			> .is-suspended
-				margin 0 0 0 .5em
-				color #4dabf7
+	> .actions
+		margin-left 80px
 </style>