Installation
Copy
npm install @gately/sdk
The npm package is available but still in active development. Some features may be limited. Join our Slack community for updates and support.
Setup
Provider
Wrap your app withGatelyProvider:
Copy
// App.jsx
import { GatelyProvider } from '@gately/sdk'
function App() {
return (
<GatelyProvider projectId="YOUR_PROJECT_ID">
<Router>
<Routes />
</Router>
</GatelyProvider>
)
}
With Options
Copy
<GatelyProvider
projectId="YOUR_PROJECT_ID"
options={{
autoRefresh: true,
suppressConsoleErrors: process.env.NODE_ENV === 'production'
}}
>
<App />
</GatelyProvider>
Hooks
useGately
The main hook for authentication:Copy
import { useGately } from '@gately/sdk'
function Profile() {
const {
user,
session,
isAuthenticated,
isLoading,
login,
signup,
logout,
loginWithGoogle,
loginWithGithub
} = useGately()
if (isLoading) {
return <div>Loading...</div>
}
if (!isAuthenticated) {
return <LoginForm />
}
return (
<div>
<h1>Welcome, {user.full_name || user.email}</h1>
<button onClick={logout}>Log Out</button>
</div>
)
}
useAuth
Simplified auth hook:Copy
import { useAuth } from '@gately/sdk'
function AuthStatus() {
const { user, isAuthenticated, isLoading } = useAuth()
if (isLoading) return <Spinner />
if (!isAuthenticated) return <Link to="/login">Log In</Link>
return <span>Hello, {user.email}</span>
}
Components
Login Form
Copy
import { useState } from 'react'
import { useGately } from '@gately/sdk'
function LoginForm() {
const { login, loginWithGoogle, loginWithGithub } = useGately()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
setError('')
try {
await login(email, password)
// Redirect handled by SDK or manually
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Logging in...' : 'Log In'}
</button>
<div className="social-buttons">
<button type="button" onClick={() => loginWithGoogle()}>
Continue with Google
</button>
<button type="button" onClick={() => loginWithGithub()}>
Continue with GitHub
</button>
</div>
</form>
)
}
Signup Form
Copy
import { useState } from 'react'
import { useGately } from '@gately/sdk'
function SignupForm() {
const { signup } = useGately()
const [formData, setFormData] = useState({
email: '',
password: '',
fullName: ''
})
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
setError('')
try {
await signup(formData.email, formData.password, {
full_name: formData.fullName
})
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}
<input
type="text"
value={formData.fullName}
onChange={(e) => setFormData({ ...formData, fullName: e.target.value })}
placeholder="Full Name"
/>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
placeholder="Email"
required
/>
<input
type="password"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
placeholder="Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Creating account...' : 'Sign Up'}
</button>
</form>
)
}
Protected Routes
With React Router
Copy
import { Navigate, Outlet } from 'react-router-dom'
import { useAuth } from '@gately/sdk'
function ProtectedRoute() {
const { isAuthenticated, isLoading } = useAuth()
if (isLoading) {
return <LoadingSpinner />
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />
}
return <Outlet />
}
// Usage in routes
function AppRoutes() {
return (
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignupPage />} />
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>
)
}
Plan-Based Protection
Copy
function PlanProtectedRoute({ requiredPlan, children }) {
const { user, isAuthenticated, isLoading } = useAuth()
if (isLoading) {
return <LoadingSpinner />
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />
}
if (user.plan_id !== requiredPlan) {
return <Navigate to="/upgrade" replace />
}
return children
}
// Usage
<Route
path="/premium-content"
element={
<PlanProtectedRoute requiredPlan="pro">
<PremiumContent />
</PlanProtectedRoute>
}
/>
Form Embedding
Copy
import { FormEmbed } from '@gately/sdk'
function ContactPage() {
return (
<div>
<h1>Contact Us</h1>
<FormEmbed
formId="YOUR_FORM_ID"
onSubmit={(data) => {
console.log('Form submitted:', data)
}}
onError={(error) => {
console.error('Form error:', error)
}}
/>
</div>
)
}
User Profile Management
Copy
import { useState, useEffect } from 'react'
import { useGately } from '@gately/sdk'
function ProfileSettings() {
const { user, updateProfile } = useGately()
const [formData, setFormData] = useState({
fullName: '',
avatarUrl: ''
})
const [saving, setSaving] = useState(false)
useEffect(() => {
if (user) {
setFormData({
fullName: user.full_name || '',
avatarUrl: user.avatar_url || ''
})
}
}, [user])
const handleSave = async () => {
setSaving(true)
try {
await updateProfile({
full_name: formData.fullName,
avatar_url: formData.avatarUrl
})
alert('Profile updated!')
} catch (error) {
alert(error.message)
} finally {
setSaving(false)
}
}
return (
<div>
<h2>Profile Settings</h2>
<label>
Full Name
<input
type="text"
value={formData.fullName}
onChange={(e) => setFormData({ ...formData, fullName: e.target.value })}
/>
</label>
<label>
Avatar URL
<input
type="url"
value={formData.avatarUrl}
onChange={(e) => setFormData({ ...formData, avatarUrl: e.target.value })}
/>
</label>
<button onClick={handleSave} disabled={saving}>
{saving ? 'Saving...' : 'Save Changes'}
</button>
</div>
)
}
TypeScript
Full TypeScript support:Copy
import { useGately, User, Session } from '@gately/sdk'
interface ProfileProps {
onLogout: () => void
}
function Profile({ onLogout }: ProfileProps) {
const { user, logout } = useGately()
const handleLogout = async () => {
await logout()
onLogout()
}
if (!user) return null
return (
<div>
<h1>{user.full_name}</h1>
<p>{user.email}</p>
<button onClick={handleLogout}>Log Out</button>
</div>
)
}
Best Practices
Error Boundaries
Error Boundaries
Wrap auth components in error boundaries to handle failures gracefully.
Loading States
Loading States
Always show loading indicators during auth operations.
Optimistic Updates
Optimistic Updates
Update UI optimistically for better perceived performance.
Session Persistence
Session Persistence
The SDK handles session persistence automatically via localStorage.
