Interactive demos and experiments exploring modern web development techniques, performance optimizations, and creative implementations.
Collection of reusable custom hooks for common patterns like debouncing, local storage, and media queries.
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}Lazy loading images and triggering animations on scroll using the Intersection Observer API.
const LazyImage = ({ src, alt }) => {
const [isVisible, setIsVisible] = useState(false)
const imgRef = useRef(null)
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true)
observer.disconnect()
}
},
{ threshold: 0.1 }
)
if (imgRef.current) {
observer.observe(imgRef.current)
}
return () => observer.disconnect()
}, [])
return (
<img
ref={imgRef}
src={isVisible ? src : ''}
alt={alt}
/>
)
}Smooth number animations perfect for dashboards, landing pages, and data visualizations.
function AnimatedCounter({ value, duration = 2000 }) {
const [displayValue, setDisplayValue] = useState(0)
useEffect(() => {
const startTime = Date.now()
const startValue = displayValue
const animate = () => {
const now = Date.now()
const progress = Math.min((now - startTime) / duration, 1)
const easeOut = 1 - Math.pow(1 - progress, 3)
const current = startValue + (value - startValue) * easeOut
setDisplayValue(Math.floor(current))
if (progress < 1) {
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
}, [value, duration])
return <span>{displayValue.toLocaleString()}</span>
}Bidirectional communication using WebSockets for real-time messaging with connection management.
const useWebSocket = (url: string) => {
const [messages, setMessages] = useState([])
const ws = useRef<WebSocket | null>(null)
useEffect(() => {
ws.current = new WebSocket(url)
ws.current.onmessage = (event) => {
setMessages(prev => [...prev, event.data])
}
return () => ws.current?.close()
}, [url])
const sendMessage = (msg: string) => {
ws.current?.send(msg)
}
return { messages, sendMessage }
}Efficiently render large lists by only rendering visible items. Improves performance for thousands of items.
const VirtualList = ({ items, itemHeight }) => {
const [scrollTop, setScrollTop] = useState(0)
const containerHeight = 600
const startIndex = Math.floor(scrollTop / itemHeight)
const endIndex = Math.ceil(
(scrollTop + containerHeight) / itemHeight
)
const visibleItems = items.slice(
startIndex,
endIndex
)
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: items.length * itemHeight }}>
{visibleItems.map((item, i) => (
<div
key={startIndex + i}
style={{
position: 'absolute',
top: (startIndex + i) * itemHeight
}}
>
{item}
</div>
))}
</div>
</div>
)
}Smooth drag and drop interactions using Framer Motion with gesture controls and layout animations.
const DraggableCard = ({ id, children }) => {
return (
<motion.div
drag
dragConstraints={{
left: 0, right: 300,
top: 0, bottom: 300
}}
dragElastic={0.1}
whileDrag={{ scale: 1.05 }}
dragTransition={{
bounceStiffness: 600,
bounceDamping: 20
}}
>
{children}
</motion.div>
)
}Interactive particle system using HTML5 Canvas with mouse interaction and physics simulation.
class Particle {
constructor(x, y) {
this.x = x
this.y = y
this.vx = Math.random() * 2 - 1
this.vy = Math.random() * 2 - 1
}
update() {
this.x += this.vx
this.y += this.vy
if (this.x < 0 || this.x > canvas.width) {
this.vx *= -1
}
if (this.y < 0 || this.y > canvas.height) {
this.vy *= -1
}
}
draw(ctx) {
ctx.beginPath()
ctx.arc(this.x, this.y, 2, 0, Math.PI * 2)
ctx.fill()
}
}Pinterest-style masonry layout using modern CSS Grid with dynamic item heights.
.masonry-grid {
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(250px, 1fr)
);
grid-auto-rows: 10px;
gap: 16px;
}
.masonry-item {
grid-row-end: span var(--row-span);
}
/* Calculate row span based on content height */
const item = document.querySelector('.masonry-item')
const rowHeight = 10
const rowGap = 16
const rowSpan = Math.ceil(
(item.offsetHeight + rowGap) / (rowHeight + rowGap)
)
item.style.setProperty('--row-span', rowSpan)Smooth physics-based animations using React Spring. Demonstrates natural motion with spring physics.
const AnimatedBox = () => {
const [flip, setFlip] = useState(false)
const { x } = useSpring({
x: flip ? 1 : 0,
config: { tension: 280, friction: 60 }
})
return (
<animated.div
onClick={() => setFlip(!flip)}
style={{
transform: x.to(val =>
`scale(${1 + val * 0.5}) rotate(${val * 180}deg)`
)
}}
/>
)
}Predictable state management using finite state machines. Perfect for complex UI flows.
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' }
},
active: {
on: { TOGGLE: 'inactive' }
}
}
})
const Toggle = () => {
const [state, send] = useMachine(toggleMachine)
return (
<button onClick={() => send('TOGGLE')}>
{state.value}
</button>
)
}Offload CPU-intensive tasks to background threads without blocking the main UI thread.
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data)
self.postMessage(result)
}
// main.js
const worker = new Worker('worker.js')
worker.postMessage({ data: largeDataset })
worker.onmessage = (e) => {
console.log('Result:', e.data)
updateUI(e.data)
}Recognize swipe, pinch, and rotate gestures on touch devices with custom gesture handlers.
const useGesture = (ref) => {
const [gesture, setGesture] = useState(null)
useEffect(() => {
let startX, startY
const handleTouchStart = (e) => {
startX = e.touches[0].clientX
startY = e.touches[0].clientY
}
const handleTouchEnd = (e) => {
const endX = e.changedTouches[0].clientX
const endY = e.changedTouches[0].clientY
const diffX = endX - startX
const diffY = endY - startY
if (Math.abs(diffX) > Math.abs(diffY)) {
setGesture(diffX > 0 ? 'swipe-right' : 'swipe-left')
} else {
setGesture(diffY > 0 ? 'swipe-down' : 'swipe-up')
}
}
const el = ref.current
el?.addEventListener('touchstart', handleTouchStart)
el?.addEventListener('touchend', handleTouchEnd)
return () => {
el?.removeEventListener('touchstart', handleTouchStart)
el?.removeEventListener('touchend', handleTouchEnd)
}
}, [ref])
return gesture
}Implement offline-first PWA with service worker caching strategies for better performance.
// service-worker.js
const CACHE_NAME = 'v1'
const urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
]
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
)
})
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
return response || fetch(event.request)
})
)
})