diff --git a/migration/1586641139527-remote-reaction.ts b/migration/1586641139527-remote-reaction.ts
new file mode 100644
index 0000000000..5a7fb36e35
--- /dev/null
+++ b/migration/1586641139527-remote-reaction.ts
@@ -0,0 +1,12 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class remoteReaction1586641139527 implements MigrationInterface {
+    name = 'remoteReaction1586641139527'
+    public async up(queryRunner: QueryRunner): Promise<any> {
+      await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(260)`, undefined);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<any> {
+      await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, undefined);
+  }
+}
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 18d5cc34ba..a39520fb4c 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -301,6 +301,14 @@ export default Vue.extend({
 				case 'reacted': {
 					const reaction = body.reaction;
 
+					if (body.emoji) {
+						const emojis = this.appearNote.emojis || [];
+						if (!emojis.includes(body.emoji)) {
+							emojis.push(body.emoji);
+							Vue.set(this.appearNote, 'emojis', emojis);
+						}
+					}
+
 					if (this.appearNote.reactions == null) {
 						Vue.set(this.appearNote, 'reactions', {});
 					}
diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue
index f415887e76..97a2fe1873 100644
--- a/src/client/components/notification.vue
+++ b/src/client/components/notification.vue
@@ -12,7 +12,7 @@
 			<fa :icon="faReply" v-else-if="notification.type === 'reply'"/>
 			<fa :icon="faAt" v-else-if="notification.type === 'mention'"/>
 			<fa :icon="faQuoteLeft" v-else-if="notification.type === 'quote'"/>
-			<x-reaction-icon v-else-if="notification.type === 'reaction'" :reaction="notification.reaction" :no-style="true"/>
+			<x-reaction-icon v-else-if="notification.type === 'reaction'" :reaction="notification.reaction" :customEmojis="notification.note.emojis" :no-style="true"/>
 		</div>
 	</div>
 	<div class="tail">
diff --git a/src/client/components/reaction-icon.vue b/src/client/components/reaction-icon.vue
index 9155c59440..3c6d56b80a 100644
--- a/src/client/components/reaction-icon.vue
+++ b/src/client/components/reaction-icon.vue
@@ -1,5 +1,5 @@
 <template>
-<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :normal="true" :no-style="noStyle"/>
+<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :customEmojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
 </template>
 
 <script lang="ts">
@@ -12,6 +12,10 @@ export default Vue.extend({
 			type: String,
 			required: true
 		},
+		customEmojis: {
+			required: false,
+			default: () => []
+		},
 		noStyle: {
 			type: Boolean,
 			required: false,
diff --git a/src/client/components/reactions-viewer.reaction.vue b/src/client/components/reactions-viewer.reaction.vue
index 7494fde25f..67774cbb39 100644
--- a/src/client/components/reactions-viewer.reaction.vue
+++ b/src/client/components/reactions-viewer.reaction.vue
@@ -9,7 +9,7 @@
 	ref="reaction"
 	v-particle
 >
-	<x-reaction-icon :reaction="reaction" ref="icon"/>
+	<x-reaction-icon :reaction="reaction" :customEmojis="note.emojis" ref="icon"/>
 	<span>{{ count }}</span>
 </button>
 </template>
diff --git a/src/misc/reaction-lib.ts b/src/misc/reaction-lib.ts
index 43dbe1cc2c..e5da5ca4aa 100644
--- a/src/misc/reaction-lib.ts
+++ b/src/misc/reaction-lib.ts
@@ -1,6 +1,7 @@
 import { emojiRegex } from './emoji-regex';
 import { fetchMeta } from './fetch-meta';
 import { Emojis } from '../models';
+import { toPunyNullable } from './convert-host';
 
 const legacies: Record<string, string> = {
 	'like':     '👍',
@@ -40,12 +41,20 @@ export function convertLegacyReactions(reactions: Record<string, number>) {
 		}
 	}
 
-	return _reactions;
+	const _reactions2 = {} as Record<string, number>;
+
+	for (const reaction of Object.keys(_reactions)) {
+		_reactions2[decodeReaction(reaction).reaction] = _reactions[reaction];
+	}
+
+	return _reactions2;
 }
 
-export async function toDbReaction(reaction?: string | null): Promise<string> {
+export async function toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise<string> {
 	if (reaction == null) return await getFallbackReaction();
 
+	reacterHost = toPunyNullable(reacterHost);
+
 	// 文字列タイプのリアクションを絵文字に変換
 	if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
 
@@ -61,18 +70,58 @@ export async function toDbReaction(reaction?: string | null): Promise<string> {
 
 	const custom = reaction.match(/^:([\w+-]+):$/);
 	if (custom) {
+		const name = custom[1];
 		const emoji = await Emojis.findOne({
-			host: null,
-			name: custom[1],
+			host: reacterHost || null,
+			name,
 		});
 
-		if (emoji) return reaction;
+		if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`
 	}
 
 	return await getFallbackReaction();
 }
 
