svelte-animations is a comprehensive collection of animation components for Svelte applications, built with Tailwind CSS and svelte-motion. It provides a rich set of pre-built animated components and utilities that enable developers to create sophisticated, performant animations with minimal effort. This guide walks through advanced animation techniques, custom animation patterns, and complex animation orchestration using svelte-animations with Svelte. This article is part 21 of a series on using svelte-animations with Svelte.
Prerequisites
Before you begin, make sure you have:
- Node.js version 18 or higher installed
- npm, pnpm, or yarn package manager
- SvelteKit project set up (or any Svelte project with build tooling)
- Tailwind CSS configured in your project
- A solid understanding of Svelte reactivity, components, and lifecycle hooks
- Familiarity with CSS animations and transitions
- Basic knowledge of svelte-motion (the underlying animation library)
For this advanced tutorial, we'll work with complex animation patterns including:
- Animation orchestration: Coordinating multiple animations together
- Custom animation variants: Creating reusable animation patterns
- Advanced transitions: Complex enter/exit animations with AnimatePresence
- Performance optimization: Optimizing animations for smooth performance
- SVG animations: Animating SVG properties with svelte-motion
Installation
Install the svelte-animations package using npm:
npm install @svelte-animations/aceternity-ui
Or with pnpm:
pnpm add @svelte-animations/aceternity-ui
Or with yarn:
yarn add @svelte-animations/aceternity-ui
You'll also need to install svelte-motion as a peer dependency:
npm install svelte-motion
After installation, your package.json should include:
{
"dependencies": {
"@svelte-animations/aceternity-ui": "^1.0.0",
"svelte-motion": "^0.10.0"
}
}
Project Setup
Ensure Tailwind CSS is properly configured in your project. If you're using SvelteKit, add the svelte-animations path to your tailwind.config.js:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./src/**/*.{html,js,svelte,ts}",
"./node_modules/@svelte-animations/**/*.{js,svelte}"
],
theme: {
extend: {
// Add custom animations if needed
animation: {
'aurora': 'aurora 60s linear infinite',
},
keyframes: {
aurora: {
'0%': { backgroundPosition: '50% 50%, 50% 50%' },
'100%': { backgroundPosition: '350% 50%, 350% 50%' },
},
},
},
},
plugins: [],
}
Create a utility file for component imports (optional but recommended):
// src/lib/animations/index.ts
export { default as AnimatedTestimonials } from '@svelte-animations/aceternity-ui/components/animated-testimonials/AnimatedTestimonials.svelte';
export { default as BackgroundLines } from '@svelte-animations/aceternity-ui/components/background-lines/BackgroundLines.svelte';
export { default as AuroraBackground } from '@svelte-animations/aceternity-ui/components/aurora-background/AuroraBackground.svelte';
// Export other components as needed
First Example / Basic Usage
Let's start with a simple example that demonstrates the basic usage of svelte-motion (the core animation library):
<!-- src/components/SimpleAnimation.svelte -->
<script>
import { Motion } from "svelte-motion";
let isVisible = true;
</script>
<div class="p-8">
<button
on:click={() => isVisible = !isVisible}
class="px-4 py-2 bg-blue-500 text-white rounded mb-4"
>
Toggle Animation
</button>
{#if isVisible}
<Motion
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.3, ease: "easeOut" }}
let:motion
>
<div
use:motion
class="p-4 bg-gray-100 rounded-lg shadow-md"
>
<h2 class="text-xl font-bold">Animated Content</h2>
<p>This content animates in and out smoothly.</p>
</div>
</Motion>
{/if}
</div>
This example demonstrates:
- Motion component: The core animation component from svelte-motion
-
Initial state:
initialprop defines the starting animation state -
Animate state:
animateprop defines the target animation state -
Exit state:
exitprop defines how the element animates out - Transition: Controls timing, easing, and duration
- use:motion: The action directive that applies the animation to the element
Understanding the Basics
How svelte-animations Integrates with Svelte
svelte-animations is built on top of svelte-motion, which provides a declarative API for animations in Svelte:
- Motion Component: Wraps elements to enable animations
- AnimatePresence: Manages enter/exit animations for conditional rendering
- Variants: Reusable animation state objects
- Reactivity: Animations automatically update when reactive values change
- Performance: Uses CSS transforms and opacity for hardware-accelerated animations
Animation Variants Pattern
Variants allow you to define reusable animation states:
<script>
import { Motion } from "svelte-motion";
const fadeInUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -20 }
};
const scaleIn = {
initial: { opacity: 0, scale: 0.8 },
animate: { opacity: 1, scale: 1 },
exit: { opacity: 0, scale: 0.8 }
};
let animationType = 'fadeInUp';
</script>
<Motion
variants={animationType === 'fadeInUp' ? fadeInUp : scaleIn}
initial="initial"
animate="animate"
exit="exit"
transition={{ duration: 0.4 }}
let:motion
>
<div use:motion class="p-6 bg-blue-100 rounded">
Content with {animationType} animation
</div>
</Motion>
AnimatePresence for Enter/Exit Animations
AnimatePresence manages animations when elements are added or removed from the DOM:
<script>
import { Motion, AnimatePresence } from "svelte-motion";
let items = [
{ id: 1, text: "Item 1" },
{ id: 2, text: "Item 2" },
{ id: 3, text: "Item 3" }
];
function removeItem(id) {
items = items.filter(item => item.id !== id);
}
</script>
<div class="space-y-2">
<AnimatePresence let:item list={items}>
{#each items as item (item.id)}
<Motion
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
transition={{ duration: 0.3 }}
let:motion
>
<div
use:motion
class="flex items-center justify-between p-4 bg-gray-100 rounded"
>
<span>{item.text}</span>
<button
on:click={() => removeItem(item.id)}
class="px-3 py-1 bg-red-500 text-white rounded"
>
Remove
</button>
</div>
</Motion>
{/each}
</AnimatePresence>
</div>
Practical Example / Building Something Real
Let's build a sophisticated animated dashboard with multiple coordinated animations:
<!-- src/components/AnimatedDashboard.svelte -->
<script>
import { Motion, AnimatePresence } from "svelte-motion";
import { onMount } from "svelte";
let activeCard = 0;
let cards = [
{ id: 1, title: "Sales", value: "$12,450", change: "+12%", color: "bg-blue-500" },
{ id: 2, title: "Users", value: "1,234", change: "+8%", color: "bg-green-500" },
{ id: 3, title: "Revenue", value: "$45,678", change: "+15%", color: "bg-purple-500" },
{ id: 4, title: "Orders", value: "567", change: "+5%", color: "bg-orange-500" }
];
let notifications = [];
let notificationId = 0;
// Stagger animation variants
const cardVariants = {
initial: { opacity: 0, y: 20, scale: 0.95 },
animate: (index) => ({
opacity: 1,
y: 0,
scale: 1,
transition: {
delay: index * 0.1,
duration: 0.4,
ease: "easeOut"
}
})
};
// Notification animation
const notificationVariants = {
initial: { opacity: 0, x: 300, scale: 0.8 },
animate: {
opacity: 1,
x: 0,
scale: 1,
transition: { type: "spring", stiffness: 300, damping: 30 }
},
exit: {
opacity: 0,
x: 300,
scale: 0.8,
transition: { duration: 0.2 }
}
};
function addNotification(message) {
const id = notificationId++;
notifications = [...notifications, { id, message, timestamp: Date.now() }];
// Auto-remove after 3 seconds
setTimeout(() => {
notifications = notifications.filter(n => n.id !== id);
}, 3000);
}
function nextCard() {
activeCard = (activeCard + 1) % cards.length;
addNotification(`Switched to ${cards[activeCard].title}`);
}
onMount(() => {
// Simulate periodic updates
const interval = setInterval(() => {
nextCard();
}, 5000);
return () => clearInterval(interval);
});
</script>
<div class="min-h-screen bg-gray-50 p-8">
<div class="max-w-7xl mx-auto">
<!-- Header with animated title -->
<Motion
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
let:motion
>
<h1
use:motion
class="text-4xl font-bold mb-8 text-gray-900"
>
Animated Dashboard
</h1>
</Motion>
<!-- Cards grid with stagger animation -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{#each cards as card, index}
<Motion
variants={cardVariants}
initial="initial"
animate="animate"
custom={index}
let:motion
>
<div
use:motion
class="p-6 bg-white rounded-lg shadow-lg hover:shadow-xl transition-shadow cursor-pointer {card.color}"
on:click={() => { activeCard = index; addNotification(`Selected ${card.title}`); }}
role="button"
tabindex="0"
>
<h3 class="text-white text-sm font-medium mb-2">{card.title}</h3>
<p class="text-white text-3xl font-bold mb-1">{card.value}</p>
<p class="text-white text-sm opacity-90">{card.change}</p>
</div>
</Motion>
{/each}
</div>
<!-- Active card detail with complex animation -->
<AnimatePresence mode="wait">
{#key activeCard}
<Motion
key={`card-${activeCard}`}
initial={{ opacity: 0, rotateX: -90 }}
animate={{ opacity: 1, rotateX: 0 }}
exit={{ opacity: 0, rotateX: 90 }}
transition={{
duration: 0.5,
ease: "easeInOut"
}}
let:motion
>
<div
use:motion
class="p-8 bg-white rounded-lg shadow-lg mb-8"
style="transform-style: preserve-3d;"
>
<h2 class="text-2xl font-bold mb-4">{cards[activeCard].title} Details</h2>
<p class="text-gray-600">Current value: <span class="font-bold">{cards[activeCard].value}</span></p>
<p class="text-gray-600 mt-2">Change: <span class="font-bold text-green-600">{cards[activeCard].change}</span></p>
</div>
</Motion>
{/key}
</AnimatePresence>
<!-- Notification stack -->
<div class="fixed top-4 right-4 z-50 space-y-2">
<AnimatePresence let:item list={notifications}>
{#each notifications as notification (notification.id)}
<Motion
variants={notificationVariants}
initial="initial"
animate="animate"
exit="exit"
let:motion
>
<div
use:motion
class="p-4 bg-white rounded-lg shadow-xl border-l-4 border-blue-500 min-w-[300px]"
>
<p class="text-sm font-medium text-gray-900">{notification.message}</p>
</div>
</Motion>
{/each}
</AnimatePresence>
</div>
<!-- Control button -->
<Motion
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
let:motion
>
<button
use:motion
on:click={nextCard}
class="px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
Next Card
</button>
</Motion>
</div>
</div>
This example demonstrates:
- Staggered animations: Cards animate in sequence with delays
-
3D transforms: Using
rotateXfor card flip effect - Spring animations: Natural, physics-based motion for notifications
- Coordinated animations: Multiple elements animating together
-
Interactive animations: Hover and tap states with
whileHoverandwhileTap - AnimatePresence: Managing enter/exit animations for dynamic lists
- Performance: Using transform properties for hardware acceleration
Advanced Animation Patterns
SVG Path Animation
Animating SVG paths for drawing effects:
<script>
import { Motion } from "svelte-motion";
const pathData = "M 10 80 Q 95 10 180 80 T 350 80";
const pathVariants = {
initial: {
pathLength: 0,
opacity: 0,
strokeDashoffset: 1000
},
animate: {
pathLength: 1,
opacity: 1,
strokeDashoffset: 0,
transition: {
duration: 2,
ease: "easeInOut"
}
}
};
</script>
<svg width="400" height="200" class="border border-gray-300">
<Motion
variants={pathVariants}
initial="initial"
animate="animate"
let:motion
isSVG={true}
>
<path
use:motion
d={pathData}
fill="none"
stroke="blue"
stroke-width="3"
stroke-linecap="round"
/>
</Motion>
</svg>
Scroll-Triggered Animations
Animations that trigger based on scroll position:
<script>
import { Motion } from "svelte-motion";
import { onMount } from "svelte";
let element;
let isVisible = false;
onMount(() => {
const observer = new IntersectionObserver(
(entries) => {
isVisible = entries[0].isIntersecting;
},
{ threshold: 0.1 }
);
if (element) {
observer.observe(element);
}
return () => {
if (element) observer.unobserve(element);
};
});
</script>
<div bind:this={element}>
<Motion
initial={{ opacity: 0, y: 50 }}
animate={isVisible ? { opacity: 1, y: 0 } : { opacity: 0, y: 50 }}
transition={{ duration: 0.6, ease: "easeOut" }}
let:motion
>
<div
use:motion
class="p-8 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-lg"
>
<h2 class="text-2xl font-bold">Scroll to Animate</h2>
<p>This content animates when it enters the viewport.</p>
</div>
</Motion>
</div>
Complex Sequence Animations
Orchestrating multiple animations in sequence:
<script>
import { Motion, AnimatePresence } from "svelte-motion";
import { onMount } from "svelte";
let step = 0;
const steps = ['Step 1', 'Step 2', 'Step 3', 'Complete'];
onMount(() => {
const interval = setInterval(() => {
step = (step + 1) % steps.length;
}, 2000);
return () => clearInterval(interval);
});
</script>
<div class="p-8">
<AnimatePresence mode="wait">
{#key step}
<Motion
key={`step-${step}`}
initial={{ opacity: 0, x: 50, rotate: -10 }}
animate={{ opacity: 1, x: 0, rotate: 0 }}
exit={{ opacity: 0, x: -50, rotate: 10 }}
transition={{
duration: 0.5,
ease: "easeInOut"
}}
let:motion
>
<div
use:motion
class="p-6 bg-blue-500 text-white rounded-lg text-center"
>
<h3 class="text-2xl font-bold">{steps[step]}</h3>
</div>
</Motion>
{/key}
</AnimatePresence>
</div>
Common Issues / Troubleshooting
Issue 1: Animations Not Playing
Problem: Motion components render but animations don't execute.
Solution: Ensure you're using the use:motion directive on the target element:
<!-- ❌ Wrong - missing use:motion -->
<Motion animate={{ opacity: 1 }} let:motion>
<div>Content</div>
</Motion>
<!-- ✅ Correct - using use:motion -->
<Motion animate={{ opacity: 1 }} let:motion>
<div use:motion>Content</div>
</Motion>
Issue 2: Exit Animations Not Working
Problem: Elements disappear instantly without exit animation.
Solution: Wrap conditional rendering with AnimatePresence:
<!-- ❌ Wrong - no AnimatePresence -->
{#if visible}
<Motion exit={{ opacity: 0 }} let:motion>
<div use:motion>Content</div>
</Motion>
{/if}
<!-- ✅ Correct - with AnimatePresence -->
<AnimatePresence>
{#if visible}
<Motion exit={{ opacity: 0 }} let:motion>
<div use:motion>Content</div>
</Motion>
{/if}
</AnimatePresence>
Issue 3: SVG Animations Not Working
Problem: SVG properties don't animate correctly.
Solution: Use isSVG={true} prop on Motion component for SVG elements:
<!-- ✅ Correct - isSVG prop for SVG elements -->
<Motion animate={{ pathLength: 1 }} isSVG={true} let:motion>
<path use:motion d="M10 10 L100 100" />
</Motion>
Issue 4: Performance Issues with Many Animations
Problem: Animations are janky or cause performance problems.
Solution:
- Use
transformandopacityproperties (hardware-accelerated) - Avoid animating
width,height,top,leftdirectly - Use
will-changeCSS property for elements that will animate - Debounce rapid state changes
<!-- ✅ Good - animating transform -->
<Motion animate={{ x: 100, scale: 1.2 }} let:motion>
<div use:motion>Content</div>
</Motion>
<!-- ❌ Avoid - animating layout properties -->
<Motion animate={{ width: 200, height: 200 }} let:motion>
<div use:motion>Content</div>
</Motion>
Next Steps
Now that you've learned advanced animation techniques with svelte-animations, here are some directions to continue learning:
- Custom Animation Hooks: Create reusable animation utilities and composables
- Gesture Animations: Integrate with gesture libraries for drag, pinch, and swipe animations
- Layout Animations: Animate layout changes using FLIP technique
- Physics-Based Animations: Explore spring and physics-based motion
- Animation Performance: Learn about optimizing animations for 60fps
-
Accessibility: Ensure animations respect
prefers-reduced-motion - Animation Testing: Learn how to test animated components
- Official Documentation: Visit the svelte-animations repository and svelte-motion documentation for complete API reference
Continue with other articles in this series to learn about specific components, animation patterns, and integration techniques.
Summary
You've learned how to implement advanced animation techniques with svelte-animations in Svelte, including animation orchestration, custom variants, complex transitions, SVG animations, and performance optimization. You should now be able to create sophisticated, performant animations that enhance user experience while maintaining smooth 60fps performance.

Top comments (0)