A production-ready starter template for building immersive 3D web experiences with React Three Fiber, Next.js 16, and TypeScript.
- React Three Fiber - Declarative Three.js with React
- Next.js 16 - App Router, Server Components, and optimized builds
- TypeScript - Full type safety throughout
- Tailwind CSS v4 - Modern utility-first styling
- Zustand - Lightweight state management with persistence
- @react-three/drei - Useful helpers for R3F (controls, loaders, abstractions)
- @react-three/postprocessing - Post-processing effects
- @react-three/rapier - Physics simulation
- @react-three/uikit - 3D UI components
- GLSL Support - Custom shaders with glslify
- Sandbox System - Modular 3D scene architecture
- View Mode Toggle - Switch between 2D and 3D experiences
- Scene Transitions - Smooth animated transitions between pages
- Node.js >= 22.x
- pnpm (recommended) or npm/yarn
# Clone the repository
git clone https://github.com/your-username/r3f-next-starter.git
cd r3f-next-starter
# Run the setup script (installs dependencies and validates environment)
pnpm setup
# Or install manually
pnpm install
# Start development server (with HTTPS)
pnpm devThe app will be available at https://localhost:3000
| Script | Description |
|---|---|
pnpm dev |
Start development server with HTTPS |
pnpm build |
Create production build |
pnpm start |
Start production server |
pnpm lint |
Run ESLint with auto-fix |
pnpm analyze |
Analyze bundle size |
pnpm new:sandbox |
Generate a new sandbox scene |
pnpm new:component |
Generate a new component (canvas or DOM) |
pnpm setup |
Initialize project after cloning |
pnpm reset |
Reset to blank starter state |
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout with 3D canvas
│ ├── page.tsx # Home page with view mode selection
│ └── space/ # Dynamic routes for sandboxes
│ └── [...segments]/
│ └── page.tsx # Sandbox renderer
│
├── src/
│ ├── components/
│ │ ├── canvas/ # 3D components (rendered in Canvas)
│ │ │ ├── Scene.tsx # Main canvas wrapper
│ │ │ ├── HomeScene.tsx
│ │ │ ├── View.tsx # Viewport for 3D scenes
│ │ │ └── ...
│ │ └── dom/ # 2D components (HTML/React)
│ │ ├── Layout.tsx # DOM layout wrapper
│ │ └── ...
│ │
│ ├── config/
│ │ └── sandboxes.ts # Sandbox registry
│ │
│ ├── helpers/
│ │ ├── global.ts # Tunnel-rat setup for R3F
│ │ └── components/
│ │ ├── Three.tsx # Helper to inject into 3D scene
│ │ └── Ui.tsx # Helper to inject UI overlays
│ │
│ ├── hooks/ # Custom React hooks
│ ├── sandboxes/ # 3D scene modules
│ │ ├── Basic.tsx
│ │ └── business/ # Example business sandboxes
│ │
│ ├── templates/ # Reusable templates
│ │ └── Shader/ # Custom shader template
│ │
│ ├── store.ts # Zustand store
│ └── utils.ts # Utility functions
│
├── public/ # Static assets
│ ├── models/ # 3D models (.glb)
│ └── icons/ # PWA icons
│
└── scripts/ # Developer scripts
├── new-sandbox.js # Sandbox generator
├── new-component.js # Component generator
├── setup.js # Project setup
└── reset.js # Reset to starter state
Sandboxes are self-contained 3D scenes. Use the generator or create manually:
pnpm new:sandboxFollow the prompts to create a new sandbox with boilerplate code.
- Create a new file in
src/sandboxes/:
// src/sandboxes/MySandbox.tsx
'use client'
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
import * as THREE from 'three'
export default function MySandbox() {
const meshRef = useRef<THREE.Mesh>(null)
useFrame((state, delta) => {
if (meshRef.current) {
meshRef.current.rotation.y += delta
}
})
return (
<>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
<mesh ref={meshRef}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
</>
)
}- Register in
src/config/sandboxes.ts:
import dynamic from 'next/dynamic'
export const sandboxes: SandboxConfig[] = [
// ... existing sandboxes
{
slug: 'my-sandbox',
title: 'My Sandbox',
char: 'M',
color: '#ff6b6b',
position: [0, 50, 0],
rotation: [0, 0, 0],
description: 'My custom 3D scene.',
Component: dynamic(() => import('@/sandboxes/MySandbox')),
},
]- Visit
/space/my-sandboxto see your sandbox.
This starter uses tunnel-rat to enable components anywhere in the React tree to render into the shared 3D canvas:
// In any component
import { Three } from '@/helpers/components/Three'
function MyPage() {
return (
<Three>
{/* This renders inside the Canvas */}
<mesh>
<sphereGeometry />
<meshBasicMaterial />
</mesh>
</Three>
)
}Zustand store with persistence (src/store.ts):
import { useAppStore } from '@/store'
// Access state
const viewMode = useAppStore((state) => state.viewMode)
// Update state
const setViewMode = useAppStore((state) => state.setViewMode)
setViewMode('3d')Place .glb or .gltf files in public/models/ and load them with drei:
import { useGLTF } from '@react-three/drei'
function Model() {
const { scene } = useGLTF('/models/my-model.glb')
return <primitive object={scene} />
}
// Preload for better performance
useGLTF.preload('/models/my-model.glb')Use the shader template in src/templates/Shader/:
import Shader from '@/templates/Shader/Shader'
function MyScene() {
return <Shader />
}Or create custom shaders with glslify:
// Import GLSL files
import vertexShader from './shader.vert'
import fragmentShader from './shader.frag'
<shaderMaterial
vertexShader={vertexShader}
fragmentShader={fragmentShader}
uniforms={{ uTime: { value: 0 } }}
/>- Use
<Suspense>boundaries for async loaded components - Preload models and textures with
useGLTF.preload()anduseTexture.preload() - Instance meshes when rendering many similar objects
- Use LOD (Level of Detail) for complex scenes
- Optimize DPR - Already configured in
Scene.tsxfor mobile - Memoize complex calculations with
useMemo
# Install Vercel CLI
pnpm add -g vercel
# Deploy
vercelpnpm build
# Output in .next/Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
MIT License - see LICENSE for details.
Built on top of create-r3f-app by Renaud ROHLINGER.