Docs

Vertical Tiles

A preloader component that makes transition to any section look vivid

Installation

Install dependencies

npm install framer-motion lucide-react

Run the following command

It will create a new file vertical-tiles.tsx inside the components/animata/preloader directory.

mkdir -p components/animata/preloader && touch components/animata/preloader/vertical-tiles.tsx

Paste the code

Open the newly created file and paste the following code:

import React, { useCallback, useEffect, useRef, useState } from "react";
import { motion, useInView } from "framer-motion";
 
import { cn } from "@/lib/utils";
 
interface Tile {
  id: number;
  width: number;
  order: number;
}
 
interface VerticalTilesProps {
  tileClassName?: string;
  minTileWidth?: number;
  animationDuration?: number;
  animationDelay?: number;
  stagger?: number;
  children?: React.ReactNode;
}
 
export default function VerticalTiles({
  tileClassName,
  minTileWidth = 32,
  animationDuration = 0.5,
  animationDelay = 1,
  stagger = 0.05,
  children,
}: VerticalTilesProps) {
  const [tiles, setTiles] = useState<Tile[]>([]);
  const containerRef = useRef<HTMLDivElement>(null);
  const isInView = useInView(containerRef, { once: true, amount: 0.3 });
 
  const calculateTiles = useCallback(() => {
    if (containerRef.current) {
      const { offsetWidth: width, offsetHeight: _ } = containerRef.current;
      const tileCount = Math.max(3, Math.floor(width / minTileWidth));
      const tileWidth = width / tileCount + 1;
 
      const newTiles = Array.from({ length: tileCount }, (_, index) => ({
        id: index,
        width: tileWidth,
        order: Math.abs(index - Math.floor((tileCount - 1) / 2)),
      }));
 
      setTiles(newTiles);
    }
  }, [minTileWidth]);
 
  useEffect(() => {
    calculateTiles();
    const resizeObserver = new ResizeObserver(calculateTiles);
    if (containerRef.current) {
      resizeObserver.observe(containerRef.current);
    }
    return () => resizeObserver.disconnect();
  }, [calculateTiles]);
 
  return (
    <div ref={containerRef} className="relative overflow-hidden">
      {children}
 
      <div className="absolute inset-0 flex">
        {tiles.map((tile) => (
          <motion.div
            key={tile.id}
            className={cn("bg-gray-800", tileClassName)}
            style={{
              width: tile.width,
              position: "absolute",
              left: `${(tile.id * 100) / tiles.length}%`,
              top: 0,
              height: "100%",
            }}
            initial={{ y: 0 }}
            animate={isInView ? { y: "100%" } : { y: 0 }}
            transition={{
              duration: animationDuration,
              delay: animationDelay + tile.order * stagger,
              ease: [0.45, 0, 0.55, 1],
            }}
          />
        ))}
      </div>
    </div>
  );
}

Credits

Built by SatyamVyas04

Inspired by SteviaPlease.me