We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide17 min read

React Bits - Kolekcja Animowanych Komponentów React

React Bits is an open-source collection of high-quality, animated and interactive React components. Ready-to-use UI elements for building modern interfaces.

React Bits - Kolekcja Animowanych Komponentów React

Czym jest React Bits?

React Bits to open-source'owa kolekcja wysokiej jakości, animowanych i interaktywnych komponentów React. Stworzona przez Davida Haza, biblioteka oferuje gotowe do użycia elementy UI, które możesz skopiować bezpośrednio do swojego projektu.

W świecie, gdzie użytkownicy oczekują płynnych animacji i responsywnych interfejsów, React Bits dostarcza komponenty, które robią wrażenie - bez konieczności pisania skomplikowanego kodu animacji od zera.

Podobnie jak Magic UI czy Aceternity UI, React Bits stosuje podejście "copy-paste" - zamiast instalować pakiet npm, kopiujesz kod źródłowy komponentu i masz nad nim pełną kontrolę. Możesz go modyfikować, dostosowywać i rozszerzać według własnych potrzeb.

Filozofia projektu

React Bits powstał z przekonania, że:

  • Piękne UI nie powinno wymagać godzin kodowania
  • Deweloperzy powinni mieć pełną kontrolę nad kodem
  • Animacje powinny być płynne i wydajne
  • Komponenty powinny być łatwe do customizacji

Strona

Website: reactbits.dev

Autor: David Haz

Status: Open-source | Aktywny development

Dlaczego React Bits?

Problem z tradycyjnymi bibliotekami

  1. Biblioteki npm - Dodają zależności, trudna customizacja
  2. Pisanie od zera - Czasochłonne, wymaga wiedzy o animacjach
  3. Płatne komponenty - Kosztowne dla małych projektów
  4. Generyczny wygląd - Bootstrap/Material wyglądają wszędzie tak samo

Rozwiązanie React Bits

  • Copy-paste - Zero zależności, pełna kontrola
  • Animacje out-of-the-box - Framer Motion pod maską
  • Customizable - Kod jest Twój
  • Nowoczesny design - Komponenty wyglądające profesjonalnie
  • TypeScript - Pełne typy dla lepszego DX

Kategorie komponentów

Text animations

Animowane efekty tekstowe:

TScomponents/TextReveal.tsx
TypeScript
// components/TextReveal.tsx
"use client"

import { motion } from "framer-motion"
import { useInView } from "react-intersection-observer"

interface TextRevealProps {
  text: string
  className?: string
}

export function TextReveal({ text, className }: TextRevealProps) {
  const [ref, inView] = useInView({
    triggerOnce: true,
    threshold: 0.1,
  })

  const words = text.split(" ")

  const container = {
    hidden: { opacity: 0 },
    visible: (i = 1) => ({
      opacity: 1,
      transition: { staggerChildren: 0.12, delayChildren: 0.04 * i },
    }),
  }

  const child = {
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        type: "spring",
        damping: 12,
        stiffness: 100,
      },
    },
    hidden: {
      opacity: 0,
      y: 20,
    },
  }

  return (
    <motion.div
      ref={ref}
      className={className}
      variants={container}
      initial="hidden"
      animate={inView ? "visible" : "hidden"}
    >
      {words.map((word, index) => (
        <motion.span
          key={index}
          variants={child}
          className="inline-block mr-2"
        >
          {word}
        </motion.span>
      ))}
    </motion.div>
  )
}

// Użycie
<TextReveal
  text="Welcome to React Bits"
  className="text-4xl font-bold"
/>

Animated buttons

Przyciski z efektami:

TScomponents/MagneticButton.tsx
TypeScript
// components/MagneticButton.tsx
"use client"

import { useRef, useState } from "react"
import { motion } from "framer-motion"

interface MagneticButtonProps {
  children: React.ReactNode
  className?: string
}

