How Sign-In Experience Customization Works in Logto: Console-to-Experience Flow Explained
Logto's sign-in experience customization is handled through a React context-based architecture that fetches configuration from the Core backend, stores it in a shared PageContext, and reflects changes instantly in the Experience SPA without requiring a page reload.
The logto-io/logto repository provides a fully configurable authentication layer where sign-in experience customization occurs entirely within the Console admin UI. This system allows administrators to modify branding, colors, authentication methods, and custom CSS through a live-editable interface that synchronizes immediately with the end-user Experience SPA.
Architecture Overview
The sign-in experience customization follows a strict separation of concerns across four distinct layers. The Data layer handles API communication with getSignInExperienceSettings() fetching from /api/sign-in-experience. The State layer uses PageContext to store the SignInExperienceResponse object. The Hook layer provides the useSignInExperience hook for consuming components, while the Provider layer manages the SettingsProvider to determine whether to load preview or production data.
This architecture enables the Console to edit settings while the Experience SPA consumes the same context, ensuring that any configuration change appears instantly in the live preview without flashing or page reloads.
Data Flow and State Management
Fetching Configuration from the Core API
When the Console page loads, the useSignInExperience hook (located in packages/experience/src/Providers/SettingsProvider/use-sign-in-experience.tsx) triggers an asynchronous fetch to the Core backend. The hook calls getSignInExperienceSettings() from packages/experience/src/utils/sign-in-experience.ts, which makes a GET request to /api/sign-in-experience and returns the complete SignInExperienceResponse object containing branding, colors, language settings, and enabled authentication methods.
Storing Settings in PageContext
The fetched configuration is stored in PageContext (packages/experience/src/Providers/PageContextProvider/PageContext.tsx). This context exposes experienceSettings and setExperienceSettings, making the data accessible to any component in the Experience SPA. The SettingsProvider (packages/experience/src/Providers/SettingsProvider/index.tsx) wraps the application and decides whether to inject preview data (for demo purposes) or real settings from the API.
Editing via the Console
Administrators customize the sign-in flow through the Console interface at packages/console/src/pages/SignInExperience/index.tsx. This page renders sub-components for Branding, Custom UI, Social Connectors, and Sign-in Methods. Each form component updates the local context state immediately.
When an admin clicks Save, the Console makes a PUT /api/sign-in-experience request with the updated SignInExperienceResponse payload. The Core server validates the configuration and persists it to the database, making the changes available for subsequent GET requests.
Live Preview Implementation
The Experience SPA consumes the same PageContext as the Console. Because the context updates instantly when setExperienceSettings is called, the sign-in pages preview new configurations without a full page reload. In use-sign-in-experience.tsx (lines 19-22), the hook sets the theme immediately after fetching data, preventing any visual flashing during the transition.
For development and testing, the system uses mock data from packages/experience/src/__mocks__/logto.tsx, which provides a default SignInExperienceResponse object when the API is unavailable.
Key Implementation Files
| File | Responsibility |
|---|---|
packages/experience/src/Providers/SettingsProvider/use-sign-in-experience.tsx |
Loads settings on mount and applies theming logic. |
packages/experience/src/Providers/PageContextProvider/PageContext.tsx |
Stores experienceSettings and provides the setter function. |
packages/experience/src/Providers/SettingsProvider/index.tsx |
Determines preview vs. real data mode. |
packages/console/src/pages/SignInExperience/index.tsx |
Main Console interface for editing configuration. |
packages/experience/src/utils/sign-in-experience.ts |
Utility functions for GET/PUT API requests. |
packages/experience/src/__mocks__/logto.tsx |
Mock data for development and testing. |
Practical Code Examples
Accessing Settings in a Component
import useSignInExperience from '@/Providers/SettingsProvider/use-sign-in-experience';
import { useContext } from 'react';
import PageContext from '@/Providers/PageContextProvider/PageContext';
// Inside a component
useSignInExperience(); // Triggers the async fetch on mount
const { experienceSettings } = useContext(PageContext);
// `experienceSettings` now contains the full configuration object
Updating Sign-In Methods Programmatically
// Assume `settings` is the current `SignInExperienceResponse`
const updated = {
...settings,
signIn: {
methods: [
{ identifier: SignInIdentifier.Email, password: true, verificationCode: true, isPasswordPrimary: true },
{ identifier: SignInIdentifier.Phone, password: true, verificationCode: true, isPasswordPrimary: true },
],
},
};
// Persist the change (Console UI uses an internal API client)
await fetch('/api/sign-in-experience', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updated),
});
Rendering with Live Preview
// Inside the Experience app
const { experienceSettings } = useContext(PageContext);
if (!experienceSettings) {
return <LoadingScreen />;
}
// Render the sign-in page according to the settings
return <SignInPage config={experienceSettings} />;
Summary
- Logto's sign-in experience customization uses a React context pattern where
PageContextstores theSignInExperienceResponseshared between Console and Experience SPA. - The
useSignInExperiencehook inpackages/experience/src/Providers/SettingsProvider/use-sign-in-experience.tsxhandles fetching from/api/sign-in-experienceand applies themes before rendering. - Changes made in
packages/console/src/pages/SignInExperience/index.tsxpersist viaPUT /api/sign-in-experienceand reflect immediately in the live preview without page reloads. - The architecture separates data fetching, state management, and UI editing into distinct layers for maintainability.
Frequently Asked Questions
How does Logto handle real-time updates to the sign-in experience?
Logto uses a shared PageContext that both the Console and Experience SPA consume. When an admin saves changes via PUT /api/sign-in-experience, the Console updates the local context state, which triggers an immediate re-render of the preview components. This avoids full page reloads and prevents visual flashing by setting the theme before the component mounts (as seen in lines 19-22 of use-sign-in-experience.tsx).
What is the difference between preview data and real settings in Logto?
The SettingsProvider (packages/experience/src/Providers/SettingsProvider/index.tsx) determines whether to load preview data or production settings. Preview data typically comes from packages/experience/src/__mocks__/logto.tsx for development and Storybook demonstrations, while real settings are fetched from the Core API at /api/sign-in-experience in production environments.
Where are sign-in experience settings stored in the Logto codebase?
The configuration is stored in the Core backend database and accessed via the /api/sign-in-experience endpoint. In the frontend, the types and interfaces are defined in the Experience package, with the primary context living in packages/experience/src/Providers/PageContextProvider/PageContext.tsx and API utilities in packages/experience/src/utils/sign-in-experience.ts.
How can I customize the sign-in methods programmatically?
You can modify the signIn.methods array within the SignInExperienceResponse object and send it via a PUT request to /api/sign-in-experience. Each method object requires an identifier (Email or Phone), boolean flags for password and verificationCode, and an isPasswordPrimary boolean to determine the default authentication flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →