src/App.tsx
Ready
localhost:5173
Motion 12ReadyTSXLn 1, Col 1
Online Motion playground with React, TypeScript, spring animations, gestures, layout transitions, editable project files, and instant live preview.
Online Motion playground with React, TypeScript, spring animations, gestures, layout transitions, editable project files, and instant live preview.
Runtime: v12.42.2
Press Enter to evaluate, ↑ and ↓ to browse command history, and Ctrl/⌘ + L to clear the console.
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import './styles.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
import { useState } from 'react'
import { AnimatePresence, motion } from 'motion/react'
import { MotionCard, type MotionDemo } from './components/MotionCard'
const demos: MotionDemo[] = [
{ id: 'springs', label: 'Springs', value: '0.42s', detail: 'Natural, interruptible movement', tone: 'violet' },
{ id: 'layout', label: 'Layout', value: '12', detail: 'Shared layout transitions', tone: 'cyan' },
{ id: 'gestures', label: 'Gestures', value: '4', detail: 'Hover, tap, focus and drag', tone: 'lime' },
]
export default function App() {
const [filter, setFilter] = useState<'All' | 'Core'>('All')
const [replay, setReplay] = useState(0)
const visibleDemos = filter === 'All' ? demos : demos.slice(0, 2)
return (
<main className="app-shell">
<nav className="topbar">
<a className="brand" href="#top" aria-label="Motion lab home">
<span className="brand-mark" aria-hidden="true">M</span>
Motion lab
</a>
<span className="runtime"><i /> React runtime live</span>
</nav>
<section className="hero" id="top">
<div className="hero-copy">
<motion.p
className="eyebrow"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
>
MOTION FOR REACT
</motion.p>
<motion.h1
initial={{ opacity: 0, y: 18 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.08 }}
>
Make interfaces<br />feel <em>alive.</em>
</motion.h1>
<motion.p
className="hero-description"
initial={{ opacity: 0, y: 18 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.16 }}
>
Build fluid gestures, spring animations and layout transitions with a declarative React API.
</motion.p>
<div className="hero-actions">
<motion.button
className="primary-button"
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.96 }}
onClick={() => setReplay((value) => value + 1)}
>
Replay animation <span aria-hidden="true">↗</span>
</motion.button>
<span className="install-command">npm install motion</span>
</div>
</div>
<div className="stage" aria-label="Interactive spring animation">
<div className="stage-grid" />
<AnimatePresence mode="wait">
<motion.div
key={replay}
className="orbital"
initial={{ opacity: 0, scale: 0.55, rotate: -35 }}
animate={{ opacity: 1, scale: 1, rotate: 0 }}
exit={{ opacity: 0, scale: 1.15, rotate: 20 }}
transition={{ type: 'spring', stiffness: 170, damping: 17 }}
>
<motion.div
className="orbit orbit-one"
animate={{ rotate: 360 }}
transition={{ duration: 10, repeat: Infinity, ease: 'linear' }}
>
<span />
</motion.div>
<motion.div
className="orbit orbit-two"
animate={{ rotate: -360 }}
transition={{ duration: 14, repeat: Infinity, ease: 'linear' }}
>
<span />
</motion.div>
<motion.div
className="core"
animate={{ scale: [1, 1.08, 1] }}
transition={{ duration: 2.4, repeat: Infinity, ease: 'easeInOut' }}
>
motion
</motion.div>
</motion.div>
</AnimatePresence>
<span className="stage-label">SPRING · STIFFNESS 170</span>
</div>
</section>
<section className="demo-section" aria-labelledby="demo-title">
<header className="section-heading">
<div>
<p className="eyebrow">BUILDING BLOCKS</p>
<h2 id="demo-title">Animation primitives</h2>
</div>
<div className="filter" aria-label="Filter animation primitives">
{(['All', 'Core'] as const).map((option) => (
<button
key={option}
className={filter === option ? 'active' : ''}
onClick={() => setFilter(option)}
>
{option}
</button>
))}
</div>
</header>
<motion.div className="card-grid" layout>
<AnimatePresence mode="popLayout">
{visibleDemos.map((demo, index) => (
<MotionCard key={demo.id} demo={demo} index={index} />
))}
</AnimatePresence>
</motion.div>
</section>
</main>
)
}