Custom Login UI
When your app needs its own branded login experience, you can build custom forms and wire them into the SDK through tribe.login() and tribe.register(). You get complete control over the interface while Tribe handles session management, token storage, and security checks like breached password detection under the hood.
import { Tribe } from "@tribecloud/sdk";import { useState } from "react";
const tribe = new Tribe({ siteId: "YOUR_SITE_ID" });
function LoginForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState("");
const handleSubmit = async (e) => { e.preventDefault(); try { const { user } = await tribe.login(email, password); console.log("Logged in:", user); } catch (err) { setError(err.message); } };
return ( <form onSubmit={handleSubmit}> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> {error && <p style={{ color: "red" }}>{error}</p>} <button type="submit">Sign In</button> </form> );}<form id="login-form"> <input type="email" id="email" placeholder="Email" /> <input type="password" id="password" placeholder="Password" /> <p id="error" style="color: red; display: none"></p> <button type="submit">Sign In</button></form>
<script src="https://api.tribe.utopian.build/sdk.js?site=YOUR_SITE_ID" defer></script><script defer> document.getElementById("login-form").addEventListener("submit", async (e) => { e.preventDefault(); try { const { user } = await Tribe.login( document.getElementById("email").value, document.getElementById("password").value ); console.log("Logged in:", user); } catch (err) { const errorEl = document.getElementById("error"); errorEl.textContent = err.message; errorEl.style.display = "block"; } });</script>Registration
Section titled “Registration”const { user } = await tribe.register(email, password);Registration behaves just like login from your code’s perspective. The session and user tokens are stored automatically, and the user is signed in the moment their account is created.
Checking the session
Section titled “Checking the session”Before rendering a login form, it’s worth checking whether the user already has a valid session. This avoids showing a login page to someone who’s already signed in:
const session = await tribe.getSession();if (session) { // User is already logged in console.log("Welcome back:", session.user);} else { // Show login form}Logging out
Section titled “Logging out”await tribe.logout();// Session token and user token are cleared from localStorageAuth context pattern (React)
Section titled “Auth context pattern (React)”A common pattern in React apps is centralizing auth state in a context provider. This makes the current user and auth actions available anywhere in your component tree without prop drilling:
import { Tribe } from "@tribecloud/sdk";import { createContext, useContext, useEffect, useState } from "react";
const tribe = new Tribe({ siteId: "YOUR_SITE_ID" });const AuthContext = createContext(null);
export function AuthProvider({ children }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => { tribe.getSession().then((session) => { setUser(session?.user ?? null); setLoading(false); }); }, []);
const login = async (email, password) => { const { user } = await tribe.login(email, password); setUser(user); };
const register = async (email, password) => { const { user } = await tribe.register(email, password); setUser(user); };
const logout = async () => { await tribe.logout(); setUser(null); };
return ( <AuthContext.Provider value={{ user, loading, login, register, logout }}> {children} </AuthContext.Provider> );}
export const useAuth = () => { const ctx = useContext(AuthContext); if (!ctx) throw new Error("useAuth must be used within AuthProvider"); return ctx;};Email verification
Section titled “Email verification”After a user registers, Tribe sends them a verification email automatically. You just need a page that handles the token when they click the link:
// On your /verify-email?token=xxx pageconst token = new URLSearchParams(window.location.search).get("token");await tribe.verifyEmail(token);If the user needs the email resent, you can trigger that programmatically:
await tribe.resendVerification();// Optionally override the verify URL:await tribe.resendVerification({ verifyEmailUrl: "https://myapp.com/verify-email" });Password reset
Section titled “Password reset”To kick off a password reset, send the user a reset email. Tribe delivers it; you just provide the address:
await tribe.forgotPassword(email);// Optionally override the reset URL:await tribe.forgotPassword(email, { resetPasswordUrl: "https://myapp.com/reset-password" });Then on your reset page, pull the token from the URL and set the new password:
// On your /reset-password?token=xxx pageconst token = new URLSearchParams(window.location.search).get("token");await tribe.resetPassword(token, newPassword);Access mode
Section titled “Access mode”Sites can operate in either "public" or "internal" access mode. Querying the auth config lets you adapt your UI accordingly, for example hiding the registration form on internal sites:
const config = await tribe.getAuthConfig();if (config.accessMode === "internal") { // Only organization members can register/login // Consider showing "Contact your admin" instead of a register form}