Skip to content

Hook Composition

Hook Composition assembles low-level framework hooks with application logic in a wrapper component. The framework hook provides the foundation (authentication, state, lifecycle); the application adds its business logic on top.

graph TD
    Framework["useKeycloakInit()\n(framework layer)"] --> Composition["AuthProvider\n(composition point)"]
    AppLogic["Application logic\n(Capacitor, locale, roles)"] --> Composition
    Composition --> Context["AuthContext.Provider\n(consumed by app)"]
Framework hookApplication layer addsComposition point
useKeycloakInitCapacitor browser, locale, custom redirectsAuthProvider in consumer app
useNotificationContextCustom toast rendering, sound effectsNotification wrapper component
useQueryEndpointDomain-specific column transformsData table component

Framework hooks must remain generic — no Capacitor, Electron, or app-specific code. Hook Composition lets applications extend framework behavior without modifying the framework. The boundary is clear: framework hooks handle the universal lifecycle, application wrappers add the specific logic.

function AuthProvider({ children }: { children: React.ReactNode }) {
const auth = useKeycloakInit({
url: import.meta.env.VITE_KEYCLOAK_URL,
realm: import.meta.env.VITE_KEYCLOAK_REALM,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
});
if (auth.loading) return <Spinner />;
return (
<AuthContext.Provider value={auth}>
{children}
</AuthContext.Provider>
);
}

Advanced composition (Capacitor + custom login)

Section titled “Advanced composition (Capacitor + custom login)”
function AuthProvider({ children }: { children: React.ReactNode }) {
// Framework layer
const {
keycloak, keycloakRef, authenticated, loading, user,
login: hookLogin, logout: hookLogout,
} = useKeycloakInit(config);
// Application layer — platform-specific logic
const login = useCallback(async () => {
if (isNative) {
const url = keycloakRef.current?.createLoginUrl({ redirectUri });
await Browser.open({ url }); // Capacitor: system browser
} else {
hookLogin({ locale });
}
}, [isNative, keycloakRef, hookLogin]);
// Stabilization for context consumers
const value = useMemo(
() => ({ keycloak, authenticated, loading, user, login, logout }),
[keycloak, authenticated, loading, user, login, logout],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

The framework useKeycloakInit handles Keycloak init, PKCE, token refresh, and React state. The application wrapper adds Capacitor-specific login flow and useMemo stabilization — without any change to the framework hook.