diff --git a/web_src/js/features/scoped-access-token.js b/web_src/js/features/scoped-access-token.js
new file mode 100644
index 0000000000..c498d4c011
--- /dev/null
+++ b/web_src/js/features/scoped-access-token.js
@@ -0,0 +1,20 @@
+import {createApp} from 'vue';
+
+export async function initScopedAccessTokenCategories() {
+ const el = document.querySelector('#scoped-access-token-selector');
+ if (!el) return;
+
+ const {default: ScopedAccessTokenSelector} = await import(/* webpackChunkName: "scoped-access-token-selector" */'../components/ScopedAccessTokenSelector.vue');
+ try {
+ const View = createApp(ScopedAccessTokenSelector, {
+ isAdmin: JSON.parse(el.getAttribute('data-is-admin')),
+ noAccessLabel: el.getAttribute('data-no-access-label'),
+ readLabel: el.getAttribute('data-read-label'),
+ writeLabel: el.getAttribute('data-write-label'),
+ });
+ View.mount(el);
+ } catch (err) {
+ console.error('ScopedAccessTokenSelector failed to load', err);
+ el.textContent = el.getAttribute('data-locale-component-failed-to-load');
+ }
+}
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 1867556eee..4c3852b406 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -2,7 +2,6 @@
import './bootstrap.js';
import {initRepoActivityTopAuthorsChart} from './components/RepoActivityTopAuthors.vue';
-import {initScopedAccessTokenCategories} from './components/ScopedAccessTokenSelector.vue';
import {initDashboardRepoList} from './components/DashboardRepoList.vue';
import {initGlobalCopyToClipboardListener} from './features/clipboard.js';
@@ -88,6 +87,7 @@ import {initDirAuto} from './modules/dirauto.js';
import {initRepositorySearch} from './features/repo-search.js';
import {initColorPickers} from './features/colorpicker.js';
import {initAdminSelfCheck} from './features/admin/selfcheck.js';
+import {initScopedAccessTokenCategories} from './features/scoped-access-token.js';
// Init Gitea's Fomantic settings
initGiteaFomantic();