export function MagneticButton({ children, className }: MagneticButtonProps) {
  const ref = useRef<HTMLButtonElement>(null)
  const [position, setPosition] = useState({ x: 0, y: 0 })

  const handleMouse = (e: React.MouseEvent<HTMLButtonElement>) => {
    const { clientX, clientY } = e
    const { left, top, width, height } = ref.current!.getBoundingClientRect()
    const x = clientX - (left + width / 2)
    const y = clientY - (top + height / 2)
    setPosition({ x, y })
  }

  const reset = () => {
    setPosition({ x: 0, y: 0 })
  }

  return (
    <motion.button
      ref={ref}
      onMouseMove={handleMouse}
      onMouseLeave={reset}
      animate={{ x: position.x * 0.3, y: position.y * 0.3 }}
      transition={{ type: "spring", stiffness: 150, damping: 15 }}
      className={className}
    >
      <motion.span
        animate={{ x: position.x * 0.2, y: position.y * 0.2 }}
        transition={{ type: "spring", stiffness: 150, damping: 15 }}
      >
        {children}
      </motion.span>
    </motion.button>
  )
}

// Użycie
<MagneticButton className="px-6 py-3 bg-black text-white rounded-full">
  Hover me
</MagneticButton>

Cards & containers

Animowane karty:

TScomponents/TiltCard.tsx
TypeScript
// components/TiltCard.tsx
"use client"

import { useRef, useState } from "react"
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"

interface TiltCardProps {
  children: React.ReactNode
  className?: string
}

export function TiltCard({ children, className }: TiltCardProps) {
  const ref = useRef<HTMLDivElement>(null)

  const x = useMotionValue(0)
  const y = useMotionValue(0)

  const xSpring = useSpring(x, { stiffness: 300, damping: 30 })
  const ySpring = useSpring(y, { stiffness: 300, damping: 30 })

  const rotateX = useTransform(ySpring, [-0.5, 0.5], ["15deg", "-15deg"])
  const rotateY = useTransform(xSpring, [-0.5, 0.5], ["-15deg", "15deg"])

  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    const rect = ref.current!.getBoundingClientRect()
    const xPos = (e.clientX - rect.left) / rect.width - 0.5
    const yPos = (e.clientY - rect.top) / rect.height - 0.5
    x.set(xPos)
    y.set(yPos)
  }

  const handleMouseLeave = () => {
    x.set(0)
    y.set(0)
  }

  return (
    <motion.div
      ref={ref}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      style={{
        rotateX,
        rotateY,
        transformStyle: "preserve-3d",
      }}
      className={className}
    >
      <div style={{ transform: "translateZ(50px)" }}>
        {children}
      </div>
    </motion.div>
  )
}

// Użycie
<TiltCard className="p-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl">
  <h2 className="text-2xl font-bold text-white">3D Tilt Effect</h2>
  <p className="text-white/80">Move your mouse over me</p>
</TiltCard>

Backgrounds

Animowane tła:

TScomponents/GradientBackground.tsx
TypeScript
// components/GradientBackground.tsx
"use client"

import { motion } from "framer-motion"

export function GradientBackground() {
  return (
    <div className="fixed inset-0 -z-10 overflow-hidden">
      <motion.div
        className="absolute inset-0"
        animate={{
          background: [
            "radial-gradient(circle at 0% 0%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 100% 0%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 100% 100%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 0% 100%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 0% 0%, #7c3aed 0%, transparent 50%)",
          ],
        }}
        transition={{
          duration: 10,
          repeat: Infinity,
          ease: "linear",
        }}
      />
      <motion.div
        className="absolute inset-0"
        animate={{
          background: [
            "radial-gradient(circle at 100% 100%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 0% 100%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 0% 0%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 100% 0%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 100% 100%, #ec4899 0%, transparent 50%)",
          ],
        }}
        transition={{
          duration: 10,
          repeat: Infinity,
          ease: "linear",
        }}
      />
    </div>
  )
}

Transitions

Przejścia między elementami:

TScomponents/PageTransition.tsx
TypeScript
// components/PageTransition.tsx
"use client"

