Customization Guide
Configure AuthProvider defaults and package-owned UI overrides for Athena Auth UI.
This package exposes two layers of customization:
AuthProviderconfig from@xylex-group/athena-auth-uifor auth behavior that belongs to the packaged Athena UI runtime.- The HeroUI package
uioverrides for package-owned copy, routes, class-name slots, loading indicators, and email template metadata.
Use The AuthProvider For Runtime Behavior
These controls already exist on AuthProvider and remain the source of truth for auth runtime behavior:
localizationUse this for broad text changes across auth and settings screens.redirectToUse this for the default post-auth redirect target.viewPathsUse this when you want the built-in route segments to change.socialProvidersUse this to control which OAuth providers are configured and in what default order.pluginsUse this to add or remove package-owned auth features like passkeys, API keys, workspace tooling, theme controls, and organization support.
Use ui For Package Overrides
Pass ui either on AuthProvider for global defaults or on individual exported components for a local override:
import {
AuthProvider,
SignInPage,
type AthenaAuthUiOptions,
} from "@xylex-group/athena-auth-ui"
const ui: AthenaAuthUiOptions = {
pages: {
auth: {
container: "min-h-screen bg-[radial-gradient(circle_at_top,#dbeafe,white_55%)]",
auth: "shadow-2xl",
},
},
auth: {
logo: <img src="/brand/logo.svg" alt="Acme" className="h-10" />,
routes: {
afterSignIn: "/dashboard",
signUp: "/join",
forgotPassword: "/recover-account",
settings: "/account",
},
copy: {
global: {
separator: "or keep going with",
},
signIn: {
title: "Welcome back",
submitLabel: "Enter workspace",
forgotPasswordLink: {
label: "Need help signing in?",
},
signUpLink: {
description: "Need an account?",
label: "Create one",
},
},
providerButtons: {
labels: {
github: "Continue with GitHub Enterprise",
},
},
passkeyButton: {
label: "Use security key",
},
},
classNames: {
authCardHeader: {
title: "tracking-tight text-2xl",
},
signIn: {
card: "border border-sky-100",
submitButton: "bg-sky-600 text-white",
secondaryLink: "text-sky-700",
},
providerButtons: {
base: "gap-2.5",
},
providerButton: {
base: "rounded-full",
},
},
components: {
loadingIndicator: ({ kind }) => (
<span className="inline-flex items-center text-xs uppercase tracking-[0.2em]">
{kind === "signOut" ? "Leaving" : "Working"}
</span>
),
},
methods: {
passkey: "disabled",
oauthProviders: {
google: "hidden",
},
},
},
settings: {
navigation: "hidden",
},
}
export function AuthRoute() {
return (
<AuthProvider authClient={authClient} navigate={({ to }) => router.push(to)} ui={ui}>
<SignInPage />
</AuthProvider>
)
}What The ui Contract Covers
auth.copy
global.separatorsignIn,signUp,forgotPassword,resetPassword,magicLinkproviderButtonspasskeyButtonsignOut
auth.routes
signInsignUpforgotPasswordsignOutsettingsafterSignInafterSignUpafterForgotPasswordafterResetPasswordafterSignOutcallbackURL
auth.classNames
authCardHeadersignInsignUpforgotPasswordresetPasswordmagicLinksignInUsernameproviderButtonsproviderButtonpasskeyButtonsignOut
auth.components
loadingIndicatorsignOutLoading
pages
authsettings
settings
navigation
settings.classNames
rootmobileTriggermobilePanelnavnavItemnavItemActivecontent
Use hideNav on Settings, SettingsPage, AccountSettingsPage, or SecuritySettingsPage when you want to hide the settings navigation for one instance only.
Use ui.settings.navigation = "hidden" when you want to hide the settings navigation globally for everything below an AuthProvider.
userButton.classNames
unauthenticatedRootunauthenticatedLinktriggericonTriggerpopovermenuHeadermenuItem
Per-Component Overrides
Any component that exposes a ui prop merges its local ui object on top of the nearest provider value:
<SignIn
ui={{
auth: {
copy: {
signIn: {
submitLabel: "Open app",
},
},
classNames: {
signIn: {
submitButton: "bg-emerald-600 text-white",
},
},
},
}}
/>The merge order is:
- Upstream auth runtime defaults
AuthProviderui- Component-level
ui
Nested link objects also merge by field, so a component-level signUpLink.label override does not wipe an inherited signUpLink.href or signUpLink.description.
Email Template Keys
The email entrypoint now binds each email component to a stable templateKey, an Athena Auth eventType, and an overrideSelector that tells you which column Athena Auth actually uses when selecting an email_templates override.
import {
EmailVerificationEmail,
getAthenaAuthEmailTemplateCatalog,
} from "@xylex-group/athena-auth-ui/email"
EmailVerificationEmail.templateKey
// "verification_email"
getAthenaAuthEmailTemplateCatalog()Canonical Athena Auth Keys
These keys line up with the built-in Athena Auth template catalog, so Athena Auth resolves overrides by template_key:
| Component | templateKey | eventType | overrideSelector |
|---|---|---|---|
EmailVerificationEmail | verification_email | user.email.verify | templateKey |
ResetPasswordEmail | password_reset_email | user.password.reset | templateKey |
OtpEmail | two_factor_otp_email | user.sign-in.otp | templateKey |
OrganizationInvitationEmail | organization_invitation_email | organization.member.invite | templateKey |
Package-Owned Stable Keys
These components also have unique stable package keys, but Athena Auth does not seed a canonical runtime template_key for these flows. Create or query overrides by event_type instead:
| Component | templateKey | eventType | overrideSelector |
|---|---|---|---|
MagicLinkEmail | magic_link_email | user.sign-in.email | eventType |
EmailChangedEmail | email_changed_email | user.email.changed | eventType |
PasswordChangedEmail | password_changed_email | user.password.changed | eventType |
NewDeviceEmail | new_device_email | user.security.alert | eventType |
Recommended Override Pattern
Use localization when you want broad product copy changes across the auth runtime.
Use viewPaths and redirectTo when the route structure itself is changing.
Use ui.copy, ui.routes, and ui.classNames when the change is specific to this HeroUI package surface.
For canonical emails, create and look up Athena Auth email_templates rows by template_key.
For package-owned non-canonical emails, create and look up Athena Auth email_templates rows by event_type, and treat the component templateKey as a package-level identifier that helps you map the row back to the matching React email component.
const verification = getAthenaAuthEmailTemplate("verification_email")
// create row with template_key = verification.templateKey
const magicLink = getAthenaAuthEmailTemplate("magic_link_email")
// create row with event_type = magicLink.eventType
// optionally also persist template_key = magicLink.templateKey for your own cataloging