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
- Biblioteki npm - Dodają zależności, trudna customizacja
- Pisanie od zera - Czasochłonne, wymaga wiedzy o animacjach
- Płatne komponenty - Kosztowne dla małych projektów
- 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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
# 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-observerStruktura projektu
components/
├── ui/
│ ├── TextReveal.tsx
│ ├── MagneticButton.tsx
│ ├── TiltCard.tsx
│ └── ...
├── backgrounds/
│ ├── GradientBackground.tsx
│ └── ParticlesBackground.tsx
└── transitions/
├── PageTransition.tsx
└── FadeIn.tsxUtility functions
// 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
// 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
// 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
// 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
- npm libraries - Add dependencies, difficult customization
- Writing from scratch - Time-consuming, requires animation knowledge
- Paid components - Expensive for small projects
- 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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
# 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-observerProject structure
components/
├── ui/
│ ├── TextReveal.tsx
│ ├── MagneticButton.tsx
│ ├── TiltCard.tsx
│ └── ...
├── backgrounds/
│ ├── GradientBackground.tsx
│ └── ParticlesBackground.tsx
└── transitions/
├── PageTransition.tsx
└── FadeIn.tsxUtility functions
// 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
// 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
// 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
// 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.