| name | splash-screen-animation |
| description | A polished animated splash/loading screen built with React and Tailwind CSS. Features animated gradient blobs, SVG line-drawing icon, pulsing ring, segmented progress bar, and smooth fade-out transition. |
| tags | ["react","tailwind","animation","splash-screen","loading-screen","ui"] |
Splash / Loading Screen Animation (React + Tailwind CSS)
A polished, animated splash/loading screen built with React and Tailwind CSS. It features:
- Animated gradient blobs in the background
- A minimalist icon with SVG line-drawing animation
- A pulsing ring around the icon
- Brand typography (name + tagline)
- A segmented progress bar with percentage counter
- Smooth fade-out transition when loading completes
File: SplashScreen.tsx
import { useEffect, useState } from 'react';
interface SplashScreenProps {
onComplete: () => void;
}
const SplashScreen = ({ onComplete }: SplashScreenProps) => {
const [progress, setProgress] = useState(0);
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
clearInterval(interval);
setIsLoaded(true);
setTimeout(onComplete, 800);
return 100;
}
return prev + 2;
});
}, 25);
return () => clearInterval(interval);
}, [onComplete]);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-white overflow-hidden">
{/* Subtle animated gradient orbs */}
<div className="absolute inset-0 opacity-40">
<div
className="absolute top-1/4 -left-1/4 w-96 h-96 bg-emerald-200 rounded-full mix-blend-multiply filter blur-3xl animate-blob"
/>
<div
className="absolute top-1/3 -right-1/4 w-96 h-96 bg-blue-200 rounded-full mix-blend-multiply filter blur-3xl animate-blob animation-delay-2000"
/>
<div
className="absolute -bottom-1/4 left-1/2 w-96 h-96 bg-purple-200 rounded-full mix-blend-multiply filter blur-3xl animate-blob animation-delay-4000"
/>
</div>
{/* Main content */}
<div className={`relative z-10 text-center transition-all duration-700 ${isLoaded ? 'opacity-0 scale-95' : 'opacity-100 scale-100'}`}>
{/* Minimalist icon */}
<div className="mb-12 relative">
<div className="inline-flex items-center justify-center w-20 h-20 rounded-2xl bg-gradient-to-br from-emerald-400 to-emerald-600 shadow-lg shadow-emerald-200/50">
<svg
className="w-12 h-12 text-white"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 2v20M2 12h20" className="animate-draw-line" />
<circle cx="12" cy="12" r="4" className="animate-draw-circle" />
</svg>
</div>
{/* Pulsing ring */}
<div className="absolute inset-0 rounded-2xl border-2 border-emerald-400 animate-ping-slow" />
</div>
{/* Typography */}
<h1 className="text-5xl font-light text-slate-900 mb-2 tracking-tight">
BrandName
</h1>
<p className="text-sm text-slate-500 mb-16 font-normal tracking-wide">
Smart Assistant
</p>
{/* Minimal progress indicator */}
<div className="w-48 mx-auto">
<div className="flex gap-1.5 mb-3">
{[...Array(8)].map((_, i) => (
<div
key={i}
className="flex-1 h-1 rounded-full bg-slate-200 overflow-hidden"
>
<div
className="h-full bg-gradient-to-r from-emerald-400 to-emerald-600 transition-all duration-300 ease-out"
style={{
width: `${Math.min(100, Math.max(0, (progress - i * 12.5) * 8))}%`,
}}
/>
</div>
))}
</div>
<p className="text-xs text-slate-400 font-medium">
{Math.round(progress)}%
</p>
</div>
</div>
<style>{`
@keyframes blob {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(30px, -50px) scale(1.1); }
66% { transform: translate(-20px, 20px) scale(0.9); }
}
@keyframes ping-slow {
0% { transform: scale(1); opacity: 1; }
75%, 100% { transform: scale(1.8); opacity: 0; }
}
@keyframes draw-line {
0% { stroke-dasharray: 0, 100; }
100% { stroke-dasharray: 100, 0; }
}
@keyframes draw-circle {
0% { stroke-dasharray: 0, 100; }
100% { stroke-dasharray: 100, 0; }
}
.animate-blob {
animation: blob 7s infinite;
}
.animation-delay-2000 {
animation-delay: 2s;
}
.animation-delay-4000 {
animation-delay: 4s;
}
.animate-ping-slow {
animation: ping-slow 2s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.animate-draw-line {
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: draw-line 2s ease-in-out infinite;
}
.animate-draw-circle {
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: draw-circle 2s ease-in-out infinite 0.5s;
}
`}</style>
</div>
);
};
export default SplashScreen;
How It Works — Step by Step
1. Progress Simulation
- A
setInterval fires every 25ms, incrementing progress by 2% each tick.
- At 100%, it sets
isLoaded = true, waits 800ms, then calls onComplete.
- Total simulated load time: ~2.5 seconds.
2. Background Gradient Blobs
- Three large, blurred circles (
w-96 h-96, blur-3xl) in emerald, blue, and purple.
- Positioned off-screen with negative offsets for a soft ambient glow.
- The
animate-blob keyframe makes each blob drift and scale organically (7s loop).
- Staggered delays (
0s, 2s, 4s) so they move independently.
3. Icon with SVG Line Drawing
- A gradient square (
emerald-400 → emerald-600) holds an SVG icon.
- Two SVG elements (a cross/plus and a circle) use
stroke-dasharray + stroke-dashoffset to create a "hand-drawn" animation effect.
- The lines animate from invisible to full over 2 seconds, looping infinitely.
4. Pulsing Ring
- A border ring around the icon uses
animate-ping-slow — it scales up to 1.8× while fading to 0 opacity, creating a radar-ping effect (2s loop).
5. Typography
- Brand name in large, light-weight text (
text-5xl font-light).
- Tagline in small, muted text below.
6. Segmented Progress Bar
- 8 small pill-shaped segments, each filled progressively based on the
progress state.
- Each segment's fill is calculated as:
(progress - i * 12.5) * 8, clipped to [0, 100].
- A percentage text shows the current value below the bar.
7. Fade-Out on Complete
- When
isLoaded becomes true, the main content container transitions to opacity-0 scale-95 over 700ms.
- The parent component listens via
onComplete and hides the splash screen (sets showSplash = false).
Integration (App.tsx)
import { useState } from 'react';
import SplashScreen from './components/SplashScreen';
function App() {
const [showSplash, setShowSplash] = useState(true);
const handleSplashComplete = () => {
setShowSplash(false);
};
if (showSplash) {
return <SplashScreen onComplete={handleSplashComplete} />;
}
}
Tailwind Dependencies
This component uses standard Tailwind utility classes plus inline <style> for custom keyframes. No extra Tailwind plugins are required.
Key Tailwind classes used:
fixed inset-0 z-50 — full-screen overlay
flex items-center justify-center — centered content
bg-white overflow-hidden — clean background
mix-blend-multiply filter blur-3xl — soft gradient blending
rounded-2xl shadow-lg — rounded, elevated card
transition-all duration-700 — smooth fade-out
Customization Tips
| What to change | How |
|---|
| Brand name / tagline | Edit the <h1> and <p> text inside the component |
| Color scheme | Swap emerald-400/600 for your brand colors (e.g., blue, violet) |
| Icon SVG | Replace the <svg> content with any icon you like |
| Load duration | Change prev + 2 / 25 or the setTimeout(onComplete, 800) value |
| Progress bar segments | Change Array(8) to a different number and adjust i * 12.5 accordingly |
| Blob colors | Change the bg-emerald-200, bg-blue-200, bg-purple-200 classes |
| Animation speed | Adjust the 7s in .animate-blob or 2s in ping-slow / draw-line |
Keyframes Reference
| Animation | Duration | Effect |
|---|
blob | 7s, infinite | Drift + scale organic blob movement |
ping-slow | 2s, infinite | Scale up + fade (radar ping) |
draw-line | 2s, infinite | SVG stroke draw-in effect |
draw-circle | 2s, infinite + 0.5s delay | SVG circle draw-in effect |