@zaplink/react
v0.4.5
Published
React components for Zaplink authentication
Maintainers
Readme
@zaplink/react
React components and hooks for Zaplink authentication with Clerk-style APIs.
Features
- Provider & Context:
ZaplinkProviderfor app-wide configuration - Authentication Hooks:
useZaplink,useSession,useLoginFlow - Pre-built Components:
SignIn,OTPFormwith Zaplink branding - Conditional Rendering:
SignedIn,SignedOut,RedirectToSignIn - TypeScript: Full type safety
- Smooth Animations: framer-motion powered transitions
- International Phone Input: Country selector with flags
- Auto-refresh: Automatic session management
- Redirect Handling: Clerk-style redirect URL system
Installation
npm install @zaplink/react
# or
pnpm add @zaplink/react
# or
yarn add @zaplink/reactQuick Start
1. Setup Provider
Wrap your app with ZaplinkProvider:
import { ZaplinkProvider } from '@zaplink/react';
import App from './App';
function Root() {
return (
<ZaplinkProvider
publicKey="pk_your_public_key"
signInUrl="/login" // Optional: for auto-redirects
autoRefresh={true}
>
<App />
</ZaplinkProvider>
);
}2. Add Routing
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
</Routes>
</BrowserRouter>
);
}3. Create Login Page
import { SignIn } from '@zaplink/react';
function LoginPage() {
return (
<div style={{
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f9fafb',
}}>
<SignIn />
</div>
);
}4. Protect Routes
import { SignedIn, SignedOut, RedirectToSignIn } from '@zaplink/react';
import Dashboard from '../components/Dashboard';
function HomePage() {
return (
<>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
);
}Components
<ZaplinkProvider>
App-wide configuration provider.
<ZaplinkProvider
publicKey="pk_your_key" // Required
baseUrl="https://api.zaplink.com" // Optional
signInUrl="/login" // Optional: default sign-in page
storage="localStorage" // Optional: localStorage | sessionStorage
autoRefresh={true} // Optional: auto-refresh tokens
>
<App />
</ZaplinkProvider><SignIn>
Pre-built login form with Zaplink branding, phone input, and OTP verification.
import { SignIn } from '@zaplink/react';
<SignIn
defaultPhone="+1234567890" // Optional: pre-fill phone
locale="es" // Optional: locale code
channelId="whatsapp" // Optional: channel ID
fallbackRedirectUrl="/dashboard" // Optional: where to go after login
forceRedirectUrl="/onboarding" // Optional: always redirect here
onSuccess={(session) => {}} // Optional: success callback
onError={(problem) => {}} // Optional: error callback
/><SignedIn> / <SignedOut>
Conditional rendering based on authentication state.
import { SignedIn, SignedOut } from '@zaplink/react';
function Header() {
return (
<nav>
<SignedIn>
<UserButton />
<Link to="/dashboard">Dashboard</Link>
</SignedIn>
<SignedOut>
<Link to="/login">Sign In</Link>
</SignedOut>
</nav>
);
}<RedirectToSignIn>
Automatically redirects to sign-in page when user is not authenticated.
import { SignedOut, RedirectToSignIn } from '@zaplink/react';
function ProtectedPage() {
return (
<>
<SignedIn>
<YourContent />
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
);
}<OTPForm>
Standalone OTP verification form.
import { OTPForm } from '@zaplink/react';
<OTPForm
attemptId="attempt_xxx"
onSuccess={(session) => {}}
onError={(problem) => {}}
/>Hooks
useZaplink()
Access the Zaplink client and configuration.
import { useZaplink } from '@zaplink/react';
function MyComponent() {
const { client, ready, signInUrl } = useZaplink();
const handleLogin = async () => {
const result = await client.startLogin({
phone: '+1234567890',
locale: 'en',
});
};
}useSession()
Access current session state.
import { useSession } from '@zaplink/react';
function UserProfile() {
const { session, loading } = useSession();
if (loading) return <div>Loading...</div>;
if (!session) return <div>Not logged in</div>;
return (
<div>
<p>User ID: {session.userId}</p>
<p>Phone: {session.phone}</p>
</div>
);
}useLoginFlow()
Manage login flow state (phone → OTP → success).
import { useLoginFlow } from '@zaplink/react';
function CustomSignIn() {
const { step, error, start, verify } = useLoginFlow({
phone: '',
onSuccess: (session) => console.log('Logged in!', session),
onError: (problem) => console.error('Error:', problem),
});
// step: 'idle' | 'starting' | 'otp' | 'verifying' | 'success' | 'error'
}Redirect URL Handling
The library follows Clerk's redirect pattern:
Priority System
forceRedirectUrl- Always redirect here (highest priority)redirect_urlquery param - From URL (e.g.,/login?redirect_url=/dashboard)fallbackRedirectUrl- Default fallback (defaults to/)
Example Flow
// 1. User visits protected page
<SignedOut>
<RedirectToSignIn /> // Redirects to /login?redirect_url=/dashboard
</SignedOut>
// 2. User signs in
<SignIn fallbackRedirectUrl="/" /> // After login → redirects to /dashboard
// 3. Or force specific redirect
<SignIn forceRedirectUrl="/onboarding" /> // Always goes to /onboardingCustomization
Labels & Localization
<SignIn
labels={{
phoneLabel: 'Número de teléfono',
phonePlaceholder: '55 1234 5678',
submitPhone: 'Continuar',
otpLabel: 'Código de verificación',
submitOtp: 'Verificar',
loading: 'Cargando...',
errorGeneric: 'Algo salió mal',
}}
/>Custom Styling
<SignIn
className="my-custom-login"
style={{
maxWidth: '500px',
margin: '0 auto',
}}
/>Custom Phone Input
<SignIn
renderPhoneInput={({ value, onChange }) => (
<MyCustomPhoneInput value={value} onChange={onChange} />
)}
/>TypeScript
Full TypeScript support:
import type {
SessionData,
SessionResult,
Problem,
SignInProps,
OTPFormProps,
} from '@zaplink/react';Examples
Complete App Example
// main.tsx
import { ZaplinkProvider } from '@zaplink/react';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<ZaplinkProvider publicKey="pk_xxx" signInUrl="/login">
<App />
</ZaplinkProvider>
);
// App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
</Routes>
</BrowserRouter>
);
}
// pages/HomePage.tsx
import { SignedIn, SignedOut, RedirectToSignIn } from '@zaplink/react';
function HomePage() {
return (
<>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
);
}
// pages/LoginPage.tsx
import { SignIn } from '@zaplink/react';
function LoginPage() {
return <SignIn fallbackRedirectUrl="/dashboard" />;
}Session Management
import { useSession, useZaplink } from '@zaplink/react';
function UserMenu() {
const { session } = useSession();
const { client } = useZaplink();
const handleLogout = async () => {
await client.clearSession();
window.location.href = '/';
};
return (
<div>
<p>Logged in as: {session?.phone}</p>
<button onClick={handleLogout}>Sign Out</button>
</div>
);
}License
MIT
