Sdks
React SDK
Add authentication to any React application with @hellojohn/react — hooks, components, and context provider.
React SDK
@hellojohn/react provides hooks and components for adding authentication to React applications. It works with React 18+, Vite, Create React App, and any React-based framework.
Installation
npm install @hellojohn/react
# or
yarn add @hellojohn/react
# or
pnpm add @hellojohn/reactSetup
Wrap your app with HelloJohnProvider:
// main.tsx (or index.tsx)
import { HelloJohnProvider } from '@hellojohn/react';
ReactDOM.createRoot(document.getElementById('root')!).render(
<HelloJohnProvider publishableKey="pk_live_...">
<App />
</HelloJohnProvider>
);Get your publishable key from the HelloJohn dashboard under Settings → API Keys.
Authentication hooks
useSignIn
import { useSignIn } from '@hellojohn/react';
function SignInForm() {
const { signIn, isLoading, error } = useSignIn();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await signIn({ email, password });
if (result.status === 'mfa_required') {
// Navigate to MFA challenge page
} else if (result.status === 'complete') {
// Navigate to dashboard
}
};
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} type="email" />
<input value={password} onChange={e => setPassword(e.target.value)} type="password" />
{error && <p>{error.message}</p>}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Signing in...' : 'Sign In'}
</button>
</form>
);
}useSignUp
import { useSignUp } from '@hellojohn/react';
function SignUpForm() {
const { signUp, isLoading, error } = useSignUp();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await signUp({
email: 'alice@example.com',
password: 'strongpassword',
displayName: 'Alice',
});
// User is signed in and session is set automatically
};
}useSignOut
import { useSignOut } from '@hellojohn/react';
function NavBar() {
const { signOut } = useSignOut();
return <button onClick={() => signOut()}>Sign Out</button>;
}useUser
Access the current user from anywhere in your component tree:
import { useUser } from '@hellojohn/react';
function Profile() {
const { user, isLoaded, isSignedIn } = useUser();
if (!isLoaded) return <Spinner />;
if (!isSignedIn) return <Redirect to="/sign-in" />;
return (
<div>
<h1>Hello, {user.displayName}</h1>
<p>{user.email}</p>
<p>Role: {user.role}</p>
<p>Tenant: {user.tenantId}</p>
</div>
);
}useSession
Access the current session and tokens:
import { useSession } from '@hellojohn/react';
function ApiExample() {
const { session, getToken } = useSession();
const fetchData = async () => {
const token = await getToken(); // Returns fresh access token (auto-refreshed)
const res = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
};
}useMagicLink
import { useMagicLink } from '@hellojohn/react';
function MagicLinkForm() {
const { sendMagicLink, isLoading, isSent } = useMagicLink();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await sendMagicLink({ email: 'alice@example.com' });
};
if (isSent) return <p>Check your email!</p>;
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<button disabled={isLoading}>Send magic link</button>
</form>
);
}useMFA
import { useMFA } from '@hellojohn/react';
function MFASetup() {
const { enrollTOTP, verifyTOTP, unenroll, enrolledMethods } = useMFA();
const startEnrollment = async () => {
const { qrCodeUrl, secret } = await enrollTOTP();
// Show QR code to user
setQrCode(qrCodeUrl);
};
const completeEnrollment = async (code: string) => {
await verifyTOTP({ code, isEnrollment: true });
// MFA enrolled!
};
}Pre-built components
For faster integration, use the pre-built UI components:
import {
SignIn,
SignUp,
UserButton,
UserProfile,
} from '@hellojohn/react';
// Drop-in sign-in form
<SignIn afterSignInUrl="/dashboard" />
// Drop-in sign-up form
<SignUp afterSignUpUrl="/onboarding" />
// Avatar button with dropdown (sign out, manage account)
<UserButton afterSignOutUrl="/" />
// Full profile management page
<UserProfile />Route protection
Higher-order component
import { withAuth } from '@hellojohn/react';
const DashboardPage = withAuth(function Dashboard() {
return <div>Protected content</div>;
}, {
redirectTo: '/sign-in', // where to redirect if not signed in
});Hook-based guard
import { useAuth } from '@hellojohn/react';
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isSignedIn, isLoaded } = useAuth();
if (!isLoaded) return <Spinner />;
if (!isSignedIn) return <Navigate to="/sign-in" />;
return <>{children}</>;
}HelloJohnProvider options
<HelloJohnProvider
publishableKey="pk_live_..."
// Optional: override API URL (for self-hosted)
apiUrl="https://auth.yourdomain.com"
// Optional: custom tenant ID
tenantId="tnt_01H..."
// Optional: token storage
tokenStorage="cookie" // "cookie" (default) | "localStorage" | "memory"
// Optional: after sign-out URL
afterSignOutUrl="/"
>
<App />
</HelloJohnProvider>TypeScript
The SDK is fully typed. Key types:
import type {
User,
Session,
SignInResult,
SignUpResult,
AuthError,
MFAMethod,
} from '@hellojohn/react';React Native
For React Native, use @hellojohn/react-native instead — it handles native token storage and deep linking for magic links.