Skip to content

Adapter

The Adapter pattern converts a third-party library’s API into a React-compatible interface. It isolates application code from library details — transforming imperative callbacks and mutable state into reactive hooks with typed state.

graph LR
    Keycloak["keycloak-js\n(imperative)"] --> Adapter["useKeycloakInit\n(adapter)"]
    Adapter --> React["React state\n(reactive)"]
SourceAdapterTarget
keycloak-js APIuseKeycloakInit hookReactive { authenticated, loading, user } state
klaro/dist/klaro-no-csscreateKlaroCookieConsentProvider()CookieConsentProvider interface
keycloak-js nativeAdapted interface
keycloak.init({ onLoad, pkceMethod })Single useEffect with init guard
keycloak.loadUserInfo() → Promiseuser: KeycloakUserInfo | null (state)
keycloak.onTokenExpired = callbackAutomatic renewal every 60s
keycloak.token (mutable string)Transparent wiring to setTokenGetter()
keycloak.authenticated (mutable bool)authenticated: boolean (reactive)

Keycloak-js uses callbacks, promises, and mutable properties — a paradigm mismatch with React’s declarative model. The adapter bridges this gap so that the rest of the application works with standard React state.

import { useKeycloakInit } from '@granit/react-authentication';
function AuthProvider({ children }: { children: React.ReactNode }) {
const { authenticated, loading, user, login, logout } = useKeycloakInit({
url: import.meta.env.VITE_KEYCLOAK_URL,
realm: import.meta.env.VITE_KEYCLOAK_REALM,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
});
if (loading) return <Spinner />;
if (!authenticated) { login(); return null; }
return (
<AuthContext.Provider value={{ authenticated, user, login, logout }}>
{children}
</AuthContext.Provider>
);
}