This guide will walk you through setting up Motion Icons React in your Next.js application, whether you’re using the App Router or Pages Router.
Prerequisites
Node.js 14 or higher
Next.js 13+ (for App Router) or Next.js 12+ (for Pages Router)
Basic familiarity with Next.js
Installation
First, install the required packages:
npm install motion-icons-react@latest lucide-react
Setup for App Router (Next.js 13+)
The App Router is the recommended approach for new Next.js applications.
Step 1: Use in Client Components
Motion Icons React requires client-side JavaScript, so use the 'use client' directive and import the CSS:
'use client' ;
import { MotionIcon } from 'motion-icons-react' ;
import 'motion-icons-react/style.css' ;
export default function Home () {
return (
< div className = "flex items-center justify-center min-h-screen" >
< div className = "text-center space-y-8" >
< h1 className = "text-4xl font-bold" > Welcome! </ h1 >
{ /* Animated heart icon */ }
< MotionIcon
name = "Heart"
animation = "heartbeat"
size = { 80 }
color = "red"
/>
{ /* Loading spinner */ }
< MotionIcon
name = "Loader2"
animation = "spin"
size = { 32 }
className = "text-blue-500"
/>
</ div >
</ div >
);
}
Important: Import the CSS file (import 'motion-icons-react/style.css';) in any component that uses Motion Icons. Next.js will automatically handle the CSS bundling.
Step 2: Create Reusable Components
For better organization, create reusable icon components:
components/AnimatedIcons.tsx
'use client' ;
import { MotionIcon } from 'motion-icons-react' ;
import 'motion-icons-react/style.css' ;
export function LoadingSpinner () {
return (
< MotionIcon
name = "Loader2"
animation = "spin"
size = { 20 }
className = "text-gray-500"
/>
);
}
export function LikeButton ({ isLiked , onClick } : { isLiked : boolean ; onClick : () => void }) {
return (
< button onClick = { onClick } className = "p-2 rounded-full hover:bg-gray-100" >
< MotionIcon
name = "Heart"
animation = { isLiked ? "heartbeat" : "none" }
size = { 24 }
color = { isLiked ? "red" : "gray" }
interactive
/>
</ button >
);
}
Then use them in any component:
'use client' ;
import { useState } from 'react' ;
import { LoadingSpinner , LikeButton } from '@/components/AnimatedIcons' ;
export default function Home () {
const [ isLiked , setIsLiked ] = useState ( false );
return (
< div >
< LoadingSpinner />
< LikeButton isLiked = { isLiked } onClick = { () => setIsLiked ( ! isLiked ) } />
</ div >
);
}
Setup for Pages Router (Next.js 12+)
If you’re using the Pages Router, follow these steps:
Step 1: Use in Pages
Import the CSS and use Motion Icons in your pages:
import { MotionIcon } from 'motion-icons-react' ;
import 'motion-icons-react/style.css' ;
export default function Home () {
return (
< div className = "flex items-center justify-center min-h-screen" >
< div className = "text-center space-y-8" >
< h1 className = "text-4xl font-bold" > Welcome! </ h1 >
< MotionIcon
name = "Heart"
animation = "heartbeat"
size = { 80 }
color = "red"
/>
</ div >
</ div >
);
}
Common Use Cases
Loading States
'use client' ;
import { useState } from 'react' ;
import { MotionIcon } from 'motion-icons-react' ;
export default function DataFetcher () {
const [ isLoading , setIsLoading ] = useState ( false );
const [ data , setData ] = useState ( null );
const fetchData = async () => {
setIsLoading ( true );
const response = await fetch ( '/api/data' );
const result = await response . json ();
setData ( result );
setIsLoading ( false );
};
return (
< div >
< button onClick = { fetchData } disabled = { isLoading } >
{ isLoading ? (
<>
< MotionIcon name = "Loader2" animation = "spin" size = { 16 } />
Loading...
</>
) : (
'Fetch Data'
) }
</ button >
{ data && < pre > { JSON . stringify ( data , null , 2 ) } </ pre > }
</ div >
);
}
Interactive Navigation
'use client' ;
import Link from 'next/link' ;
import { usePathname } from 'next/navigation' ;
import { MotionIcon } from 'motion-icons-react' ;
export function Navigation () {
const pathname = usePathname ();
const links = [
{ href: '/' , icon: 'Home' , label: 'Home' },
{ href: '/about' , icon: 'Info' , label: 'About' },
{ href: '/contact' , icon: 'Mail' , label: 'Contact' },
];
return (
< nav className = "flex gap-4" >
{ links . map (( link ) => {
const isActive = pathname === link . href ;
return (
< Link
key = { link . href }
href = { link . href }
className = "flex items-center gap-2 p-2 rounded-lg hover:bg-gray-100"
>
< MotionIcon
name = { link . icon }
animation = { isActive ? "bounce" : "none" }
trigger = "hover"
size = { 20 }
className = { isActive ? "text-blue-500" : "text-gray-600" }
/>
< span > { link . label } </ span >
</ Link >
);
}) }
</ nav >
);
}
'use client' ;
import { useState } from 'react' ;
import { MotionIcon } from 'motion-icons-react' ;
export function EmailForm () {
const [ email , setEmail ] = useState ( '' );
const [ status , setStatus ] = useState < 'idle' | 'success' | 'error' >( 'idle' );
const handleSubmit = async ( e : React . FormEvent ) => {
e . preventDefault ();
try {
// Submit logic here
setStatus ( 'success' );
} catch ( error ) {
setStatus ( 'error' );
}
};
return (
< form onSubmit = { handleSubmit } className = "space-y-4" >
< div className = "relative" >
< input
type = "email"
value = { email }
onChange = { ( e ) => setEmail ( e . target . value ) }
className = "w-full px-4 py-2 border rounded-lg"
placeholder = "Enter your email"
/>
{ status === 'success' && (
< div className = "absolute right-3 top-1/2 -translate-y-1/2" >
< MotionIcon
name = "CheckCircle"
animation = "tada"
entrance = "zoomIn"
size = { 20 }
className = "text-green-500"
/>
</ div >
) }
{ status === 'error' && (
< div className = "absolute right-3 top-1/2 -translate-y-1/2" >
< MotionIcon
name = "XCircle"
animation = "shake"
entrance = "fadeIn"
size = { 20 }
className = "text-red-500"
/>
</ div >
) }
</ div >
< button type = "submit" className = "px-4 py-2 bg-blue-500 text-white rounded-lg" >
Subscribe
</ button >
</ form >
);
}
Troubleshooting
Problem: Icons appear but don’t animate.Solution:
Make sure you imported the CSS in your layout/app file:
import 'motion-icons-react/style.css' ;
Verify you’re using 'use client' directive in components that use animations
Clear Next.js cache: rm -rf .next && npm run dev
Problem: Getting hydration mismatch errors.Solution:
Ensure you’re using 'use client' in components with MotionIcon
Don’t use animations that depend on client-side state during SSR
Use dynamic imports if needed:
import dynamic from 'next/dynamic' ;
const AnimatedIcon = dynamic (
() => import ( '@/components/AnimatedIcon' ),
{ ssr: false }
);
Icons not showing in production
Problem: Icons work in development but not in production build.Solution:
Verify CSS is imported in the root layout
Check that lucide-react is installed as a dependency (not devDependency)
Clear build cache: rm -rf .next && npm run build
Problem: TypeScript compilation errors.Solution:
Ensure you have @types/react installed
Update TypeScript to version 4.0 or higher
Check that both packages are properly installed:
npm list motion-icons-react lucide-react
1. Use Dynamic Imports for Heavy Pages
If you have many animated icons, consider lazy loading:
import dynamic from 'next/dynamic' ;
const HeavyIconSection = dynamic (() => import ( '@/components/HeavyIconSection' ), {
loading : () => < p > Loading... </ p > ,
});
2. Optimize Animation Triggers
Use trigger="hover" or trigger="click" instead of trigger="always" to reduce continuous animations:
< MotionIcon
name = "Star"
animation = "pulse"
trigger = "hover" // Only animates on hover
/>
3. Limit Simultaneous Animations
Avoid having too many icons animating at once. Use animations strategically for important UI elements.
Next Steps