Ever tried customizing ZITADEL's default login page and realized you're stuck with their hosted UI? I needed full control over the login experience—branding, flows, languages—so I built a drop-in replacement using Next.js that speaks OIDC natively.
The Problem
ZITADEL is great for identity management, but the default login UI has limitations:
- Customization options are restricted to themes and basic branding
- No control over the actual login flow UX
- Adding custom pages (passkeys, TOTP setup) requires workarounds
- Multi-language support needs external tooling
- Self-hosted instances still look like hosted ZITADEL
The Solution: Custom Login UI
A Next.js app that replaces ZITADEL's login interface entirely. Same OIDC flows, your design.
# Your app redirects to your login UI, not ZITADEL's
OIDC Authorization → Your Custom UI → ZITADEL API → Token
Full control over every screen: login, register, password reset, TOTP, passkeys.
How It Works
- OIDC-compliant - Implements standard authorization flows, works with any OIDC client
- Service user auth - Talks to ZITADEL's API using service account tokens
- i18n built-in - Multi-language support via next-translate out of the box
- Modern stack - Next.js 14 App Router, TypeScript, Tailwind CSS, React Hook Form + Zod
Your existing apps just point to this UI instead of ZITADEL's hosted login.
Get Started in 30 Seconds
git clone https://github.com/quochuydev/zitadel-login-ui.git
cd zitadel-login-ui
cp .env.example .env
yarn install
yarn dev
This gives you:
- Login page - Username/password authentication
- Registration - New user signup flow
- Password reset - Self-service password recovery
- TOTP setup - Two-factor authentication with QR codes
- Passkeys - WebAuthn/FIDO2 passwordless login
Authentication Routes
| Route | Purpose | Features |
|---|---|---|
/login |
User authentication | Username/password, remember me |
/register |
New account creation | Form validation, email verification |
/password |
Password management | Reset flow, change password |
/totp |
2FA setup | QR code generation, backup codes |
/passkeys |
Passwordless auth | WebAuthn registration and login |
/account |
User profile | Manage account settings |
Environment Configuration
| Variable | Purpose |
|---|---|
APP_URL |
Your login UI URL (e.g., http://localhost:3333) |
ZITADEL_URL |
Your ZITADEL instance URL |
ZITADEL_SERVICE_USER_ID |
Service user ID from ZITADEL |
ZITADEL_SERVICE_USER_TOKEN |
API token for service user |
RESEND_API_KEY |
Optional: for email delivery |
Setting Up ZITADEL Service User
- Go to
https://YOUR_ZITADEL/ui/console/instance/members - Create a service user with
IAM_OWNERrole - Generate a personal access token
- Add the user ID and token to your
.env
Why This Works
- Full design control - Every pixel is yours, not ZITADEL's theme system
- Standard OIDC - Any app expecting OIDC works without changes
- Production-ready stack - Next.js 14, TypeScript, Tailwind—battle-tested
-
i18n from day one - Add languages by dropping translation files in
/locales - Modern auth methods - Passkeys and TOTP built in, not bolted on
Try It
Live demo: zitadel-login-ui-v2.vercel.app
git clone https://github.com/quochuydev/zitadel-login-ui.git
cd zitadel-login-ui
yarn dev
Start with the login page, then explore the TOTP and passkey flows to see the full auth experience.
GitHub: quochuydev/zitadel-login-ui
Live Demo: zitadel-login-ui-v2.vercel.app
Are you using ZITADEL's default UI or did you build something custom? I'd love to hear how others handle login customization.
Top comments (1)
Nice write-up — thanks for sharing this! 🙌
Small add-on from our side: ZITADEL’s new Login UI is also a Next.js app, designed to be forked and customized if you want full control over look & flow.
If anyone’s interested, you can check it out here:
zitadel.com/docs/guides/integrate/...