+type DecodedReaction = {
+	/**
+	 * リアクション名 (Unicode Emoji or ':name@hostname' or ':name@.')
+	 */
+	reaction: string;
+
+	/**
+	 * name (カスタム絵文字の場合name, Emojiクエリに使う)
+	 */
+	name?: string;
+
+	/**
+	 * host (カスタム絵文字の場合host, Emojiクエリに使う)
+	 */
+	host?: string | null;
+};
+
+export function decodeReaction(str: string): DecodedReaction {
+	const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/);
+
+	if (custom) {
+		const name = custom[1];
+		const host = custom[2] || null;
+
+		return {
+			reaction: `:${name}@${host || '.'}:`,	// ローカル分は@以降を省略するのではなく.にする
+			name,
+			host
+		};
+	}
+
+	return {
+		reaction: str,
+		name: undefined,
+		host: undefined
+	};
+}
+
 export function convertLegacyReaction(reaction: string): string {
+	reaction = decodeReaction(reaction).reaction;
 	if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
 	return reaction;
 }
diff --git a/src/models/entities/note-reaction.ts b/src/models/entities/note-reaction.ts
index 995748760c..ed38450bb2 100644
--- a/src/models/entities/note-reaction.ts
+++ b/src/models/entities/note-reaction.ts
@@ -36,7 +36,7 @@ export class NoteReaction {
 	public note: Note | null;
 
 	@Column('varchar', {
-		length: 130
+		length: 260
 	})
 	public reaction: string;
 }
diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 2aad5c0fa3..d29a48b7ec 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -5,9 +5,11 @@ import { Emojis, Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls
 import { ensure } from '../../prelude/ensure';
 import { SchemaType } from '../../misc/schema';
 import { awaitAll } from '../../prelude/await-all';
-import { convertLegacyReaction, convertLegacyReactions } from '../../misc/reaction-lib';
+import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '../../misc/reaction-lib';
 import { toString } from '../../mfm/toString';
 import { parse } from '../../mfm/parse';
+import { Emoji } from '../entities/emoji';
+import { concat } from '../../prelude/array';
 
 export type PackedNote = SchemaType<typeof packedNoteSchema>;
 
@@ -129,31 +131,61 @@ export class NoteRepository extends Repository<Note> {
 			};
 		}
 
