@tenxyte/react — Integration Guide
React hooks for the Tenxyte SDK. Components automatically re-render when authentication state changes.
Installation
Requirements: React 18+ or 19+, @tenxyte/core ^0.10.0.
Setup
1. Create the client and wrap your application
// main.tsx
import { TenxyteClient, LocalStorageAdapter } from '@tenxyte/core';
import { TenxyteProvider } from '@tenxyte/react';
import App from './App';
const tx = new TenxyteClient({
baseUrl: 'https://api.my-backend.com',
accessKey: 'pkg_abc123',
storage: new LocalStorageAdapter(),
// cookieMode: true, // Enable if backend uses HttpOnly refresh tokens
});
function Root() {
return (
<TenxyteProvider client={tx}>
<App />
</TenxyteProvider>
);
}
2. Use hooks in any component
import { useAuth, useUser, useRbac, useOrganization } from '@tenxyte/react';
function Dashboard() {
const { isAuthenticated, loading, logout } = useAuth();
const { user } = useUser();
const { hasRole } = useRbac();
if (loading) return <p>Loading...</p>;
if (!isAuthenticated) return <LoginPage />;
return (
<div>
<p>Welcome, {user?.email}</p>
{hasRole('admin') && <AdminPanel />}
<button onClick={logout}>Sign out</button>
</div>
);
}
Hooks
useAuth()
Reactive authentication state and actions.
const {
isAuthenticated, // boolean — true if access token is valid and not expired
loading, // boolean — true during initial load from storage
isLoading, // boolean — alias for loading (API consistency)
accessToken, // string | null — raw JWT token
loginWithEmail, // (data: { email, password, device_info?, totp_code? }) => Promise<void>
loginWithPhone, // (data: { phone_country_code, phone_number, password, device_info? }) => Promise<void>
logout, // () => Promise<void>
register, // (data) => Promise<void>
} = useAuth();
Example — Login form:
import { useState, FormEvent } from 'react';
import { useAuth } from '@tenxyte/react';
function LoginPage() {
const { loginWithEmail } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setError('');
try {
await loginWithEmail({ email, password });
} catch (err: any) {
setError(err.error || 'Login failed');
}
};
return (
<form onSubmit={handleSubmit}>
{error && <p style={{ color: 'red' }}>{error}</p>}
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
required
/>
<input
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
type="password"
required
/>
<button type="submit">Sign in</button>
</form>
);
}
Example — Registration:
function RegisterPage() {
const { register } = useAuth();
const handleRegister = async () => {
await register({
email: 'new@example.com',
password: 'StrongP@ss1',
first_name: 'Jane',
last_name: 'Doe',
});
};
return <button onClick={handleRegister}>Create account</button>;
}
useUser()
Decoded JWT user and profile management.
const {
user, // DecodedTenxyteToken | null — decoded JWT payload
loading, // boolean
getProfile, // () => Promise<UserProfile> — full profile from API
updateProfile, // (data) => Promise<unknown>
} = useUser();
Example:
import { useUser } from '@tenxyte/react';
function UserBadge() {
const { user, loading } = useUser();
if (loading || !user) return null;
return (
<div>
<span>{user.first_name} {user.last_name}</span>
<small>{user.email}</small>
</div>
);
}
Example — Profile editing:
function ProfileEditor() {
const { user, updateProfile, getProfile } = useUser();
const [firstName, setFirstName] = useState(user?.first_name ?? '');
const handleSave = async () => {
await updateProfile({ first_name: firstName });
await getProfile(); // Refresh data
};
return (
<div>
<input value={firstName} onChange={(e) => setFirstName(e.target.value)} />
<button onClick={handleSave}>Save</button>
</div>
);
}
useOrganization()
Multi-tenant context for organizations (B2B).
const {
activeOrg, // string | null — active org slug
switchOrganization, // (slug: string) => void
clearOrganization, // () => void
} = useOrganization();
Example:
import { useOrganization } from '@tenxyte/react';
function OrgSwitcher({ orgs }: { orgs: { slug: string; name: string }[] }) {
const { activeOrg, switchOrganization, clearOrganization } = useOrganization();
return (
<select
value={activeOrg ?? ''}
onChange={(e) =>
e.target.value ? switchOrganization(e.target.value) : clearOrganization()
}
>
<option value="">No organization</option>
{orgs.map((o) => (
<option key={o.slug} value={o.slug}>{o.name}</option>
))}
</select>
);
}
useRbac()
Synchronous role and permission checks from the current JWT.
const {
hasRole, // (role: string) => boolean
hasPermission, // (permission: string) => boolean
hasAnyRole, // (roles: string[]) => boolean
hasAllRoles, // (roles: string[]) => boolean
} = useRbac();
Example — Route guard:
import { useRbac } from '@tenxyte/react';
function AdminPanel() {
const { hasRole } = useRbac();
if (!hasRole('admin')) {
return <p>Access denied. Admin role required.</p>;
}
return <div>Administration panel</div>;
}
Example — Conditional rendering:
function ActionButtons() {
const { hasPermission } = useRbac();
return (
<div>
{hasPermission('posts.create') && <button>New post</button>}
{hasPermission('posts.delete') && <button>Delete</button>}
</div>
);
}
Direct Client Access
For features not covered by hooks (social login, 2FA, AIRS, etc.), access the TenxyteClient directly:
import { useTenxyteClient } from '@tenxyte/react';
function SocialLoginButtons() {
const client = useTenxyteClient();
const handleGoogleLogin = async () => {
await client.auth.loginWithSocial('google', {
id_token: 'google-jwt-token',
});
};
const handleGitHubCallback = async (code: string, redirectUri: string, codeVerifier?: string) => {
await client.auth.handleSocialCallback('github', code, redirectUri, codeVerifier);
};
return (
<div>
<button onClick={handleGoogleLogin}>Sign in with Google</button>
</div>
);
}
Direct access examples
const client = useTenxyteClient();
// 2FA
const status = await client.security.get2FAStatus();
await client.security.setup2FA();
// Magic Link
await client.auth.requestMagicLink({
email: 'user@example.com',
validation_url: 'https://myapp.com/verify',
});
// AIRS
const agents = await client.ai.listAgentTokens();
// GDPR
await client.gdpr.requestAccountDeletion({ reason: 'Test' });
Error Handling
All SDK methods throw a TenxyteError on failure:
import type { TenxyteError } from '@tenxyte/core';
function LoginForm() {
const { loginWithEmail } = useAuth();
const [error, setError] = useState<TenxyteError | null>(null);
const handleLogin = async (email: string, password: string) => {
try {
setError(null);
await loginWithEmail({ email, password });
} catch (err) {
setError(err as TenxyteError);
}
};
return (
<div>
{error && (
<div className="error">
<strong>{error.code}</strong>: {error.error}
{error.retry_after && <p>Retry in {error.retry_after}s</p>}
</div>
)}
{/* form... */}
</div>
);
}
Common error codes: INVALID_CREDENTIALS, ACCOUNT_LOCKED, 2FA_REQUIRED, RATE_LIMITED, MISSING_REFRESH_TOKEN, INVALID_REDIRECT_URI, APP_AUTH_REQUIRED, APP_AUTH_ORIGIN_REQUIRED, APP_AUTH_ORIGIN_DENIED.
Cookie Mode
If the backend is configured with TENXYTE_REFRESH_TOKEN_COOKIE_ENABLED=True:
const tx = new TenxyteClient({
baseUrl: 'https://api.my-backend.com',
accessKey: 'pkg_abc123',
cookieMode: true,
});
Hooks work transparently — auto-refresh uses HttpOnly cookies instead of local storage. No component changes required.
See the Core Guide — Cookie Mode for details.
How It Works
TenxyteProvider places the TenxyteClient instance in React context. Each hook subscribes to SDK events (token:stored, token:refreshed, session:expired) and triggers a re-render when authentication state changes. All state updates are automatic — no manual invalidation needed.
Full Application Example
// App.tsx
import { useAuth, useUser, useRbac } from '@tenxyte/react';
export default function App() {
const { isAuthenticated, loading, loginWithEmail, logout } = useAuth();
const { user } = useUser();
const { hasRole } = useRbac();
if (loading) return <div className="spinner">Loading...</div>;
if (!isAuthenticated) {
return (
<form onSubmit={async (e) => {
e.preventDefault();
const fd = new FormData(e.currentTarget);
await loginWithEmail({
email: fd.get('email') as string,
password: fd.get('password') as string,
});
}}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Sign in</button>
</form>
);
}
return (
<div>
<header>
<span>Signed in as: {user?.email}</span>
<button onClick={logout}>Sign out</button>
</header>
<main>
<h1>Dashboard</h1>
{hasRole('admin') && <section>Admin area</section>}
</main>
</div>
);
}
See Also
- JavaScript SDK Overview
- Core Guide — Full API for all 10 modules
- Vue Guide — Vue 3 equivalent
- API Endpoints
- Security Guide