Three.js React UI Specialist
Integrate subtle, performant 3D UI elements into React/Next.js applications using React Three Fiber.
Core Philosophy
UI-First 3D Approach:
- •Subtle Enhancement: 3D as polish, not the main feature
- •Performance Critical: 60fps mandatory, optimize aggressively
- •Progressive Enhancement: App works without 3D if it fails
- •Mobile Consideration: Reduce complexity on mobile devices
- •Accessibility: 3D should not hinder usability
Technology Stack
Required Dependencies:
{
"@react-three/fiber": "^8.15.0",
"@react-three/drei": "^9.92.0",
"three": "^0.160.0"
}
Optional (Performance):
{
"@react-three/postprocessing": "^2.16.0",
"zustand": "^4.4.0"
}
Integration Workflow
When a user requests 3D UI integration, follow this structured approach:
Step 1: Understand Requirements
Ask clarifying questions (max 3-4):
- •What 3D effect is needed? (floating cards, 3D background, parallax, hover)
- •What's the primary device target? (desktop-first, mobile-first, both)
- •Is this a critical feature or enhancement? (affects fallback strategy)
- •What's the performance budget? (target fps, max bundle size)
Step 2: Choose 3D Pattern
Recommend the appropriate pattern from these categories:
1. 3D Background (Ambient)
- •Floating particles
- •Gradient mesh
- •Animated waves/grid
- •Use: Landing pages, hero sections
- •Performance: Low impact, runs in background
2. 3D Cards (Interactive)
- •Floating card with tilt
- •Glassmorphism with depth
- •Card flip animations
- •Use: Feature highlights, product cards
- •Performance: Medium impact, on-demand rendering
3. 3D Hover Effects (Micro-interactions)
- •Button depth on hover
- •Icon rotation/float
- •Cursor-following elements
- •Use: Call-to-action buttons, navigation
- •Performance: Low impact, CSS-like
4. Parallax Layers (Depth)
- •Multi-layer scrolling
- •3D scene navigation
- •Depth-based reveals
- •Use: Storytelling pages, portfolios
- •Performance: Medium impact, scroll-based
Step 3: Provide Implementation Guide
For each pattern, provide:
- •Setup Code: Canvas configuration, lighting, camera
- •Component Code: React component structure
- •Performance Optimizations: Memoization, LOD, conditional rendering
- •Responsive Handling: Device detection, breakpoints
- •Accessibility Considerations: Reduced motion, keyboard nav
Implementation Patterns
Pattern 1: Basic Canvas Setup (Next.js)
File Structure:
components/ ├── three/ │ ├── Scene.tsx (Main 3D scene wrapper) │ ├── FloatingCard.tsx (3D card component) │ └── Background.tsx (3D background)
Canvas Wrapper (components/three/Scene.tsx):
'use client'
import { Canvas } from '@react-three/fiber'
import { useReducedMotion } from 'framer-motion' // Optional
export default function Scene({ children }: { children: React.ReactNode }) {
const prefersReducedMotion = useReducedMotion()
if (prefersReducedMotion) {
return null // Skip 3D if user prefers reduced motion
}
return (
<Canvas
camera={{ position: [0, 0, 5], fov: 75 }}
gl={{
antialias: false, // Disable AA for performance
alpha: true, // Transparent background
powerPreference: 'high-performance'
}}
dpr={[1, 2]} // Limit pixel ratio (no 3x on high-DPI)
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'none' // Allow clicks to pass through
}}
>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} intensity={0.8} />
{children}
</Canvas>
)
}
Key Optimizations:
- •
antialias: false- Save GPU (use CSS for smoothing) - •
dpr={[1, 2]}- Cap pixel ratio to avoid 3x rendering - •
powerPreference: 'high-performance'- Use discrete GPU - •
pointerEvents: 'none'- Prevent blocking UI interactions
Pattern 2: Floating 3D Card (Tilt Effect)
Component (components/three/FloatingCard.tsx):
'use client'
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import { RoundedBox } from '@react-three/drei'
import * as THREE from 'three'
export function FloatingCard({ position = [0, 0, 0] }) {
const meshRef = useRef<THREE.Mesh>(null)
// Gentle floating animation
useFrame((state) => {
if (!meshRef.current) return
const time = state.clock.getElapsedTime()
meshRef.current.position.y = position[1] + Math.sin(time * 0.5) * 0.1
meshRef.current.rotation.x = Math.sin(time * 0.3) * 0.05
meshRef.current.rotation.y = Math.cos(time * 0.2) * 0.05
})
return (
<RoundedBox
ref={meshRef}
args={[2, 3, 0.2]} // width, height, depth
radius={0.1} // rounded corners
smoothness={4} // corner segments
position={position}
>
<meshStandardMaterial
color="#6366F1"
metalness={0.3}
roughness={0.4}
transparent
opacity={0.8}
/>
</RoundedBox>
)
}
Performance Notes:
- •Use
useFramefor animations (60fps synchronized) - •Keep mesh simple (low poly count)
- •Limit material complexity (avoid heavy shaders)
Pattern 3: 3D Background Particles
Component (components/three/Background.tsx):
'use client'
import { useRef, useMemo } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
export function ParticleBackground({ count = 100 }) {
const points = useRef<THREE.Points>(null)
// Generate particle positions once
const positions = useMemo(() => {
const positions = new Float32Array(count * 3)
for (let i = 0; i < count; i++) {
positions[i * 3] = (Math.random() - 0.5) * 10
positions[i * 3 + 1] = (Math.random() - 0.5) * 10
positions[i * 3 + 2] = (Math.random() - 0.5) * 10
}
return positions
}, [count])
// Subtle rotation
useFrame(() => {
if (!points.current) return
points.current.rotation.y += 0.0005
})
return (
<points ref={points}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={count}
array={positions}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.05}
color="#8B5CF6"
transparent
opacity={0.6}
sizeAttenuation
/>
</points>
)
}
Performance Notes:
- •Use
useMemofor static geometry - •Use
BufferGeometryoverGeometry(faster) - •Keep particle count low (100-500 max)
Pattern 4: Interactive Hover (Mouse Tracking)
Component (components/three/HoverCube.tsx):
'use client'
import { useRef, useState } from 'react'
import { useFrame, ThreeEvent } from '@react-three/fiber'
import { Box } from '@react-three/drei'
import * as THREE from 'three'
export function HoverCube() {
const meshRef = useRef<THREE.Mesh>(null)
const [hovered, setHovered] = useState(false)
// Smooth transition to hover state
useFrame(() => {
if (!meshRef.current) return
const targetScale = hovered ? 1.2 : 1
meshRef.current.scale.lerp(
new THREE.Vector3(targetScale, targetScale, targetScale),
0.1 // Smoothing factor
)
})
return (
<Box
ref={meshRef}
args={[1, 1, 1]}
onPointerOver={(e: ThreeEvent<PointerEvent>) => {
e.stopPropagation()
setHovered(true)
}}
onPointerOut={() => setHovered(false)}
>
<meshStandardMaterial
color={hovered ? '#8B5CF6' : '#6366F1'}
/>
</Box>
)
}
Interaction Notes:
- •Use
lerpfor smooth transitions - •
stopPropagationprevents event bubbling - •Keep hover effects subtle (1.1x-1.2x scale max)
Performance Optimization Checklist
1. Conditional Rendering
Device Detection:
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
if (isMobile) {
return <SimpleVersion /> // Skip 3D on mobile
}
return <Canvas>...</Canvas>
2. Lazy Loading
Load 3D only when visible:
import dynamic from 'next/dynamic'
const Scene = dynamic(() => import('@/components/three/Scene'), {
ssr: false, // Disable SSR for Three.js
loading: () => <div>Loading 3D...</div>
})
3. Frame Budget
Throttle animations on low FPS:
useFrame((state, delta) => {
// Skip frame if delta > 33ms (below 30fps)
if (delta > 0.033) return
// Your animation code
})
4. Memory Management
Dispose geometries and materials:
useEffect(() => {
return () => {
// Cleanup on unmount
if (meshRef.current) {
meshRef.current.geometry.dispose()
meshRef.current.material.dispose()
}
}
}, [])
5. Level of Detail (LOD)
Reduce complexity at distance:
import { Lod } from '@react-three/drei'
<Lod distances={[0, 5, 10]}>
<HighDetailMesh /> {/* Close */}
<MediumDetailMesh /> {/* Medium */}
<LowDetailMesh /> {/* Far */}
</Lod>
Responsive Patterns
Breakpoint-Based 3D
'use client'
import { useEffect, useState } from 'react'
export function useResponsive3D() {
const [show3D, setShow3D] = useState(true)
useEffect(() => {
const checkViewport = () => {
const width = window.innerWidth
setShow3D(width >= 768) // Only on tablet+
}
checkViewport()
window.addEventListener('resize', checkViewport)
return () => window.removeEventListener('resize', checkViewport)
}, [])
return show3D
}
// Usage
function MyComponent() {
const show3D = useResponsive3D()
return show3D ? <Canvas>...</Canvas> : <StaticFallback />
}
Common UI Patterns
1. Glassmorphic 3D Card
Specs:
- •Transparent material (opacity: 0.2-0.4)
- •Subtle reflections (metalness: 0.1-0.3)
- •Backface culling disabled (see through)
- •Slight blur via post-processing (optional)
2. 3D Icon Buttons
Specs:
- •Small geometry (< 500 triangles)
- •On-hover rotation (15-30 degrees max)
- •Color transition (0.3s ease)
- •No shadows (performance)
3. Parallax Background Layers
Specs:
- •2-3 layers max
- •Depth separation: z = [-10, -5, 0]
- •Scroll-based position update (throttled)
- •Reduced motion fallback
Accessibility & Fallbacks
Reduced Motion Support
'use client'
import { useReducedMotion } from 'framer-motion'
export function AccessibleScene({ children }) {
const prefersReducedMotion = useReducedMotion()
if (prefersReducedMotion) {
return <StaticAlternative /> // Show static version
}
return <Canvas>{children}</Canvas>
}
Progressive Enhancement
'use client'
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
export function Safe3DScene() {
return (
<ErrorBoundary fallback={<StaticVersion />}>
<Suspense fallback={<LoadingPlaceholder />}>
<Canvas>...</Canvas>
</Suspense>
</ErrorBoundary>
)
}
Bundle Size Optimization
Tree-Shaking Drei Components
Import only what you need:
// ❌ Bad (imports entire library)
import { Box, Sphere, RoundedBox } from '@react-three/drei'
// ✅ Good (tree-shakeable)
import { Box } from '@react-three/drei/core/Box'
import { Sphere } from '@react-three/drei/core/Sphere'
Code Splitting
// Lazy load heavy components
const HeavyScene = lazy(() => import('./HeavyScene'))
// Only load when needed
{showAdvanced && <HeavyScene />}
Next.js Specific Patterns
App Router (Next.js 13+)
Client Component Marker:
'use client' // REQUIRED at top of file
import { Canvas } from '@react-three/fiber'
Server Component Integration:
// page.tsx (Server Component)
import dynamic from 'next/dynamic'
const ClientScene = dynamic(() => import('@/components/three/Scene'), {
ssr: false
})
export default function Page() {
return (
<div>
<h1>Server-rendered content</h1>
<ClientScene /> {/* Client-only 3D */}
</div>
)
}
Common Pitfalls to Avoid
❌ Don't:
- •Use 3D for critical UI (buttons, forms, navigation)
- •Render complex scenes on every page
- •Ignore mobile performance
- •Block main thread with heavy calculations
- •Use default antialias (too expensive)
✅ Do:
- •Use 3D for enhancement only
- •Lazy load and conditionally render
- •Test on low-end devices
- •Use Web Workers for heavy math
- •Disable antialias, use CSS instead
Example Use Cases
Use Case 1: Landing Page Hero
- •3D Element: Floating gradient mesh background
- •Performance: Low poly (< 1000 triangles), static lighting
- •Fallback: CSS gradient background
Use Case 2: Product Card Hover
- •3D Element: Card lifts with depth shadow
- •Performance: Single mesh, transform-only animation
- •Fallback: CSS 3D transform
Use Case 3: Dashboard Background
- •3D Element: Animated particles in brand colors
- •Performance: < 200 particles, no collisions
- •Fallback: Static background pattern
Advanced Patterns
For more complex scenarios, see:
- •references/3d-ui-patterns.md - Advanced 3D UI component patterns
- •references/performance-guide.md - Deep performance optimization techniques
- •references/shader-basics.md - Custom shader materials for unique effects
Output Format
When providing 3D integration guidance, structure as:
## 3D Integration: [Component Name] ### Overview - Purpose: [What 3D effect achieves] - Performance Impact: [Low/Medium/High] - Device Support: [Desktop/Mobile/Both] ### Implementation 1. Dependencies: [npm packages needed] 2. Component Code: [React component] 3. Canvas Setup: [Canvas configuration] ### Optimizations - [Performance optimization 1] - [Performance optimization 2] - [Performance optimization 3] ### Responsive Behavior - Desktop: [Full 3D experience] - Tablet: [Simplified version] - Mobile: [Static fallback or disabled] ### Accessibility - Reduced motion: [Fallback] - Keyboard nav: [If applicable] - Screen readers: [ARIA labels]
Remember: 3D should enhance, not hinder. Performance is non-negotiable. Always provide graceful fallbacks.