+		/**
+		 * 添付用emojisを解決する
+		 * @param emojiNames Note等に添付されたカスタム絵文字名 (:は含めない)
+		 * @param noteUserHost Noteのホスト
+		 * @param reactionNames Note等にリアクションされたカスタム絵文字名 (:は含めない)
+		 */
 		async function populateEmojis(emojiNames: string[], noteUserHost: string | null, reactionNames: string[]) {
-			const where = [] as {}[];
+			let all = [] as {
+				name: string,
+				url: string
+			}[];
 
+			// カスタム絵文字
 			if (emojiNames?.length > 0) {
-				where.push({
-					name: In(emojiNames),
-					host: noteUserHost
-				});
+				const tmp = await Emojis.find({
+					where: {
+						name: In(emojiNames),
+						host: noteUserHost
+					},
+					select: ['name', 'host', 'url']
+				}).then(emojis => emojis.map((emoji: Emoji) => {
+					return {
+						name: emoji.name,
+						url: emoji.url,
+					};
+				}));
+
+				all = concat([all, tmp]);
 			}
 
-			reactionNames = reactionNames?.filter(x => x.match(/^:[^:]+:$/)).map(x => x.replace(/:/g, ''));
+			const customReactions = reactionNames?.map(x => decodeReaction(x)).filter(x => x.name);
 
-			if (reactionNames?.length > 0) {
-				where.push({
-					name: In(reactionNames),
-					host: null
-				});
+			if (customReactions?.length > 0) {
+				const where = [] as {}[];
+
+				for (const customReaction of customReactions) {
+					where.push({
+						name: customReaction.name,
+						host: customReaction.host
+					});
+				}
+
+				const tmp = await Emojis.find({
+					where,
+					select: ['name', 'host', 'url']
+				}).then(emojis => emojis.map((emoji: Emoji) => {
+					return {
+						name: `${emoji.name}@${emoji.host || '.'}`,	// @host付きでローカルは.
+						url: emoji.url,
+					};
+				}));
+				all = concat([all, tmp]);
 			}
 
-			if (where.length === 0) return [];
-
-			return Emojis.find({
-				where,
-				select: ['name', 'host', 'url', 'aliases']
-			});
+			return all;
 		}
 
 		async function populateMyReaction() {
diff --git a/src/remote/activitypub/kernel/like.ts b/src/remote/activitypub/kernel/like.ts
index b25f80aedc..a877110303 100644
--- a/src/remote/activitypub/kernel/like.ts
+++ b/src/remote/activitypub/kernel/like.ts
@@ -1,7 +1,7 @@
 import { IRemoteUser } from '../../../models/entities/user';
 import { ILike, getApId } from '../type';
 import create from '../../../services/note/reaction/create';
-import { fetchNote } from '../models/note';
+import { fetchNote, extractEmojis } from '../models/note';
 
 export default async (actor: IRemoteUser, activity: ILike) => {
 	const targetUri = getApId(activity.object);
@@ -11,6 +11,8 @@ export default async (actor: IRemoteUser, activity: ILike) => {
 
 	if (actor.id === note.userId) return `skip: cannot react to my note`;
 
+	await extractEmojis(activity.tag || [], actor.host).catch(() => null);
+
 	await create(actor, note, activity._misskey_reaction || activity.content || activity.name);
 	return `ok`;
 };
diff --git a/src/remote/activitypub/renderer/like.ts b/src/remote/activitypub/renderer/like.ts
index e36a3ab0d6..d4dd3663d4 100644
--- a/src/remote/activitypub/renderer/like.ts
+++ b/src/remote/activitypub/renderer/like.ts
@@ -1,12 +1,30 @@
 import config from '../../../config';
 import { NoteReaction } from '../../../models/entities/note-reaction';
 import { Note } from '../../../models/entities/note';
+import { Emojis } from '../../../models';
+import renderEmoji from './emoji';
 
-export const renderLike = (noteReaction: NoteReaction, note: Note) => ({
-	type: 'Like',
-	id: `${config.url}/likes/${noteReaction.id}`,
-	actor: `${config.url}/users/${noteReaction.userId}`,
-	object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
-	content: noteReaction.reaction,
-	_misskey_reaction: noteReaction.reaction
-});
+export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
+	const reaction = noteReaction.reaction;
+
+	const object =  {
+		type: 'Like',
+		id: `${config.url}/likes/${noteReaction.id}`,
+		actor: `${config.url}/users/${noteReaction.userId}`,
+		object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
+		content: reaction,
+		_misskey_reaction: reaction
+	} as any;
+
+	if (reaction.startsWith(':')) {
+		const name = reaction.replace(/:/g, '');
+		const emoji = await Emojis.findOne({
+			name,
+			host: null
+		});
+
+		if (emoji) object.tag = [ renderEmoji(emoji) ];
+	}
+
+	return object;
+};
diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts
index 72750affe0..70cb1adf4b 100644
--- a/src/services/note/reaction/create.ts
+++ b/src/services/note/reaction/create.ts
@@ -4,10 +4,10 @@ import { renderLike } from '../../../remote/activitypub/renderer/like';
 import DeliverManager from '../../../remote/activitypub/deliver-manager';
 import { renderActivity } from '../../../remote/activitypub/renderer';
 import { IdentifiableError } from '../../../misc/identifiable-error';
-import { toDbReaction } from '../../../misc/reaction-lib';
+import { toDbReaction, decodeReaction } from '../../../misc/reaction-lib';
 import { User, IRemoteUser } from '../../../models/entities/user';
 import { Note } from '../../../models/entities/note';
-import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models';
+import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles, Emojis } from '../../../models';
 import { Not } from 'typeorm';
 import { perUserReactionsChart } from '../../chart';
 import { genId } from '../../../misc/gen-id';
@@ -20,7 +20,7 @@ export default async (user: User, note: Note, reaction?: string) => {
 		throw new IdentifiableError('2d8e7297-1873-4c00-8404-792c68d7bef0', 'cannot react to my note');
 	}
 
-	reaction = await toDbReaction(reaction);
+	reaction = await toDbReaction(reaction, user.host);
 
 	const exist = await NoteReactions.findOne({
 		noteId: note.id,
@@ -59,8 +59,27 @@ export default async (user: User, note: Note, reaction?: string) => {
 
 	perUserReactionsChart.update(user, note);
 
+	// カスタム絵文字リアクションだったら絵文字情報も送る
+	const decodedReaction = decodeReaction(reaction);
+
+	let emoji = await Emojis.findOne({
+		where: {
+			name: decodedReaction.name,
+			host: decodedReaction.host
+		},
+		select: ['name', 'host', 'url']
+	});
+
+	if (emoji) {
+		emoji = {
+			name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}`,
+			url: emoji.url
+		} as any;
+	}
+
 	publishNoteStream(note.id, 'reacted', {
 		reaction: reaction,
+		emoji: emoji,
 		userId: user.id
 	});
 
@@ -96,7 +115,7 @@ export default async (user: User, note: Note, reaction?: string) => {
 
 	//#region 配信
 	if (Users.isLocalUser(user) && !note.localOnly) {
-		const content = renderActivity(renderLike(inserted, note));
+		const content = renderActivity(await renderLike(inserted, note));
 		const dm = new DeliverManager(user, content);
 		if (note.userHost !== null) {
 			const reactee = await Users.findOne(note.userId)
diff --git a/src/services/note/reaction/delete.ts b/src/services/note/reaction/delete.ts
index 09566e07ba..fd6628c71f 100644
--- a/src/services/note/reaction/delete.ts
+++ b/src/services/note/reaction/delete.ts
@@ -44,7 +44,7 @@ export default async (user: User, note: Note) => {
 
 	//#region 配信
 	if (Users.isLocalUser(user) && !note.localOnly) {
-		const content = renderActivity(renderUndo(renderLike(exist, note), user));
+		const content = renderActivity(renderUndo(await renderLike(exist, note), user));
 		const dm = new DeliverManager(user, content);
 		if (note.userHost !== null) {
 			const reactee = await Users.findOne(note.userId)