import { motion, AnimatePresence } from "framer-motion"
import { usePathname } from "next/navigation"

interface PageTransitionProps {
  children: React.ReactNode
}

export function PageTransition({ children }: PageTransitionProps) {
  const pathname = usePathname()

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.3, ease: "easeInOut" }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

// Użycie w layout.tsx
export default function Layout({ children }) {
  return (
    <PageTransition>
      {children}
    </PageTransition>
  )
}

Scroll animations

Animacje przy scrollu:

TScomponents/ScrollReveal.tsx
TypeScript
// components/ScrollReveal.tsx
"use client"

import { motion } from "framer-motion"
import { useInView } from "react-intersection-observer"

interface ScrollRevealProps {
  children: React.ReactNode
  className?: string
  direction?: "up" | "down" | "left" | "right"
  delay?: number
}

export function ScrollReveal({
  children,
  className,
  direction = "up",
  delay = 0,
}: ScrollRevealProps) {
  const [ref, inView] = useInView({
    triggerOnce: true,
    threshold: 0.1,
  })

  const directions = {
    up: { y: 50, x: 0 },
    down: { y: -50, x: 0 },
    left: { x: 50, y: 0 },
    right: { x: -50, y: 0 },
  }

  return (
    <motion.div
      ref={ref}
      initial={{
        opacity: 0,
        ...directions[direction],
      }}
      animate={inView ? {
        opacity: 1,
        x: 0,
        y: 0,
      } : {}}
      transition={{
        duration: 0.6,
        delay,
        ease: [0.25, 0.1, 0.25, 1],
      }}
      className={className}
    >
      {children}
    </motion.div>
  )
}

// Użycie
<ScrollReveal direction="up" delay={0.2}>
  <Card>Content that animates on scroll</Card>
</ScrollReveal>

Instalacja i setup

Wymagania

Code
Bash
# Twój projekt powinien mieć:
- React 18+
- Tailwind CSS (opcjonalnie)
- Framer Motion

# Instalacja Framer Motion
npm install framer-motion

# Dla scroll detection
npm install react-intersection-observer

Struktura projektu

Code
TEXT
components/
├── ui/
│   ├── TextReveal.tsx
│   ├── MagneticButton.tsx
│   ├── TiltCard.tsx
│   └── ...
├── backgrounds/
│   ├── GradientBackground.tsx
│   └── ParticlesBackground.tsx
└── transitions/
    ├── PageTransition.tsx
    └── FadeIn.tsx

Utility functions

TSlib/utils.ts
TypeScript
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Przykładowa landing page

TSapp/page.tsx
TypeScript
// app/page.tsx
import { TextReveal } from "@/components/ui/TextReveal"
import { MagneticButton } from "@/components/ui/MagneticButton"
import { TiltCard } from "@/components/ui/TiltCard"
import { GradientBackground } from "@/components/backgrounds/GradientBackground"
import { ScrollReveal } from "@/components/ui/ScrollReveal"

export default function LandingPage() {
  return (
    <main className="relative min-h-screen">
      <GradientBackground />

      {/* Hero Section */}
      <section className="h-screen flex items-center justify-center">
        <div className="text-center">
          <TextReveal
            text="Build beautiful interfaces"
            className="text-6xl font-bold text-white mb-6"
          />

          <p className="text-xl text-white/70 mb-8">
            With React Bits animated components
          </p>

          <MagneticButton className="px-8 py-4 bg-white text-black rounded-full font-semibold">
            Get Started
          </MagneticButton>
        </div>
      </section>

      {/* Features Section */}
      <section className="py-24 px-6">
        <div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
          {features.map((feature, i) => (
            <ScrollReveal key={i} direction="up" delay={i * 0.1}>
              <TiltCard className="p-8 bg-white/10 backdrop-blur rounded-2xl">
                <h3 className="text-2xl font-bold text-white mb-2">
                  {feature.title}
                </h3>
                <p className="text-white/70">{feature.description}</p>
              </TiltCard>
            </ScrollReveal>
          ))}
        </div>
      </section>
    </main>
  )
}

