Explore the React ecosystem including popular libraries like React Query, React Hook Form, Framer Motion, and other essential tools for modern React development.
Explore the React ecosystem including popular libraries like React Query, React Hook Form, Framer Motion, and other essential tools for modern React development.
Learn React Query for efficient server state management, caching, and data synchronization
Content by: Kriyansh Khunt
MERN Stack Developer
React Query is a powerful library for managing server state in React applications. It provides caching, background updates, error handling, and optimistic updates out of the box.
// Install React Query
npm install @tanstack/react-query
// Setup QueryClient
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
retry: 3,
refetchOnWindowFocus: false,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<div className="app">
<UserList />
<ReactQueryDevtools initialIsOpen={false} />
</div>
</QueryClientProvider>
);
}
// Basic query usage
import { useQuery } from '@tanstack/react-query';
function UserList() {
const { data: users, isLoading, error, refetch } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
return response.json();
},
});
if (isLoading) return <div>Loading users...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>Users</h2>
<button onClick={() => refetch()}>Refresh</button>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
// Query with parameters
function UserProfile({ userId }) {
const { data: user, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: async () => {
const response = await fetch('/api/users/' + userId);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
return response.json();
},
enabled: !!userId, // Only run query if userId exists
});
if (isLoading) return <div>Loading user...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Role: {user.role}</p>
</div>
);
}
// Mutations with React Query
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateUser() {
const queryClient = useQueryClient();
const createUserMutation = useMutation({
mutationFn: async (userData) => {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData),
});
if (!response.ok) {
throw new Error('Failed to create user');
}
return response.json();
},
onSuccess: (newUser) => {
// Invalidate and refetch users query
queryClient.invalidateQueries({ queryKey: ['users'] });
// Or update cache directly
queryClient.setQueryData(['users'], (oldData) => {
return oldData ? [...oldData, newUser] : [newUser];
});
},
onError: (error) => {
console.error('Failed to create user:', error);
},
});
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const userData = {
name: formData.get('name'),
email: formData.get('email'),
};
createUserMutation.mutate(userData);
};
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button
type="submit"
disabled={createUserMutation.isPending}
>
{createUserMutation.isPending ? 'Creating...' : 'Create User'}
</button>
</form>
);
}
// Optimistic updates
function UpdateUser({ user }) {
const queryClient = useQueryClient();
const updateUserMutation = useMutation({
mutationFn: async ({ userId, updates }) => {
const response = await fetch('/api/users/' + userId, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates),
});
if (!response.ok) {
throw new Error('Failed to update user');
}
return response.json();
},
onMutate: async ({ userId, updates }) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: ['users'] });
// Snapshot the previous value
const previousUsers = queryClient.getQueryData(['users']);
// Optimistically update to the new value
queryClient.setQueryData(['users'], (old) => {
return old.map(user =>
user.id === userId ? { ...user, ...updates } : user
);
});
// Return a context object with the snapshotted value
return { previousUsers };
},
onError: (err, variables, context) => {
// If the mutation fails, use the context returned from onMutate to roll back
if (context.previousUsers) {
queryClient.setQueryData(['users'], context.previousUsers);
}
},
onSettled: () => {
// Always refetch after error or success
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
const handleUpdate = (updates) => {
updateUserMutation.mutate({ userId: user.id, updates });
};
return (
<div>
<h3>{user.name}</h3>
<button
onClick={() => handleUpdate({ role: 'Admin' })}
disabled={updateUserMutation.isPending}
>
Make Admin
</button>
</div>
);
}
Test your understanding of this topic:
Master React Hook Form for efficient form handling with minimal re-renders and built-in validation
Content by: Vijay Parmar
MERN Stack Developer
React Hook Form is a performant, flexible and extensible form library with easy-to-use validation. It reduces the amount of code you need to write while removing unnecessary re-renders.
// Install React Hook Form
npm install react-hook-form
// Basic form
import { useForm } from 'react-hook-form';
function BasicForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset,
} = useForm();
const onSubmit = async (data) => {
console.log('Form data:', data);
// Handle form submission
try {
await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
reset(); // Reset form after successful submission
} catch (error) {
console.error('Submission failed:', error);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="name">Name</label>
<input
id="name"
{...register('name', {
required: 'Name is required',
minLength: { value: 2, message: 'Name must be at least 2 characters' }
})}
/>
{errors.name && <span className="error">{errors.name.message}</span>}
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
})}
/>
{errors.email && <span className="error">{errors.email.message}</span>}
</div>
<div>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
{...register('password', {
required: 'Password is required',
minLength: { value: 8, message: 'Password must be at least 8 characters' }
})}
/>
{errors.password && <span className="error">{errors.password.message}</span>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
// Advanced React Hook Form features
import { useForm, Controller } from 'react-hook-form';
// Form with custom validation
function AdvancedForm() {
const {
control,
register,
handleSubmit,
formState: { errors, isValid, isDirty },
watch,
setValue,
getValues,
trigger,
} = useForm({
mode: 'onChange', // Validate on change
criteriaMode: 'all', // Show all validation errors
});
const watchedPassword = watch('password');
const onSubmit = async (data) => {
console.log('Form data:', data);
};
// Custom validation function
const validatePassword = (value) => {
if (!value) return 'Password is required';
if (value.length < 8) return 'Password must be at least 8 characters';
if (!/[A-Z]/.test(value)) return 'Password must contain at least one uppercase letter';
if (!/[a-z]/.test(value)) return 'Password must contain at least one lowercase letter';
if (!/[0-9]/.test(value)) return 'Password must contain at least one number';
return true;
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="username">Username</label>
<input
id="username"
{...register('username', {
required: 'Username is required',
minLength: { value: 3, message: 'Username must be at least 3 characters' },
pattern: {
value: /^[a-zA-Z0-9_]+$/,
message: 'Username can only contain letters, numbers, and underscores'
}
})}
/>
{errors.username && <span className="error">{errors.username.message}</span>}
</div>
<div>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
{...register('password', {
validate: validatePassword
})}
/>
{errors.password && <span className="error">{errors.password.message}</span>}
</div>
<div>
<label htmlFor="confirmPassword">Confirm Password</label>
<input
id="confirmPassword"
type="password"
{...register('confirmPassword', {
required: 'Please confirm your password',
validate: (value) => value === watchedPassword || 'Passwords do not match'
})}
/>
{errors.confirmPassword && <span className="error">{errors.confirmPassword.message}</span>}
</div>
{/* Using Controller for complex inputs */}
<div>
<label htmlFor="country">Country</label>
<Controller
name="country"
control={control}
rules={{ required: 'Country is required' }}
render={({ field }) => (
<select {...field}>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="ca">Canada</option>
<option value="uk">United Kingdom</option>
<option value="au">Australia</option>
</select>
)}
/>
{errors.country && <span className="error">{errors.country.message}</span>}
</div>
<div>
<label htmlFor="bio">Bio</label>
<Controller
name="bio"
control={control}
rules={{ maxLength: { value: 500, message: 'Bio must be less than 500 characters' } }}
render={({ field }) => (
<textarea
{...field}
placeholder="Tell us about yourself..."
rows={4}
/>
)}
/>
{errors.bio && <span className="error">{errors.bio.message}</span>}
</div>
<button type="submit" disabled={!isValid || !isDirty}>
Submit
</button>
<button type="button" onClick={() => trigger()}>
Validate All
</button>
</form>
);
}
Test your understanding of this topic:
Learn Framer Motion for creating smooth animations and transitions in React applications
Content by: Sejal Mishra
MERN Stack Developer
Framer Motion is a production-ready motion library for React. It provides a simple API for creating animations, gestures, and drag interactions.
// Install Framer Motion
npm install framer-motion
// Basic animations
import { motion } from 'framer-motion';
function BasicAnimations() {
return (
<div>
{/* Fade in animation */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
<h1>Welcome to our app!</h1>
</motion.div>
{/* Slide in from left */}
<motion.div
initial={{ x: -100, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ duration: 0.8, ease: "easeOut" }}
>
<p>This content slides in from the left</p>
</motion.div>
{/* Scale animation */}
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
Click me!
</motion.button>
{/* Staggered animations */}
<motion.ul
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}}
>
{['Item 1', 'Item 2', 'Item 3', 'Item 4'].map((item, index) => (
<motion.li
key={index}
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}}
>
{item}
</motion.li>
))}
</motion.ul>
</div>
);
}
// Animated card component
function AnimatedCard({ children }) {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
whileHover={{
y: -10,
boxShadow: "0 20px 40px rgba(0,0,0,0.1)"
}}
transition={{
duration: 0.3,
ease: "easeOut"
}}
style={{
padding: '20px',
border: '1px solid #ddd',
borderRadius: '8px',
backgroundColor: 'white'
}}
>
{children}
</motion.div>
);
}
// Usage
function CardGrid() {
const cards = [
{ id: 1, title: 'Card 1', content: 'This is the first card' },
{ id: 2, title: 'Card 2', content: 'This is the second card' },
{ id: 3, title: 'Card 3', content: 'This is the third card' },
];
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '20px' }}>
{cards.map((card, index) => (
<AnimatedCard key={card.id}>
<h3>{card.title}</h3>
<p>{card.content}</p>
</AnimatedCard>
))}
</div>
);
}
// Advanced Framer Motion features
import { motion, AnimatePresence } from 'framer-motion';
// Page transitions
function PageTransition({ children }) {
return (
<motion.div
initial={{ opacity: 0, x: 100 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -100 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
}
// Modal with backdrop
function AnimatedModal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
zIndex: 1000,
}}
/>
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: 50 }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
zIndex: 1001,
minWidth: '300px',
}}
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
);
}
// Drag and drop
function DraggableItem({ id, children }) {
return (
<motion.div
drag
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
dragElastic={0.1}
whileDrag={{ scale: 1.1, boxShadow: "0 10px 20px rgba(0,0,0,0.2)" }}
style={{
padding: '10px',
border: '1px solid #ddd',
borderRadius: '4px',
backgroundColor: 'white',
cursor: 'grab',
}}
>
{children}
</motion.div>
);
}
// Gesture animations
function GestureExample() {
return (
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
drag="x"
dragConstraints={{ left: -100, right: 100 }}
dragElastic={0.2}
onDragEnd={(event, info) => {
console.log('Drag ended:', info);
}}
style={{
width: '100px',
height: '100px',
backgroundColor: '#ff6b6b',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
cursor: 'grab',
}}
>
Drag me!
</motion.div>
);
}
// Layout animations
function LayoutAnimation() {
const [items, setItems] = useState([1, 2, 3, 4, 5]);
const addItem = () => {
setItems([...items, items.length + 1]);
};
const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};
return (
<div>
<button onClick={addItem}>Add Item</button>
<motion.div layout>
{items.map((item, index) => (
<motion.div
key={item}
layout
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3 }}
style={{
padding: '10px',
margin: '5px',
backgroundColor: '#f0f0f0',
borderRadius: '4px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<span>Item {item}</span>
<button onClick={() => removeItem(index)}>Remove</button>
</motion.div>
))}
</motion.div>
</div>
);
}
// Scroll-triggered animations
function ScrollAnimation() {
return (
<motion.div
initial={{ opacity: 0, y: 100 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.8 }}
style={{
padding: '40px',
backgroundColor: '#f8f9fa',
margin: '20px 0',
borderRadius: '8px',
}}
>
<h2>This animates when scrolled into view</h2>
<p>This content will animate when it comes into the viewport.</p>
</motion.div>
);
}
Test your understanding of this topic:
Explore other essential React libraries for routing, styling, and utility functions
Content by: Harshit Sangani
MERN Stack Developer
// React Router v6 setup
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/users">Users</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
return <h1>Welcome to our app!</h1>;
}
function About() {
return <h1>About Us</h1>;
}
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers);
}, []);
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>
<Link to={'/users/' + user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
function UserDetail() {
const { id } = useParams();
const navigate = useNavigate();
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/users/' + id)
.then(res => res.json())
.then(setUser);
}, [id]);
if (!user) return <div>Loading...</div>;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
<button onClick={() => navigate('/users')}>Back to Users</button>
</div>
);
}
function NotFound() {
return <h1>404 - Page Not Found</h1>;
}
// Styled Components
import styled from 'styled-components';
// Basic styled components
const Button = styled.button`
background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
&:hover {
background-color: ${props => props.primary ? '#0056b3' : '#545b62'};
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
`;
const Card = styled.div`
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px;
margin: 10px;
transition: transform 0.2s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
`;
const Title = styled.h1`
color: #333;
font-size: 2rem;
margin-bottom: 1rem;
${props => props.small && `
font-size: 1.5rem;
`}
`;
// Extended components
const PrimaryButton = styled(Button)`
background-color: #28a745;
&:hover {
background-color: #218838;
}
`;
// Responsive design
const Container = styled.div`
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
@media (max-width: 768px) {
padding: 0 10px;
}
`;
const Grid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
@media (max-width: 768px) {
grid-template-columns: 1fr;
gap: 15px;
}
`;
// Usage
function StyledExample() {
return (
<Container>
<Title>Styled Components Example</Title>
<Grid>
<Card>
<h3>Card 1</h3>
<p>This is a styled card component.</p>
<Button>Regular Button</Button>
</Card>
<Card>
<h3>Card 2</h3>
<p>Another styled card component.</p>
<PrimaryButton>Primary Button</PrimaryButton>
</Card>
</Grid>
</Container>
);
}
// Exercise: Build a Complete React App with Ecosystem Libraries
// Create a comprehensive application using multiple React ecosystem libraries
// App.js - Main application with all libraries integrated
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import styled from 'styled-components';
import { useForm } from 'react-hook-form';
const queryClient = new QueryClient();
// Styled components
const AppContainer = styled.div`
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
`;
const Header = styled.header`
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;
const Nav = styled.nav`
display: flex;
gap: 20px;
a {
color: #333;
text-decoration: none;
padding: 10px 15px;
border-radius: 5px;
transition: background-color 0.2s;
&:hover {
background-color: #f0f0f0;
}
}
`;
const MainContent = styled.main`
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;
// User management with React Query
function UserManagement() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: async () => {
const response = await fetch('/api/users');
return response.json();
},
});
if (isLoading) return <div>Loading users...</div>;
if (error) return <div>Error loading users</div>;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<h2>User Management</h2>
<UserList users={users} />
<AddUserForm />
</motion.div>
);
}
// User list with animations
function UserList({ users }) {
return (
<motion.div layout>
{users.map((user, index) => (
<motion.div
key={user.id}
layout
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 50 }}
transition={{ duration: 0.3, delay: index * 0.1 }}
whileHover={{ scale: 1.02 }}
style={{
padding: '15px',
margin: '10px 0',
border: '1px solid #ddd',
borderRadius: '8px',
backgroundColor: '#f9f9f9',
}}
>
<h3>{user.name}</h3>
<p>{user.email}</p>
</motion.div>
))}
</motion.div>
);
}
// Add user form with React Hook Form
function AddUserForm() {
const queryClient = useQueryClient();
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset,
} = useForm();
const addUserMutation = useMutation({
mutationFn: async (userData) => {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData),
});
return response.json();
},
onSuccess: () => {
queryClient.invalidateQueries(['users']);
reset();
},
});
const onSubmit = (data) => {
addUserMutation.mutate(data);
};
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<h3>Add New User</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
placeholder="Name"
{...register('name', { required: 'Name is required' })}
/>
{errors.name && <span className="error">{errors.name.message}</span>}
</div>
<div>
<input
placeholder="Email"
type="email"
{...register('email', { required: 'Email is required' })}
/>
{errors.email && <span className="error">{errors.email.message}</span>}
</div>
<motion.button
type="submit"
disabled={isSubmitting}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{isSubmitting ? 'Adding...' : 'Add User'}
</motion.button>
</form>
</motion.div>
);
}
// Dashboard with animations
function Dashboard() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8 }}
>
<motion.h1
initial={{ y: -50 }}
animate={{ y: 0 }}
transition={{ duration: 0.6 }}
>
Dashboard
</motion.h1>
<motion.div
initial={{ scale: 0.8 }}
animate={{ scale: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
gap: '20px',
marginTop: '30px',
}}
>
<motion.div
whileHover={{ y: -5 }}
style={{
padding: '20px',
backgroundColor: '#e3f2fd',
borderRadius: '8px',
textAlign: 'center',
}}
>
<h3>Total Users</h3>
<p style={{ fontSize: '2rem', fontWeight: 'bold' }}>1,234</p>
</motion.div>
<motion.div
whileHover={{ y: -5 }}
style={{
padding: '20px',
backgroundColor: '#f3e5f5',
borderRadius: '8px',
textAlign: 'center',
}}
>
<h3>Active Sessions</h3>
<p style={{ fontSize: '2rem', fontWeight: 'bold' }}>567</p>
</motion.div>
<motion.div
whileHover={{ y: -5 }}
style={{
padding: '20px',
backgroundColor: '#e8f5e8',
borderRadius: '8px',
textAlign: 'center',
}}
>
<h3>Revenue</h3>
<p style={{ fontSize: '2rem', fontWeight: 'bold' }}>$12,345</p>
</motion.div>
</motion.div>
</motion.div>
);
}
// Main App component
function App() {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AppContainer>
<Header>
<h1>React Ecosystem Demo</h1>
<Nav>
<Link to="/">Dashboard</Link>
<Link to="/users">Users</Link>
<Link to="/about">About</Link>
</Nav>
</Header>
<MainContent>
<AnimatePresence mode="wait">
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/users" element={<UserManagement />} />
<Route path="/about" element={<About />} />
</Routes>
</AnimatePresence>
</MainContent>
</AppContainer>
</BrowserRouter>
</QueryClientProvider>
);
}
// Challenge: Add authentication with React Query
// Challenge: Add real-time updates with WebSocket
// Challenge: Add advanced animations and transitions
Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 9