useStatus: Auth and Health
The React SDK separates identity and health from state data:
useStatus()returns{ auth, health }: auth status with auth actions, plus overall client health.- Keyed
usePublicState(key)returns{ value }. - Keyed
useUserState(key)returns{ value, set, clear, syncStatus, draft }, wheredraftexposesdisplayValue,isPending,set,save, anddiscard.
All hooks must be called inside a component that is a descendant of SkyStateProvider.
useStatus
typescript
function useStatus(): { auth: SkyStateAuth; health: SkyStateHealth }
type SkyStateAuth = (
| {
status: 'unauthenticated';
detail: { reason: 'signed_out' } | { reason: 'expired'; error: SkyStateAuthError };
}
| { status: 'authenticating' }
| {
status: 'authenticated';
idToken: string;
claims: Readonly<Record<string, unknown>>;
name: string | null;
email: string | null;
sessionPersisted: boolean;
}
) & {
loginWithRedirect: () => Promise<void>;
logout: () => Promise<void>;
};tsx
import { useStatus } from '@skystate/react';
export function LoginButton() {
const { auth } = useStatus();
const isAuthenticated = auth.status === 'authenticated';
return (
<button onClick={isAuthenticated ? auth.logout : auth.loginWithRedirect}>
{isAuthenticated ? 'Log out' : 'Log in'}
</button>
);
}Narrow on the authenticated branch before reading idToken, claims, name, or email. name and email are always present on the authenticated snapshot and typed string | null: they are null when the ID token has no usable claim, never omitted.
On the unauthenticated branch, detail.reason says why: 'signed_out' for a plain signed-out session, or 'expired' (with the rejecting error) when the session ended because the credential was rejected.
logout() clears the local session immediately, then fires a best-effort server call to revoke the refresh token; it never waits for or fails on the revocation. The lower-level core clearAuthTokens() API is local-only token deletion; React apps should prefer useStatus().auth.logout() for user sign-out.
Health
typescript
type SkyStateHealth =
| { status: 'loading' }
| { status: 'ok' }
| { status: 'error'; error: SkyStateHealthError | AsyncMisuseError };tsx
import { useStatus } from '@skystate/react';
export function StatusBar() {
const { health } = useStatus();
if (health.status === 'loading') return <div>Loading...</div>;
if (health.status === 'error') {
return <div>SkyState error: {health.error.message}</div>;
}
return <div>Connected</div>;
}health summarizes the whole client: loading while the client is initializing or public/user state is still loading, error while an operational error (network, server, rate limit, quota, configuration, protocol) is active, otherwise ok. Retries keep running in the background, so health returns to ok on its own when the failing source recovers. The one exception is an invalid_path misuse error from a broken functional updater: it pins health to error for the client's lifetime (until remount).
useStatus() never exposes value, set, or draft. Keyed hooks never expose auth or health.
Auth Errors and onError
The SkyStateProvider accepts an optional onError callback for provider, state, and auth-pipeline errors. When onError is omitted, development builds log the error to the console.
tsx
<SkyStateProvider
account="acc_example"
project="my-app"
environment="production"
onError={(err) => {
console.error(err.code, err.message);
}}
>
<App />
</SkyStateProvider>Token refresh failures
Temporary sign-in renewal failures keep the current session active while the SDK retries in the background.
Expired or invalid sessions transition to unauthenticated with detail: { reason: 'expired', error }, so the user can sign in again; onError receives the same error.
Session persistence
If the token storage adapter cannot persist tokens during login, the in-memory session continues but will not survive a page reload. This is not reported through onError or health; check the authenticated snapshot instead:
tsx
const { auth } = useStatus();
if (auth.status === 'authenticated' && !auth.sessionPersisted) {
showToast('Session will not persist after reload - storage unavailable.');
}Permission failures (403)
A 401 or 403 on user-state load or save means the server rejected the current credential. There is no separate authorization error code: both surface as an error with code: 'authentication', and both end the session the same way. Check that the provider is mounted with the right account, project, and environment, and that the project's auth-provider settings allow the signed-in user.
User State with Auth
tsx
import { useStatus, useUserState } from '@skystate/react';
export function Preferences() {
const { auth, health } = useStatus();
const { value: theme, set: setTheme } = useUserState('theme', 'dark');
if (auth.status === 'authenticating') return null;
if (auth.status !== 'authenticated') {
return <button onClick={auth.loginWithRedirect}>Sign in</button>;
}
if (health.status === 'loading') return <Skeleton />;
if (health.status === 'error') return <div>{health.error.message}</div>;
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Theme: {theme}
</button>
);
}