const features = [
  { title: "Animated", description: "Smooth animations out of the box" },
  { title: "Customizable", description: "Full control over the code" },
  { title: "TypeScript", description: "Complete type safety" },
]

Best practices

Performance

Code
TypeScript
// Używaj lazy loading dla ciężkich komponentów
import dynamic from "next/dynamic"

const HeavyAnimation = dynamic(
  () => import("@/components/HeavyAnimation"),
  { ssr: false }
)

// Używaj will-change dla lepszej wydajności
<motion.div
  style={{ willChange: "transform" }}
  animate={{ x: 100 }}
/>

// Unikaj animowania layout properties
// ❌ Wolne
<motion.div animate={{ width: 200 }} />

// ✅ Szybkie
<motion.div animate={{ scale: 1.2 }} />

Accessibility

Code
TypeScript
// Respektuj preferencje użytkownika
const prefersReducedMotion = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
).matches

<motion.div
  animate={prefersReducedMotion ? {} : { y: [0, -10, 0] }}
/>

// Lub globalnie
// framer-motion ma wbudowane wsparcie
<motion.div
  transition={{
    type: "spring",
    // Automatycznie wyłącza animacje jeśli użytkownik preferuje reduced motion
  }}
/>

FAQ - Najczęściej zadawane pytania

Czy React Bits jest darmowy?

Tak, React Bits jest w pełni darmowy i open-source.

Czy muszę instalować pakiet npm?

Nie, React Bits używa podejścia copy-paste. Kopiujesz kod komponentu do swojego projektu.

Jakie są zależności?

Większość komponentów wymaga:

  • React 18+
  • Framer Motion
  • (Opcjonalnie) Tailwind CSS

Czy działa z Next.js?

Tak, React Bits doskonale współpracuje z Next.js (zarówno Pages Router jak i App Router). Pamiętaj o dyrektywie "use client" dla komponentów z animacjami.

Czy mogę modyfikować komponenty?

Tak! To główna zaleta copy-paste. Kod jest Twój - zmień kolory, timing, efekty jak chcesz.

Podsumowanie

React Bits to świetne źródło animowanych komponentów React:

  • Copy-paste - Zero zależności od biblioteki
  • Animacje - Płynne efekty z Framer Motion
  • Customizable - Pełna kontrola nad kodem
  • TypeScript - Kompletne typy
  • Modern design - Profesjonalny wygląd

Idealne do tworzenia nowoczesnych landing pages, portfolio i aplikacji SaaS.


React Bits - Animated React Components Collection

What is React Bits?

React Bits is an open-source collection of high-quality, animated and interactive React components. Created by David Haz, the library offers ready-to-use UI elements that you can copy directly into your project.

In a world where users expect smooth animations and responsive interfaces, React Bits delivers components that impress - without the need to write complex animation code from scratch.

Similar to Magic UI or Aceternity UI, React Bits uses a "copy-paste" approach - instead of installing an npm package, you copy the component's source code and have full control over it. You can modify, customize, and extend it according to your own needs.

Project philosophy

React Bits was created with the belief that:

  • Beautiful UI shouldn't require hours of coding
  • Developers should have full control over the code
  • Animations should be smooth and performant
  • Components should be easy to customize

Website

Website: reactbits.dev

Author: David Haz

Status: Open-source | Active development

Why React Bits?

Problems with traditional libraries

  1. npm libraries - Add dependencies, difficult customization
  2. Writing from scratch - Time-consuming, requires animation knowledge
  3. Paid components - Expensive for small projects
  4. Generic look - Bootstrap/Material look the same everywhere

React Bits solution

  • Copy-paste - Zero dependencies, full control
  • Animations out-of-the-box - Framer Motion under the hood
  • Customizable - The code is yours
  • Modern design - Professional-looking components
  • TypeScript - Full types for better DX

Component categories

Text animations

