Skip to main content
The Gately SDK provides first-class React support with hooks, components, and a context provider.

Installation

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 with GatelyProvider:
// App.jsx
import { GatelyProvider } from '@gately/sdk'

function App() {
  return (
    <GatelyProvider projectId="YOUR_PROJECT_ID">
      <Router>
        <Routes />
      </Router>
    </GatelyProvider>
  )
}

With Options

<GatelyProvider 
  projectId="YOUR_PROJECT_ID"
  options={{
    autoRefresh: true,
    suppressConsoleErrors: process.env.NODE_ENV === 'production'
  }}
>
  <App />
</GatelyProvider>

Hooks

useGately

The main hook for authentication:
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:
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

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

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

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

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

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

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:
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

Wrap auth components in error boundaries to handle failures gracefully.
Always show loading indicators during auth operations.
Update UI optimistically for better perceived performance.
The SDK handles session persistence automatically via localStorage.

Need Help?