r3f-shaders
React Three Fiber shaders - GLSL, shaderMaterial, uniforms, custom effects. Use when creating custom visual effects, modifying vertices, writing fragment shaders, or extending built-in materials.
What this skill does
# React Three Fiber Shaders
## Quick Start
```tsx
import { Canvas, useFrame, extend } from '@react-three/fiber'
import { shaderMaterial } from '@react-three/drei'
import { useRef } from 'react'
import * as THREE from 'three'
// Create custom shader material
const ColorShiftMaterial = shaderMaterial(
// Uniforms
{ time: 0, color: new THREE.Color(0.2, 0.0, 0.1) },
// Vertex shader
`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
// Fragment shader
`
uniform float time;
uniform vec3 color;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv.x + sin(time), vUv.y + cos(time), color.b, 1.0);
}
`
)
// Extend so it can be used as JSX
extend({ ColorShiftMaterial })
function ShaderMesh() {
const materialRef = useRef()
useFrame(({ clock }) => {
materialRef.current.time = clock.elapsedTime
})
return (
<mesh>
<planeGeometry args={[2, 2]} />
{/* key={Material.key} enables HMR for shader development */}
<colorShiftMaterial ref={materialRef} key={ColorShiftMaterial.key} />
</mesh>
)
}
export default function App() {
return (
<Canvas>
<ShaderMesh />
</Canvas>
)
}
```
## shaderMaterial (Drei)
The recommended way to create shader materials in R3F.
### Basic Pattern
```tsx
import { shaderMaterial } from '@react-three/drei'
import { extend } from '@react-three/fiber'
import * as THREE from 'three'
// 1. Define the material
const MyShaderMaterial = shaderMaterial(
// Uniforms object
{
time: 0,
color: new THREE.Color(1, 0, 0),
opacity: 1,
map: null,
},
// Vertex shader (GLSL)
`
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
// Fragment shader (GLSL)
`
uniform float time;
uniform vec3 color;
uniform float opacity;
uniform sampler2D map;
varying vec2 vUv;
void main() {
vec4 texColor = texture2D(map, vUv);
gl_FragColor = vec4(color * texColor.rgb, opacity);
}
`
)
// 2. Extend R3F
extend({ MyShaderMaterial })
// 3. Use in component
function MyMesh() {
const materialRef = useRef()
useFrame(({ clock }) => {
materialRef.current.time = clock.elapsedTime
})
return (
<mesh>
<boxGeometry />
{/* key prop enables Hot Module Replacement during development */}
<myShaderMaterial
ref={materialRef}
key={MyShaderMaterial.key}
color="hotpink"
transparent
opacity={0.8}
/>
</mesh>
)
}
```
### Hot Module Replacement (HMR)
The `key` prop on shaderMaterial enables live shader editing without page refresh:
```tsx
const MyMaterial = shaderMaterial(
{ time: 0 },
vertexShader,
fragmentShader
)
extend({ MyMaterial })
// MyMaterial.key changes when shader code changes
<myMaterial key={MyMaterial.key} />
```
When you edit shader code, the material automatically updates. Without `key`, you'd need to refresh the page to see changes.
### TypeScript Support
```tsx
import { shaderMaterial } from '@react-three/drei'
import { extend, Object3DNode } from '@react-three/fiber'
import * as THREE from 'three'
// Define uniform types
type WaveMaterialUniforms = {
time: number
amplitude: number
color: THREE.Color
}
const WaveMaterial = shaderMaterial(
{
time: 0,
amplitude: 0.5,
color: new THREE.Color('hotpink'),
} as WaveMaterialUniforms,
// vertex shader
`...`,
// fragment shader
`...`
)
// Extend with proper types
extend({ WaveMaterial })
// Declare for TypeScript
declare module '@react-three/fiber' {
interface ThreeElements {
waveMaterial: Object3DNode<
typeof WaveMaterial & THREE.ShaderMaterial,
typeof WaveMaterial
>
}
}
```
## Raw THREE.ShaderMaterial
For full control without Drei helper.
```tsx
import { useFrame } from '@react-three/fiber'
import { useMemo, useRef } from 'react'
import * as THREE from 'three'
function CustomShaderMesh() {
const materialRef = useRef()
const shaderMaterial = useMemo(() => {
return new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color: { value: new THREE.Color('cyan') },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
uniform vec2 resolution;
varying vec2 vUv;
void main() {
vec2 st = gl_FragCoord.xy / resolution;
float pattern = sin(st.x * 20.0 + time) * sin(st.y * 20.0 + time);
gl_FragColor = vec4(color * pattern, 1.0);
}
`,
side: THREE.DoubleSide,
transparent: true,
})
}, [])
useFrame(({ clock }) => {
shaderMaterial.uniforms.time.value = clock.elapsedTime
})
return (
<mesh material={shaderMaterial}>
<planeGeometry args={[4, 4, 32, 32]} />
</mesh>
)
}
```
## Uniforms
### Common Uniform Types
```tsx
const MyMaterial = shaderMaterial(
{
// Numbers
time: 0,
intensity: 1.5,
// Vectors
resolution: new THREE.Vector2(1920, 1080),
lightPosition: new THREE.Vector3(5, 10, 5),
bounds: new THREE.Vector4(0, 0, 1, 1),
// Color (becomes vec3)
color: new THREE.Color('#ff0000'),
// Matrices
customMatrix: new THREE.Matrix4(),
// Textures
map: null, // sampler2D
cubeMap: null, // samplerCube
// Arrays
positions: [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()],
},
vertexShader,
fragmentShader
)
```
### GLSL Declarations
```glsl
// In shader code
uniform float time;
uniform float intensity;
uniform vec2 resolution;
uniform vec3 lightPosition;
uniform vec3 color; // THREE.Color becomes vec3
uniform vec4 bounds;
uniform mat4 customMatrix;
uniform sampler2D map;
uniform samplerCube cubeMap;
uniform vec3 positions[3];
```
### Updating Uniforms
```tsx
function AnimatedShader() {
const materialRef = useRef()
useFrame(({ clock, mouse, viewport }) => {
// Direct value update
materialRef.current.time = clock.elapsedTime
// Vector update
materialRef.current.resolution.set(viewport.width, viewport.height)
// Color update
materialRef.current.color.setHSL((clock.elapsedTime * 0.1) % 1, 1, 0.5)
// Or via uniforms object (for THREE.ShaderMaterial)
// materialRef.current.uniforms.time.value = clock.elapsedTime
})
return (
<mesh>
<boxGeometry />
<myShaderMaterial ref={materialRef} />
</mesh>
)
}
```
## Varyings
Pass data from vertex to fragment shader.
```glsl
// Vertex shader
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vWorldPosition;
void main() {
vUv = uv;
vNormal = normalize(normalMatrix * normal);
vPosition = position;
vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
// Fragment shader
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vWorldPosition;
void main() {
// Use interpolated values
gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1.0);
}
```
## Common Shader Patterns
### Texture Sampling
```tsx
import { useTexture } from '@react-three/drei'
function TexturedShaderMesh() {
const texture = useTexture('/textures/color.jpg')
const materialRef = useRef()
return (
<mesh>
<planeGeometry args={[2, 2]} />
<myShaderMaterial ref={materialRef} map={texture} />
</mesh>
)
}
// Shader
const TextureMaterial = shaderMaterial(
{ map: null },
`
varying vec2 vUv;
void main() {
vRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.