diff --git a/src/client/app/desktop/views/pages/search.vue b/src/client/app/desktop/views/pages/search.vue
index 06ea4afbca..6ebb83cac8 100644
--- a/src/client/app/desktop/views/pages/search.vue
+++ b/src/client/app/desktop/views/pages/search.vue
@@ -7,12 +7,7 @@
 		<mk-ellipsis-icon/>
 	</div>
 	<p :class="$style.empty" v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
-	<mk-notes ref="timeline" :class="$style.notes" :notes="notes">
-		<div slot="footer">
-			<template v-if="!moreFetching">%fa:search%</template>
-			<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
-		</div>
-	</mk-notes>
+	<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
 </mk-ui>
 </template>
 
@@ -29,16 +24,13 @@ export default Vue.extend({
 			moreFetching: false,
 			existMore: false,
 			offset: 0,
-			notes: []
+			empty: false
 		};
 	},
 	watch: {
 		$route: 'fetch'
 	},
 	computed: {
-		empty(): boolean {
-			return this.notes.length == 0;
-		},
 		q(): string {
 			return this.$route.query.q;
 		}
@@ -65,41 +57,43 @@ export default Vue.extend({
 			this.fetching = true;
 			Progress.start();
 
-			(this as any).api('notes/search', {
-				limit: limit + 1,
-				offset: this.offset,
-				query: this.q
-			}).then(notes => {
-				if (notes.length == limit + 1) {
-					notes.pop();
-					this.existMore = true;
-				}
-				this.notes = notes;
-				this.fetching = false;
-				Progress.done();
-			});
+			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
+				(this as any).api('notes/search', {
+					limit: limit + 1,
+					offset: this.offset,
+					query: this.q
+				}).then(notes => {
+					if (notes.length == 0) this.empty = true;
+					if (notes.length == limit + 1) {
+						notes.pop();
+						this.existMore = true;
+					}
+					res(notes);
+					this.fetching = false;
+					Progress.done();
+				}, rej);
+			}));
 		},
 		more() {
-			if (this.moreFetching || this.fetching || this.notes.length == 0 || !this.existMore) return;
 			this.offset += limit;
-			this.moreFetching = true;
-			return (this as any).api('notes/search', {
+
+			const promise = (this as any).api('notes/search', {
 				limit: limit + 1,
 				offset: this.offset,
 				query: this.q
-			}).then(notes => {
+			});
+
+			promise.then(notes => {
 				if (notes.length == limit + 1) {
 					notes.pop();
 				} else {
 					this.existMore = false;
 				}
-				this.notes = this.notes.concat(notes);
+				notes.forEach(n => (this.$refs.timeline as any).append(n));
 				this.moreFetching = false;
 			});
-		},
-		onScroll() {
-			const current = window.scrollY + window.innerHeight;
-			if (current > document.body.offsetHeight - 16) this.more();
+
+			return promise;
 		}
 	}
 });
diff --git a/src/client/app/mobile/views/pages/search.vue b/src/client/app/mobile/views/pages/search.vue
index 6b7c141bff..2559922efb 100644
--- a/src/client/app/mobile/views/pages/search.vue
+++ b/src/client/app/mobile/views/pages/search.vue
@@ -1,14 +1,10 @@
 <template>
 <mk-ui>
 	<span slot="header">%fa:search% {{ q }}</span>
-	<main v-if="!fetching">
-		<mk-notes :class="$style.notes" :notes="notes">
-			<span v-if="notes.length == 0">{{ '%i18n:@empty%'.replace('{}', q) }}</span>
-			<button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
-				<span v-if="!fetching">%i18n:@load-more%</span>
-				<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
-			</button>
-		</mk-notes>
+
+	<main>
+		<p v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
+		<mk-notes ref="timeline" :more="existMore ? more : null"/>
 	</main>
 </mk-ui>
 </template>
@@ -23,8 +19,9 @@ export default Vue.extend({
 	data() {
 		return {
 			fetching: true,
+			moreFetching: false,
 			existMore: false,
-			notes: [],
+			empty: false,
 			offset: 0
 		};
 	},
@@ -46,33 +43,43 @@ export default Vue.extend({
 			this.fetching = true;
 			Progress.start();
 
-			(this as any).api('notes/search', {
-				limit: limit + 1,
-				query: this.q
-			}).then(notes => {
-				if (notes.length == limit + 1) {
-					notes.pop();
-					this.existMore = true;
-				}
-				this.notes = notes;
-				this.fetching = false;
-				Progress.done();
-			});
+			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
+				(this as any).api('notes/search', {
+					limit: limit + 1,
+					offset: this.offset,
+					query: this.q
+				}).then(notes => {
+					if (notes.length == 0) this.empty = true;
+					if (notes.length == limit + 1) {
+						notes.pop();
+						this.existMore = true;
+					}
+					res(notes);
+					this.fetching = false;
+					Progress.done();
+				}, rej);
+			}));
 		},
 		more() {
 			this.offset += limit;
-			(this as any).api('notes/search', {
+
+			const promise = (this as any).api('notes/search', {
 				limit: limit + 1,
 				offset: this.offset,
 				query: this.q
-			}).then(notes => {
+			});
+
+			promise.then(notes => {
 				if (notes.length == limit + 1) {
 					notes.pop();
 				} else {
 					this.existMore = false;
 				}
-				this.notes = this.notes.concat(notes);
+				notes.forEach(n => (this.$refs.timeline as any).append(n));
+				this.moreFetching = false;
 			});
+
+			return promise;
 		}
 	}
 });