The Loader component displays a full-screen loading screen with the Music Store branding and a spinning animation. It automatically fades out after a delay and triggers a callback when complete.
Overview
Key features:
Full-screen overlay with black background
Large “MUSIC STORE” title in Bebas Neue font
Spinning loader animation
GSAP-powered fade-out transition
Callback support for post-load actions
High z-index for top-layer rendering
Props
Callback function triggered when the loader fade-out animation completes
Implementation
import { useEffect , useRef } from "react" ;
import gsap from "gsap" ;
function Loader ({ onComplete }) {
const loaderRef = useRef ( null );
useEffect (() => {
const tl = gsap . timeline ();
tl . to ( loaderRef . current , {
opacity: 0 ,
duration: 0.8 ,
delay: 1.2 ,
ease: "power2.out" ,
onComplete
});
}, []);
return (
< div
ref = { loaderRef }
className = "fixed inset-0 z-[9999] bg-black flex flex-col items-center justify-center gap-4"
>
< p
className = "text-white text-5xl font-bold"
style = { {
fontFamily: "'Bebas Neue', sans-serif" ,
letterSpacing: "0.1em"
} }
>
MUSIC STORE
</ p >
< div className = "w-8 h-8 border-2 border-white/20 border-t-white rounded-full animate-spin" />
</ div >
);
}
export default Loader ;
Usage
Basic Usage
import { useState } from 'react' ;
import Loader from './components/Loader/Loader' ;
function App () {
const [ loading , setLoading ] = useState ( true );
return (
<>
{ loading && < Loader onComplete = { () => setLoading ( false ) } /> }
{ ! loading && (
< div >
{ /* Your app content */ }
</ div >
) }
</>
);
}
With Additional Actions
import { useState } from 'react' ;
import Loader from './components/Loader/Loader' ;
function App () {
const [ loading , setLoading ] = useState ( true );
const handleLoadComplete = () => {
// Perform additional actions
console . log ( 'Loading complete' );
localStorage . setItem ( 'hasSeenLoader' , 'true' );
// Hide loader
setLoading ( false );
};
return (
<>
{ loading && < Loader onComplete = { handleLoadComplete } /> }
{ ! loading && < YourApp /> }
</>
);
}
Conditional Loading
import { useState , useEffect } from 'react' ;
import Loader from './components/Loader/Loader' ;
function App () {
const [ loading , setLoading ] = useState ( true );
const [ dataReady , setDataReady ] = useState ( false );
useEffect (() => {
// Fetch initial data
fetchData (). then (() => setDataReady ( true ));
}, []);
const handleLoadComplete = () => {
if ( dataReady ) {
setLoading ( false );
}
};
return (
<>
{ loading && < Loader onComplete = { handleLoadComplete } /> }
{ ! loading && < YourApp /> }
</>
);
}
Animation Timeline
The loader uses a simple GSAP timeline:
gsap . timeline (). to ( loaderRef . current , {
opacity: 0 , // Fade to transparent
duration: 0.8 , // 800ms fade duration
delay: 1.2 , // Wait 1200ms before starting fade
ease: "power2.out" , // Ease out for smooth deceleration
onComplete // Trigger callback when done
});
Total visible time : 2 seconds (1.2s delay + 0.8s fade)
Styling Breakdown
Container
className = "fixed inset-0 z-[9999] bg-black flex flex-col items-center justify-center gap-4"
fixed inset-0: Full-screen fixed positioning
z-[9999]: Very high z-index to overlay everything
bg-black: Solid black background
flex flex-col: Vertical flexbox layout
items-center justify-center: Center content
gap-4: 1rem gap between title and spinner
Title
className = "text-white text-5xl font-bold"
style = {{
fontFamily : "'Bebas Neue', sans-serif" ,
letterSpacing : "0.1em"
}}
Large (3rem) white text
Bebas Neue font for brand consistency
Wide letter spacing (0.1em) for dramatic effect
Spinner
className = "w-8 h-8 border-2 border-white/20 border-t-white rounded-full animate-spin"
8x8 (2rem) circular element
2px border with 20% white opacity
Top border fully white for contrast
animate-spin: Tailwind’s built-in spin animation
Spinner Animation
The spinner uses Tailwind’s animate-spin utility, which applies:
@keyframes spin {
from {
transform : rotate ( 0 deg );
}
to {
transform : rotate ( 360 deg );
}
}
.animate-spin {
animation : spin 1 s linear infinite ;
}
Font Requirements
The component requires the Bebas Neue font. Include it in your HTML:
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
< link href = "https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap" rel = "stylesheet" >
Or in your CSS:
@import url ( 'https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap' );
Customization
Change Duration
function Loader ({ onComplete }) {
useEffect (() => {
gsap . timeline (). to ( loaderRef . current , {
opacity: 0 ,
duration: 1.5 , // Slower fade
delay: 2.0 , // Longer display time
ease: "power2.out" ,
onComplete
});
}, []);
// ...
}
Change Easing
ease : "expo.out" // More dramatic
ease : "elastic.out" // Bouncy
ease : "back.out" // Slight overshoot
Change Text
< p className = "text-white text-5xl font-bold" >
YOUR BRAND NAME
</ p >
Change Colors
// Container
className = "... bg-gradient-to-br from-purple-900 to-black ..."
// Spinner
className = "... border-purple-500/20 border-t-purple-500 ..."
Best Practices
State Management Always use state to conditionally render the loader and hide it after completion
Callback Required Always provide an onComplete callback to hide the loader
Data Loading Combine loader visibility with actual data fetching completion
Accessibility Consider adding aria-label="Loading" and role="status" for screen readers
Accessibility Enhancement
For better accessibility:
< div
ref = { loaderRef }
className = "..."
role = "status"
aria-label = "Loading Music Store"
>
< p className = "..." aria-hidden = "true" >
MUSIC STORE
</ p >
< div className = "..." aria-hidden = "true" />
< span className = "sr-only" > Loading, please wait... </ span >
</ div >
The loader should only be displayed once per session. Consider using localStorage to track if the user has already seen it.