Animated text effects:

TScomponents/TextReveal.tsx
TypeScript
// components/TextReveal.tsx
"use client"

import { motion } from "framer-motion"
import { useInView } from "react-intersection-observer"

interface TextRevealProps {
  text: string
  className?: string
}

export function TextReveal({ text, className }: TextRevealProps) {
  const [ref, inView] = useInView({
    triggerOnce: true,
    threshold: 0.1,
  })

  const words = text.split(" ")

  const container = {
    hidden: { opacity: 0 },
    visible: (i = 1) => ({
      opacity: 1,
      transition: { staggerChildren: 0.12, delayChildren: 0.04 * i },
    }),
  }

  const child = {
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        type: "spring",
        damping: 12,
        stiffness: 100,
      },
    },
    hidden: {
      opacity: 0,
      y: 20,
    },
  }

  return (
    <motion.div
      ref={ref}
      className={className}
      variants={container}
      initial="hidden"
      animate={inView ? "visible" : "hidden"}
    >
      {words.map((word, index) => (
        <motion.span
          key={index}
          variants={child}
          className="inline-block mr-2"
        >
          {word}
        </motion.span>
      ))}
    </motion.div>
  )
}

// Usage
<TextReveal
  text="Welcome to React Bits"
  className="text-4xl font-bold"
/>

Animated buttons

Buttons with effects:

TScomponents/MagneticButton.tsx
TypeScript
// components/MagneticButton.tsx
"use client"

import { useRef, useState } from "react"
import { motion } from "framer-motion"

interface MagneticButtonProps {
  children: React.ReactNode
  className?: string
}

export function MagneticButton({ children, className }: MagneticButtonProps) {
  const ref = useRef<HTMLButtonElement>(null)
  const [position, setPosition] = useState({ x: 0, y: 0 })

  const handleMouse = (e: React.MouseEvent<HTMLButtonElement>) => {
    const { clientX, clientY } = e
    const { left, top, width, height } = ref.current!.getBoundingClientRect()
    const x = clientX - (left + width / 2)
    const y = clientY - (top + height / 2)
    setPosition({ x, y })
  }

  const reset = () => {
    setPosition({ x: 0, y: 0 })
  }

  return (
    <motion.button
      ref={ref}
      onMouseMove={handleMouse}
      onMouseLeave={reset}
      animate={{ x: position.x * 0.3, y: position.y * 0.3 }}
      transition={{ type: "spring", stiffness: 150, damping: 15 }}
      className={className}
    >
      <motion.span
        animate={{ x: position.x * 0.2, y: position.y * 0.2 }}
        transition={{ type: "spring", stiffness: 150, damping: 15 }}
      >
        {children}
      </motion.span>
    </motion.button>
  )
}

// Usage
<MagneticButton className="px-6 py-3 bg-black text-white rounded-full">
  Hover me
</MagneticButton>

Cards & containers

Animated cards:

TScomponents/TiltCard.tsx
TypeScript
// components/TiltCard.tsx
"use client"

import { useRef, useState } from "react"
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"

interface TiltCardProps {
  children: React.ReactNode
  className?: string
}

export function TiltCard({ children, className }: TiltCardProps) {
  const ref = useRef<HTMLDivElement>(null)

  const x = useMotionValue(0)
  const y = useMotionValue(0)

  const xSpring = useSpring(x, { stiffness: 300, damping: 30 })
  const ySpring = useSpring(y, { stiffness: 300, damping: 30 })

  const rotateX = useTransform(ySpring, [-0.5, 0.5], ["15deg", "-15deg"])
  const rotateY = useTransform(xSpring, [-0.5, 0.5], ["-15deg", "15deg"])

  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    const rect = ref.current!.getBoundingClientRect()
    const xPos = (e.clientX - rect.left) / rect.width - 0.5
    const yPos = (e.clientY - rect.top) / rect.height - 0.5
    x.set(xPos)
    y.set(yPos)
  }

  const handleMouseLeave = () => {
    x.set(0)
    y.set(0)
  }

  return (
    <motion.div
      ref={ref}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      style={{
        rotateX,
        rotateY,
        transformStyle: "preserve-3d",
      }}
      className={className}
    >
      <div style={{ transform: "translateZ(50px)" }}>
        {children}
      </div>
    </motion.div>
  )
}

