Summary
Please allow the built-in login page to be replaced by a custom Vue component from the custom/ directory, the same way customization.customPages lets you add pages.
Current behavior
/login is hardcoded in the SPA router (spa/src/router/index.ts) and points at @/views/LoginView.vue. No configuration option changes it. The only login-page extension points are loginPageInjections (panelHeader / underInputs / underLoginButton), loginBackgroundImage, loginBackgroundPosition, loginPromptHTML and demoCredentials — all of which add to the built-in page rather than replacing it.
Adding a customPages entry with path: '/login' does not work. I checked why, against 3.29.0 and vue-router 4.3.0 / 4.5.1 / 4.6.4 — there are four independent blockers:
customPages routes are emitted at the /* IMPORTANT:ADMINFORTH ROUTES */ placeholder in spa/src/router/index.ts, which sits after the built-in routes. The built-in /login is therefore registered first.
- vue-router 4 keeps the first matcher for a duplicate path.
resolve('/login') returns the built-in component and the custom one is unreachable — I verified this on all three vue-router versions above, with the routes in both orders.
- Redirects target the route name:
router.push({ name: 'login' }) in App.vue and in spa/src/utils/utils.ts. registerCustomPages names a custom page name: '<path>', i.e. '/login', so name: 'login' never matches it.
- There is no config key for a login component.
Why this matters
The login page is the first screen every user sees and the one teams most often want to restructure: split/centered layouts, tenant or workspace selection, SSO and passkey buttons, a language switcher, custom copy, marketing or legal panels next to the form. loginPageInjections covers three insertion points, so anything structural — replacing the heading and the inputs, two-column layouts, a different field order — is out of reach.
The only alternative today is patching AdminForth's own spa/src/views/LoginView.vue and spa/src/router/index.ts. That is exactly the maintenance burden teams try to avoid: the patch is keyed to an exact package version, so it silently stops applying on the next release, and the failure mode is a login page that quietly reverts to the stock one.
Custom pages already prove the mechanism works. The login page is the notable exception.
Proposed API
Any of these would solve it. The first is the smallest:
customization: {
// a component in ./custom that replaces views/LoginView.vue
loginPage: '@@/MyLogin.vue',
}
or a customPages-shaped declaration, so meta comes along:
customization: {
loginPage: {
component: { file: '@@/MyLogin.vue', meta: { sidebarAndHeader: 'none' } },
},
}
Requirements:
- when unset, behaviour is unchanged;
- the route keeps
path: '/login' and name: 'login', so every router.push({ name: 'login' }), the existing beforeEnter guard and the 401 redirect keep working;
- no new plumbing needed —
custom/ is already aliased to @@ and copied into the SPA sources, so this can reuse whatever customPages uses to emit component: () => import('...');
- please document the contract a custom login page has to implement (at minimum
POST /login with { username, password, rememberMe } and what to do with the response), or export the built-in LoginView.vue so it can be wrapped and extended instead of reimplemented from scratch — the latter would make this far less risky for anyone who still wants the stock flow plus their own layout;
- keep
loginPageInjections, loginBackgroundImage, loginBackgroundPosition and loginPromptHTML working, so projects that only need the small hooks are unaffected.
Workaround today
Patching the route in spa/src/router/index.ts:
- component: () => import('@/views/LoginView.vue'),
+ component: () => import('@@/MyLogin.vue'),
This does work — same path, same name: 'login', and @@ resolves to src/custom via the vite alias in spa/vite.config.ts. But it is a patch against the installed package, which is the whole problem.
Summary
Please allow the built-in login page to be replaced by a custom Vue component from the
custom/directory, the same waycustomization.customPageslets you add pages.Current behavior
/loginis hardcoded in the SPA router (spa/src/router/index.ts) and points at@/views/LoginView.vue. No configuration option changes it. The only login-page extension points areloginPageInjections(panelHeader/underInputs/underLoginButton),loginBackgroundImage,loginBackgroundPosition,loginPromptHTMLanddemoCredentials— all of which add to the built-in page rather than replacing it.Adding a
customPagesentry withpath: '/login'does not work. I checked why, against 3.29.0 and vue-router 4.3.0 / 4.5.1 / 4.6.4 — there are four independent blockers:customPagesroutes are emitted at the/* IMPORTANT:ADMINFORTH ROUTES */placeholder inspa/src/router/index.ts, which sits after the built-in routes. The built-in/loginis therefore registered first.resolve('/login')returns the built-in component and the custom one is unreachable — I verified this on all three vue-router versions above, with the routes in both orders.router.push({ name: 'login' })inApp.vueand inspa/src/utils/utils.ts.registerCustomPagesnames a custom pagename: '<path>', i.e.'/login', soname: 'login'never matches it.Why this matters
The login page is the first screen every user sees and the one teams most often want to restructure: split/centered layouts, tenant or workspace selection, SSO and passkey buttons, a language switcher, custom copy, marketing or legal panels next to the form.
loginPageInjectionscovers three insertion points, so anything structural — replacing the heading and the inputs, two-column layouts, a different field order — is out of reach.The only alternative today is patching AdminForth's own
spa/src/views/LoginView.vueandspa/src/router/index.ts. That is exactly the maintenance burden teams try to avoid: the patch is keyed to an exact package version, so it silently stops applying on the next release, and the failure mode is a login page that quietly reverts to the stock one.Custom pages already prove the mechanism works. The login page is the notable exception.
Proposed API
Any of these would solve it. The first is the smallest:
or a
customPages-shaped declaration, sometacomes along:Requirements:
path: '/login'andname: 'login', so everyrouter.push({ name: 'login' }), the existingbeforeEnterguard and the 401 redirect keep working;custom/is already aliased to@@and copied into the SPA sources, so this can reuse whatevercustomPagesuses to emitcomponent: () => import('...');POST /loginwith{ username, password, rememberMe }and what to do with the response), or export the built-inLoginView.vueso it can be wrapped and extended instead of reimplemented from scratch — the latter would make this far less risky for anyone who still wants the stock flow plus their own layout;loginPageInjections,loginBackgroundImage,loginBackgroundPositionandloginPromptHTMLworking, so projects that only need the small hooks are unaffected.Workaround today
Patching the route in
spa/src/router/index.ts:This does work — same path, same
name: 'login', and@@resolves tosrc/customvia the vite alias inspa/vite.config.ts. But it is a patch against the installed package, which is the whole problem.