diff --git a/package.json b/package.json
index a461d8076..31e60260b 100644
--- a/package.json
+++ b/package.json
@@ -216,6 +216,7 @@
 		"vue-router": "3.0.1",
 		"vue-style-loader": "4.1.2",
 		"vue-template-compiler": "2.5.17",
+		"vue-thin-modal": "1.1.1",
 		"vuedraggable": "2.16.0",
 		"vuex": "3.0.1",
 		"vuex-persistedstate": "2.5.4",
diff --git a/src/client/app/app.vue b/src/client/app/app.vue
index 7a46e7dea..011eb1466 100644
--- a/src/client/app/app.vue
+++ b/src/client/app/app.vue
@@ -1,3 +1,6 @@
 <template>
-<router-view id="app"></router-view>
+<div>
+	<router-view id="app"></router-view>
+	<modal-portal/>
+</div>
 </template>
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index cf9795740..dd31f0747 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -11,6 +11,8 @@ import VAnimateCss from 'v-animate-css';
 import Element from 'element-ui';
 import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
 import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
+import VueThinModal from 'vue-thin-modal';
+import 'vue-thin-modal/dist/vue-thin-modal.css';
 
 import App from './app.vue';
 import checkForUpdate from './common/scripts/check-for-update';
@@ -30,6 +32,9 @@ Vue.use(VModal);
 Vue.use(TreeView);
 Vue.use(VAnimateCss);
 Vue.use(Element, { locale: elementLocale });
+Vue.use(VueThinModal, {
+	autoMountPortal: false
+});
 
 // Register global directives
 require('./common/views/directives');
diff --git a/src/client/app/mobile/api/post.ts b/src/client/app/mobile/api/post.ts
deleted file mode 100644
index 15b2f6b69..000000000
--- a/src/client/app/mobile/api/post.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import PostForm from '../views/components/post-form.vue';
-
-export default (os) => (opts) => {
-	const o = opts || {};
-
-	const app = document.getElementById('app');
-	app.style.display = 'none';
-
-	function recover() {
-		app.style.display = 'block';
-	}
-
-	const vm = new PostForm({
-		parent: os.app,
-		propsData: {
-			reply: o.reply,
-			renote: o.renote
-		}
-	}).$mount();
-	vm.$once('cancel', recover);
-	vm.$once('posted', recover);
-	document.body.appendChild(vm.$el);
-	(vm as any).focus();
-};
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index 5b9d45462..edc2b3552 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -14,7 +14,6 @@ import chooseDriveFolder from './api/choose-drive-folder';
 import chooseDriveFile from './api/choose-drive-file';
 import dialog from './api/dialog';
 import input from './api/input';
-import post from './api/post';
 import notify from './api/notify';
 
 import MkIndex from './views/pages/index.vue';
@@ -91,7 +90,7 @@ init((launch) => {
 		chooseDriveFile,
 		dialog: dialog(os),
 		input,
-		post: post(os),
+		post: () => alert('deprecated'),
 		notify
 	}));
 }, true);
diff --git a/src/client/app/mobile/style.styl b/src/client/app/mobile/style.styl
index df8f4a8fa..f78146f8b 100644
--- a/src/client/app/mobile/style.styl
+++ b/src/client/app/mobile/style.styl
@@ -17,3 +17,13 @@ body
 	display flex
 	flex-direction column
 	min-height 100%
