Athena

Customization Guide

Configure AuthProvider defaults and package-owned UI overrides for Athena Auth UI.

This package exposes two layers of customization:

  1. AuthProvider config from @xylex-group/athena-auth-ui for auth behavior that belongs to the packaged Athena UI runtime.
  2. The HeroUI package ui overrides 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:

  • localization Use this for broad text changes across auth and settings screens.
  • redirectTo Use this for the default post-auth redirect target.
  • viewPaths Use this when you want the built-in route segments to change.
  • socialProviders Use this to control which OAuth providers are configured and in what default order.
  • plugins Use 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.separator
  • signIn, signUp, forgotPassword, resetPassword, magicLink
  • providerButtons
  • passkeyButton
  • signOut

auth.routes

  • signIn
  • signUp
  • forgotPassword
  • signOut
  • settings
  • afterSignIn
  • afterSignUp
  • afterForgotPassword
  • afterResetPassword
  • afterSignOut
  • callbackURL

auth.classNames

  • authCardHeader
  • signIn
  • signUp
  • forgotPassword
  • resetPassword
  • magicLink
  • signInUsername
  • providerButtons
  • providerButton
  • passkeyButton
  • signOut

auth.components

  • loadingIndicator
  • signOutLoading

pages

  • auth
  • settings

settings

  • navigation

settings.classNames

  • root
  • mobileTrigger
  • mobilePanel
  • nav
  • navItem
  • navItemActive
  • content

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

  • unauthenticatedRoot
  • unauthenticatedLink
  • trigger
  • iconTrigger
  • popover
  • menuHeader
  • menuItem

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:

  1. Upstream auth runtime defaults
  2. AuthProvider ui
  3. 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:

ComponenttemplateKeyeventTypeoverrideSelector
EmailVerificationEmailverification_emailuser.email.verifytemplateKey
ResetPasswordEmailpassword_reset_emailuser.password.resettemplateKey
OtpEmailtwo_factor_otp_emailuser.sign-in.otptemplateKey
OrganizationInvitationEmailorganization_invitation_emailorganization.member.invitetemplateKey

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:

ComponenttemplateKeyeventTypeoverrideSelector
MagicLinkEmailmagic_link_emailuser.sign-in.emaileventType
EmailChangedEmailemail_changed_emailuser.email.changedeventType
PasswordChangedEmailpassword_changed_emailuser.password.changedeventType
NewDeviceEmailnew_device_emailuser.security.alerteventType

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