// Usage
<TiltCard className="p-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl">
  <h2 className="text-2xl font-bold text-white">3D Tilt Effect</h2>
  <p className="text-white/80">Move your mouse over me</p>
</TiltCard>

Backgrounds

Animated backgrounds:

TScomponents/GradientBackground.tsx
TypeScript
// components/GradientBackground.tsx
"use client"

import { motion } from "framer-motion"

export function GradientBackground() {
  return (
    <div className="fixed inset-0 -z-10 overflow-hidden">
      <motion.div
        className="absolute inset-0"
        animate={{
          background: [
            "radial-gradient(circle at 0% 0%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 100% 0%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 100% 100%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 0% 100%, #7c3aed 0%, transparent 50%)",
            "radial-gradient(circle at 0% 0%, #7c3aed 0%, transparent 50%)",
          ],
        }}
        transition={{
          duration: 10,
          repeat: Infinity,
          ease: "linear",
        }}
      />
      <motion.div
        className="absolute inset-0"
        animate={{
          background: [
            "radial-gradient(circle at 100% 100%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 0% 100%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 0% 0%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 100% 0%, #ec4899 0%, transparent 50%)",
            "radial-gradient(circle at 100% 100%, #ec4899 0%, transparent 50%)",
          ],
        }}
        transition={{
          duration: 10,
          repeat: Infinity,
          ease: "linear",
        }}
      />
    </div>
  )
}

Transitions

Transitions between elements:

TScomponents/PageTransition.tsx
TypeScript
// components/PageTransition.tsx
"use client"

import { motion, AnimatePresence } from "framer-motion"
import { usePathname } from "next/navigation"

interface PageTransitionProps {
  children: React.ReactNode
}

export function PageTransition({ children }: PageTransitionProps) {
  const pathname = usePathname()

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.3, ease: "easeInOut" }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

// Usage in layout.tsx
export default function Layout({ children }) {
  return (
    <PageTransition>
      {children}
    </PageTransition>
  )
}

Scroll animations

Animations on scroll:

TScomponents/ScrollReveal.tsx
TypeScript
// components/ScrollReveal.tsx
"use client"

import { motion } from "framer-motion"
import { useInView } from "react-intersection-observer"

interface ScrollRevealProps {
  children: React.ReactNode
  className?: string
  direction?: "up" | "down" | "left" | "right"
  delay?: number
}

export function ScrollReveal({
  children,
  className,
  direction = "up",
  delay = 0,
}: ScrollRevealProps) {
  const [ref, inView] = useInView({
    triggerOnce: true,
    threshold: 0.1,
  })

  const directions = {
    up: { y: 50, x: 0 },
    down: { y: -50, x: 0 },
    left: { x: 50, y: 0 },
    right: { x: -50, y: 0 },
  }

  return (
    <motion.div
      ref={ref}
      initial={{
        opacity: 0,
        ...directions[direction],
      }}
      animate={inView ? {
        opacity: 1,
        x: 0,
        y: 0,
      } : {}}
      transition={{
        duration: 0.6,
        delay,
        ease: [0.25, 0.1, 0.25, 1],
      }}
      className={className}
    >
      {children}
    </motion.div>
  )
}

// Usage
<ScrollReveal direction="up" delay={0.2}>
  <Card>Content that animates on scroll</Card>
</ScrollReveal>

Installation and setup

Requirements

Code
Bash
# Your project should have:
- React 18+
- Tailwind CSS (optional)
- Framer Motion

# Install Framer Motion
npm install framer-motion

# For scroll detection
npm install react-intersection-observer

Project structure