+
+.modal-backdrop
+	z-index 10000 !important
+
+.modal-content-wrapper
+	z-index 10001 !important
+
+.modal-content
+	padding 0 !important
+	background-color transparent !important
diff --git a/src/client/app/mobile/views/components/drive-file-chooser.vue b/src/client/app/mobile/views/components/drive-file-chooser.vue
index d95d5fa22..56e41e31d 100644
--- a/src/client/app/mobile/views/components/drive-file-chooser.vue
+++ b/src/client/app/mobile/views/components/drive-file-chooser.vue
@@ -48,7 +48,7 @@ export default Vue.extend({
 <style lang="stylus" scoped>
 .mk-drive-file-chooser
 	position fixed
-	z-index 2048
+	z-index 20000
 	top 0
 	left 0
 	width 100%
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index f9996f9da..02ef3845d 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -75,6 +75,13 @@
 	<div class="replies" v-if="!compact">
 		<x-sub v-for="note in replies" :key="note.id" :note="note"/>
 	</div>
+
+	<modal name="replyForm">
+		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
+	</modal>
+	<modal name="renoteForm">
+		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
+	</modal>
 </div>
 </template>
 
@@ -116,9 +123,11 @@ export default Vue.extend({
 				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
+
 		p(): any {
 			return this.isRenote ? this.note.renote : this.note;
 		},
+
 		reactionsCount(): number {
 			return this.p.reactionCounts
 				? Object.keys(this.p.reactionCounts)
@@ -126,6 +135,7 @@ export default Vue.extend({
 					.reduce((a, b) => a + b)
 				: 0;
 		},
+
 		urls(): string[] {
 			if (this.p.text) {
 				const ast = parse(this.p.text);
@@ -180,16 +190,23 @@ export default Vue.extend({
 				this.conversation = conversation.reverse();
 			});
 		},
+
 		reply() {
-			(this as any).apis.post({
-				reply: this.p
-			});
+			this.$modal.push('replyForm');
 		},
+
+		replyFormClosed() {
+			this.$modal.pop();
+		},
+
 		renote() {
-			(this as any).apis.post({
-				renote: this.p
-			});
+			this.$modal.push('renoteForm');
 		},
+
+		renoteFormClosed() {
+			this.$modal.pop();
+		},
+
 		react() {
 			(this as any).os.new(MkReactionPicker, {
 				source: this.$refs.reactButton,
@@ -198,6 +215,7 @@ export default Vue.extend({
 				big: true
 			});
 		},
+
 		menu() {
 			(this as any).os.new(MkNoteMenu, {
 				source: this.$refs.menuButton,
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index d0cea135f..f658686b0 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -60,6 +60,13 @@
 			</footer>
 		</div>
 	</article>
+
+	<modal name="replyForm">
+		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
+	</modal>
+	<modal name="renoteForm">
+		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
+	</modal>
 </div>
 </template>
 
@@ -195,15 +202,19 @@ export default Vue.extend({
 		},
 
 		reply() {
-			(this as any).apis.post({
-				reply: this.p
-			});
+			this.$modal.push('replyForm');
+		},
+
+		replyFormClosed() {
+			this.$modal.pop();
 		},
 
 		renote() {
-			(this as any).apis.post({
-				renote: this.p
-			});
+			this.$modal.push('renoteForm');
+		},
+
+		renoteFormClosed() {
+			this.$modal.pop();
 		},
 
 		react() {
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index a74df67c0..537bf407d 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -293,9 +293,6 @@ export default Vue.extend({
 				viaMobile: viaMobile
 			}).then(data => {
 				this.$emit('posted');
-				this.$nextTick(() => {
-					this.$destroy();
-				});
 			}).catch(err => {
 				this.posting = false;
 			});
@@ -309,7 +306,6 @@ export default Vue.extend({
 
 		cancel() {
 			this.$emit('cancel');
-			this.$destroy();
 		},
 
 		kao() {
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index 706c9cd28..a03fa03c5 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -42,6 +42,10 @@
 			<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
 		</div>
 	</main>
+
+	<modal name="postForm">
+		<mk-post-form @posted="postFormClosed" @cancel="postFormClosed"/>
+	</modal>
 </mk-ui>
 </template>
 
@@ -107,7 +111,11 @@ export default Vue.extend({
 
 	methods: {
 		fn() {
-			(this as any).apis.post();
+			this.$modal.push('postForm');
+		},
+
+		postFormClosed() {
+			this.$modal.pop();
 		},
 
 		saveSrc() {