React Integration
Because <spartan-login> is a native web component, it requires explicit lifecycle management in React — useEffect for attaching event listeners and key-based remounting for attribute changes.
Minimal integration
Section titled “Minimal integration”import '@masonitestudios/spartanauth-widgets';import { useRef, useEffect, useState } from 'react';import { useNavigate } from 'react-router-dom';
// Extend JSX types so TypeScript recognises the custom elementdeclare global { namespace JSX { interface IntrinsicElements { 'spartan-login': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & { domain?: string; sector?: string; 'start-mode'?: string; locale?: string; redirect?: string; styles?: string; }; } }}
export function LoginPage() { const navigate = useNavigate(); const widgetRef = useRef<HTMLElement>(null); const [locale] = useState(() => navigator.language.slice(0, 2) || 'en'); const [widgetKey, setWidgetKey] = useState(0);
useEffect(() => { const el = widgetRef.current; if (!el) return;
function handleLogin() { navigate('/app'); }
el.addEventListener('spartan-login', handleLogin); return () => { el.removeEventListener('spartan-login', handleLogin); }; // Re-run this effect when widgetKey changes (remount scenario) }, [widgetKey, navigate]);
return ( <spartan-login key={widgetKey} ref={widgetRef} domain={import.meta.env.VITE_SPARTANAUTH_DOMAIN || 'https://api.spartanauth.com'} sector={import.meta.env.VITE_SPARTANAUTH_SECTOR} start-mode="password" locale={locale} redirect=""> </spartan-login> );}Key rules for React
Section titled “Key rules for React”1. Attach event listeners in useEffect
Section titled “1. Attach event listeners in useEffect”The web component element doesn’t exist before the component mounts. useEffect with widgetRef.current runs after the DOM is ready.
2. Always return a cleanup function
Section titled “2. Always return a cleanup function”The useEffect return function removes the event listener when the component unmounts (or when the effect re-runs). Without it, listeners accumulate across re-renders.
return () => { el.removeEventListener('spartan-login', handleLogin);};3. Use key to force remount on attribute changes
Section titled “3. Use key to force remount on attribute changes”React, like Vue, reuses DOM nodes. If you need the widget to reinitialize (e.g., when locale changes), increment the key prop to force React to destroy and recreate the element:
const [widgetKey, setWidgetKey] = useState(0);
// Force remount when locale changesfunction changeLocale(newLocale: string) { setLocale(newLocale); setWidgetKey(k => k + 1);}4. TypeScript JSX types
Section titled “4. TypeScript JSX types”React doesn’t know about custom elements by default. Extend JSX.IntrinsicElements (shown in the example above) to get proper TypeScript support.
Common mistakes
Section titled “Common mistakes”| Mistake | Fix |
|---|---|
Using document.getElementById instead of useRef | Refs are the idiomatic React way to access DOM elements |
Missing useEffect cleanup | Always return a cleanup function that calls removeEventListener |
| Referencing stale closures in event handlers | Include all dependencies in the useEffect dependency array |
| Not providing TypeScript type declarations | Extend JSX.IntrinsicElements with the custom element’s attributes |