Parallax cards
100xui/parallax-cardsFreeBlock
A reusable and responsive component that gives each of its children a smooth parallax effect, adding a modern, interactive touch to your website. Perfect for showcasing lists of content blocks, feature cards, or image galleries.
Live preview
live · rendered by the registry
Rendered by 100xui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://100xui.com/r/parallax-cards.jsonSource
1"use client";23import React from "react";4import {5 useScroll,6 useTransform,7 motion,8 type MotionValue,9 useMotionValueEvent,10} from "motion/react";1112import { cn } from "@/lib/utils";1314export interface ParallaxCardsProps extends React.ComponentPropsWithoutRef<"div"> {15 children: React.ReactElement[];16 maxStackedCards?: number;17 top?: React.CSSProperties["top"];18 forceParallax?: boolean;19}2021export function ParallaxCards({22 maxStackedCards = 3,23 top = "50px",24 forceParallax = false,25 className,26 style,27 children,28 ...rest29}: ParallaxCardsProps) {30 const totalCards = children.length;31 const topMagnitude = parseFloat(String(top));32 const topUnit = String(top).slice(String(topMagnitude).length) || "px";3334 if (topUnit === "%")35 throw new Error(36 "Invalid `top` value: percentages (%) are not supported by <ParallaxCards/>."37 );38 const containerRef = React.useRef<HTMLDivElement>(null);39 const [isSticky, setIsSticky] = React.useState(true);4041 const { scrollYProgress } = useScroll({42 target: containerRef,43 offset: ["start start", "end start"],44 });4546 React.useEffect(() => {47 if (forceParallax) return;4849 const element = containerRef.current;50 if (!element) return;5152 const handleResize = () => {53 const cardHeight = element.getBoundingClientRect().height / totalCards;54 const viewportHeight =55 window.visualViewport?.height ?? window.innerHeight;56 setIsSticky(viewportHeight >= cardHeight);57 };5859 handleResize();60 window.visualViewport?.addEventListener("resize", handleResize);61 window.addEventListener("resize", handleResize);6263 return () => {64 window.removeEventListener("resize", handleResize);65 window.visualViewport?.removeEventListener("resize", handleResize);66 };67 }, [totalCards, forceParallax]);6869 return (70 <>71 <style>{`72 html {73 scroll-behavior: smooth;74 }75 `}</style>7677 <div78 ref={containerRef}79 className={cn("w-full", className, "!relative !grid !py-0")}80 style={{81 ...style,82 gridTemplateRows: `repeat(${totalCards}, 1fr)`,83 }}84 {...rest}85 >86 {children.map((child, index) => (87 <ParallaxCard88 key={`cards[${index}]`}89 index={index}90 scrollYProgress={scrollYProgress}91 scrollRatio={1 / totalCards}92 maxStackedCards={maxStackedCards}93 top={{94 magnitude: topMagnitude,95// … truncated
What it pulls in
npm packages
Files it writes
More from 100xui
All 14 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Apple galleryapple-gallery | 100xui | Blocks | Free | 1 dep | |
| Chat bento cardchat-bento-card | 100xui | Blocks | Free | 1 dep | |
| demosdemos | 100xui | Blocks | Free | no deps · 12 files | |
| In-page navbarin-page-navbar | 100xui | Blocks | Free | 1 dep · 2 files | |
| internalsinternals | 100xui | Blocks | Free | no deps · 4 files | |
| iPhone 3D mockupiphone-3d-mockup | 100xui | Blocks | Free | no deps | |
| Menu wheelmenu-wheel | 100xui | Blocks | Free | 1 dep | |
| Morph modalmorph-modal | 100xui | Blocks | Free | 1 dep |
