diff --git a/build/emojis_plugin.js b/build/emojis_plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..aed52066d64265993a6d745e3ae87f305d717600
--- /dev/null
+++ b/build/emojis_plugin.js
@@ -0,0 +1,64 @@
+import { resolve } from 'node:path'
+import { access } from 'node:fs/promises'
+import { languages, langCodeToCldrName } from '../src/i18n/languages.js'
+
+const annotationsImportPrefix = '@kazvmoe-infra/unicode-emoji-json/annotations/'
+const specialAnnotationsLocale = {
+  ja_easy: 'ja'
+}
+
+const internalToAnnotationsLocale = (internal) => specialAnnotationsLocale[internal] || internal
+
+// This gets all the annotations that are accessible (whose language
+// can be chosen in the settings). Data for other languages are
+// discarded because there is no way for it to be fetched.
+const getAllAccessibleAnnotations = async (projectRoot) => {
+  const imports = (await Promise.all(
+    languages
+      .map(async lang => {
+        const destLang = internalToAnnotationsLocale(lang)
+        const importModule = `${annotationsImportPrefix}${destLang}.json`
+        const importFile = resolve(projectRoot, 'node_modules', importModule)
+        try {
+          await access(importFile)
+          return `'${lang}': () => import('${importModule}')`
+        } catch (e) {
+          return
+        }
+      })))
+        .filter(k => k)
+        .join(',\n')
+
+  return `
+export const annotationsLoader = {
+  ${imports}
+}
+`
+}
+
+const emojiAnnotationsId = 'virtual:pleroma-fe/emoji-annotations'
+const emojiAnnotationsIdResolved = '\0' + emojiAnnotationsId
+
+const emojisPlugin = () => {
+  let projectRoot
+  return {
+    name: 'emojis-plugin',
+    configResolved (conf) {
+      projectRoot = conf.root
+    },
+    resolveId (id) {
+      if (id === emojiAnnotationsId) {
+        return emojiAnnotationsIdResolved
+      }
+      return null
+    },
+    async load (id) {
+      if (id === emojiAnnotationsIdResolved) {
+        return await getAllAccessibleAnnotations(projectRoot)
+      }
+      return null
+    }
+  }
+}
+
+export default emojisPlugin
diff --git a/changelog.d/emoji-handling.remove b/changelog.d/emoji-handling.remove
new file mode 100644
index 0000000000000000000000000000000000000000..c9ead55452814bc9262ef78107847624096297c6
--- /dev/null
+++ b/changelog.d/emoji-handling.remove
@@ -0,0 +1 @@
+Remove emoji annotations code for unused languages from final build
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 66c1f40f861e3f8edbceeb5f9c31a33830c10291..7987f7e63488bc0b571017e15d2673946effd553 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -1,7 +1,9 @@
 import apiService from '../services/api/api.service.js'
 import { instanceDefaultProperties } from './config.js'
-import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
+import { ensureFinalFallback } from '../i18n/languages.js'
 import { useInterfaceStore } from 'src/stores/interface.js'
+// See build/emojis_plugin for more details
+import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations'
 
 const SORTED_EMOJI_GROUP_IDS = [
   'smileys-and-emotion',
@@ -179,10 +181,7 @@ const defaultState = {
 }
 
 const loadAnnotations = (lang) => {
-  const code = langCodeToCldrName(lang)
-  return import(
-    `../../node_modules/@kazvmoe-infra/unicode-emoji-json/annotations/${code}.json`
-  )
+  return annotationsLoader[lang]()
     .then(k => k.default)
 }
 
diff --git a/vite.config.js b/vite.config.js
index 2abf18e5076ce0b34dd6b553b446052bb7b7a600..d57eb32abf22459500b7f485e999772293db1beb 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -5,6 +5,7 @@ import vue from '@vitejs/plugin-vue'
 import vueJsx from '@vitejs/plugin-vue-jsx'
 import stylelint from 'vite-plugin-stylelint'
 import eslint from 'vite-plugin-eslint2'
+import emojisPlugin from './build/emojis_plugin.js'
 import { devSwPlugin, buildSwPlugin, swMessagesPlugin } from './build/sw_plugin.js'
 import copyPlugin from './build/copy_plugin.js'
 import { getCommitHash } from './build/commit_hash.js'
@@ -104,6 +105,7 @@ export default defineConfig(async ({ mode, command }) => {
       devSwPlugin({ swSrc, swDest, transformSW, alias }),
       buildSwPlugin({ swSrc, swDest }),
       swMessagesPlugin(),
+      emojisPlugin(),
       copyPlugin({
         inUrl: '/static/ruffle',
         inFs: resolve(projectRoot, 'node_modules/@ruffle-rs/ruffle')