1- import { useState , useRef } from "react" ;
1+ import { useState , useRef , lazy , Suspense } from "react" ;
22import { useNavigate } from "react-router-dom" ;
33import { useTranslation } from "react-i18next" ;
44import { useAuth } from "@/contexts/AuthContext" ;
@@ -12,7 +12,15 @@ import { LanguageSwitcher } from "@/components/LanguageSwitcher";
1212import AnimatedBackground from "@/components/AnimatedBackground" ;
1313import { Link } from "react-router-dom" ;
1414import { ROUTES } from "@/routes" ;
15- import { Turnstile , type TurnstileInstance } from "@marsidev/react-turnstile" ;
15+
16+ // Lazy-load Turnstile — only fetched when VITE_TURNSTILE_SITE_KEY is set.
17+ // Self-hosted deployments without Turnstile pay zero bundle cost.
18+ const TURNSTILE_ENABLED = Boolean ( import . meta. env . VITE_TURNSTILE_SITE_KEY ) ;
19+ const LazyTurnstile = TURNSTILE_ENABLED
20+ ? lazy ( ( ) =>
21+ import ( "@marsidev/react-turnstile" ) . then ( ( m ) => ( { default : m . Turnstile } ) )
22+ )
23+ : null ;
1624
1725export default function Auth ( ) {
1826 const { t } = useTranslation ( ) ;
@@ -27,10 +35,10 @@ export default function Auth() {
2735 const [ error , setError ] = useState < string | null > ( null ) ;
2836 const [ success , setSuccess ] = useState < string | null > ( null ) ;
2937 const [ captchaToken , setCaptchaToken ] = useState < string | null > ( null ) ;
30- const turnstileRef = useRef < TurnstileInstance > ( null ) ;
38+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39+ const turnstileRef = useRef < any > ( null ) ;
3140 const { signIn, signUp, profile } = useAuth ( ) ;
3241 const navigate = useNavigate ( ) ;
33- const turnstileEnabled = Boolean ( import . meta. env . VITE_TURNSTILE_SITE_KEY ) ;
3442
3543 // Redirect if already logged in
3644 if ( profile ) {
@@ -50,7 +58,7 @@ export default function Auth() {
5058
5159 try {
5260 // Validate captcha token (only when Turnstile is enabled)
53- if ( turnstileEnabled && ! captchaToken ) {
61+ if ( TURNSTILE_ENABLED && ! captchaToken ) {
5462 setError ( t ( "auth.captchaRequired" ) ) ;
5563 setLoading ( false ) ;
5664 turnstileRef . current ?. reset ( ) ;
@@ -294,34 +302,36 @@ export default function Auth() {
294302 </ Alert >
295303 ) }
296304
297- { /* Cloudflare Turnstile Captcha - only rendered when site key is configured */ }
298- { turnstileEnabled && (
299- < div className = "flex justify-center" >
300- < Turnstile
301- ref = { turnstileRef }
302- siteKey = { import . meta. env . VITE_TURNSTILE_SITE_KEY ! }
303- onSuccess = { ( token ) => setCaptchaToken ( token ) }
304- onError = { ( ) => {
305- setError ( t ( "auth.captchaError" ) ) ;
306- setCaptchaToken ( null ) ;
307- } }
308- onExpire = { ( ) => {
309- setCaptchaToken ( null ) ;
310- turnstileRef . current ?. reset ( ) ;
311- } }
312- options = { {
313- theme : "dark" ,
314- size : "normal" ,
315- } }
316- />
317- </ div >
305+ { /* Cloudflare Turnstile Captcha — only loaded when VITE_TURNSTILE_SITE_KEY is set */ }
306+ { TURNSTILE_ENABLED && LazyTurnstile && (
307+ < Suspense fallback = { < div className = "flex justify-center py-2" > < Loader2 className = "h-5 w-5 animate-spin text-muted-foreground" /> </ div > } >
308+ < div className = "flex justify-center" >
309+ < LazyTurnstile
310+ ref = { turnstileRef }
311+ siteKey = { import . meta. env . VITE_TURNSTILE_SITE_KEY ! }
312+ onSuccess = { ( token : string ) => setCaptchaToken ( token ) }
313+ onError = { ( ) => {
314+ setError ( t ( "auth.captchaError" ) ) ;
315+ setCaptchaToken ( null ) ;
316+ } }
317+ onExpire = { ( ) => {
318+ setCaptchaToken ( null ) ;
319+ turnstileRef . current ?. reset ( ) ;
320+ } }
321+ options = { {
322+ theme : "dark" ,
323+ size : "normal" ,
324+ } }
325+ />
326+ </ div >
327+ </ Suspense >
318328 ) }
319329
320330 < div className = "pt-2" >
321331 < Button
322332 type = "submit"
323333 className = "w-full cta-button"
324- disabled = { loading || ( ! isLogin && ! termsAgreed ) || ( turnstileEnabled && ! captchaToken ) }
334+ disabled = { loading || ( ! isLogin && ! termsAgreed ) || ( TURNSTILE_ENABLED && ! captchaToken ) }
325335 >
326336 { loading && < Loader2 className = "mr-2 h-4 w-4 animate-spin" /> }
327337 { isLogin ? t ( "auth.signIn" ) : t ( "auth.signUp" ) }
0 commit comments