Code
TEXT
components/
├── ui/
│   ├── TextReveal.tsx
│   ├── MagneticButton.tsx
│   ├── TiltCard.tsx
│   └── ...
├── backgrounds/
│   ├── GradientBackground.tsx
│   └── ParticlesBackground.tsx
└── transitions/
    ├── PageTransition.tsx
    └── FadeIn.tsx

Utility functions

TSlib/utils.ts
TypeScript
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Example landing page

TSapp/page.tsx
TypeScript
// app/page.tsx
import { TextReveal } from "@/components/ui/TextReveal"
import { MagneticButton } from "@/components/ui/MagneticButton"
import { TiltCard } from "@/components/ui/TiltCard"
import { GradientBackground } from "@/components/backgrounds/GradientBackground"
import { ScrollReveal } from "@/components/ui/ScrollReveal"

export default function LandingPage() {
  return (
    <main className="relative min-h-screen">
      <GradientBackground />

      {/* Hero Section */}
      <section className="h-screen flex items-center justify-center">
        <div className="text-center">
          <TextReveal
            text="Build beautiful interfaces"
            className="text-6xl font-bold text-white mb-6"
          />

          <p className="text-xl text-white/70 mb-8">
            With React Bits animated components
          </p>

          <MagneticButton className="px-8 py-4 bg-white text-black rounded-full font-semibold">
            Get Started
          </MagneticButton>
        </div>
      </section>

      {/* Features Section */}
      <section className="py-24 px-6">
        <div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
          {features.map((feature, i) => (
            <ScrollReveal key={i} direction="up" delay={i * 0.1}>
              <TiltCard className="p-8 bg-white/10 backdrop-blur rounded-2xl">
                <h3 className="text-2xl font-bold text-white mb-2">
                  {feature.title}
                </h3>
                <p className="text-white/70">{feature.description}</p>
              </TiltCard>
            </ScrollReveal>
          ))}
        </div>
      </section>
    </main>
  )
}

const features = [
  { title: "Animated", description: "Smooth animations out of the box" },
  { title: "Customizable", description: "Full control over the code" },
  { title: "TypeScript", description: "Complete type safety" },
]

Best practices

Performance

Code
TypeScript
// Use lazy loading for heavy components
import dynamic from "next/dynamic"

const HeavyAnimation = dynamic(
  () => import("@/components/HeavyAnimation"),
  { ssr: false }
)

// Use will-change for better performance
<motion.div
  style={{ willChange: "transform" }}
  animate={{ x: 100 }}
/>

// Avoid animating layout properties
// ❌ Slow
<motion.div animate={{ width: 200 }} />

// ✅ Fast
<motion.div animate={{ scale: 1.2 }} />

Accessibility

Code
TypeScript
// Respect user preferences
const prefersReducedMotion = window.matchMedia(
  "(prefers-reduced-motion: reduce)"
).matches

<motion.div
  animate={prefersReducedMotion ? {} : { y: [0, -10, 0] }}
/>

// Or globally
// framer-motion has built-in support
<motion.div
  transition={{
    type: "spring",
    // Automatically disables animations if user prefers reduced motion
  }}
/>

FAQ - Frequently asked questions

Is React Bits free?

Yes, React Bits is completely free and open-source.

Do I need to install an npm package?

No, React Bits uses a copy-paste approach. You copy the component code into your project.

What are the dependencies?

Most components require:

  • React 18+
  • Framer Motion
  • (Optionally) Tailwind CSS

Does it work with Next.js?

Yes, React Bits works perfectly with Next.js (both Pages Router and App Router). Remember to use the "use client" directive for components with animations.

Can I modify the components?

Yes! That's the main advantage of copy-paste. The code is yours - change colors, timing, effects as you like.

Summary

React Bits is a great source of animated React components:

  • Copy-paste - Zero library dependencies
  • Animations - Smooth effects with Framer Motion
  • Customizable - Full control over the code
  • TypeScript - Complete types
  • Modern design - Professional look

Perfect for creating modern landing pages, portfolios, and SaaS applications.