Aus dem Quell-Repository gerendert; Überschriften, Beispiele, Code, Tabellen, Links und Bilder bleiben erhalten.
R3F v10 WebGPU hooks
Use this reference in an existing R3F v10 project. Inspect the target's installed @react-three/fiber, three, and Three typings before changing code; alpha APIs and lifecycle behavior differ from upstream docs. Resolve dependencies from the target workspace, then inspect Fiber's dist/webgpu/index.d.ts and index.mjs for the relevant hook. Do not upgrade packages or migrate a WebGL canvas merely to apply this skill.
Import scene hooks from @react-three/fiber/webgpu, Three classes/types from three/webgpu, and shader expressions from three/tsl. Call scene hooks beneath the matching Canvas. The WebGPU entry already types state.renderer as WebGPURenderer; no cast is needed.
Choose the hook
| Hook | Use and call shape |
|---|---|
useUniform | One named root uniform: useUniform('uTime', 0). useUniform('uTime') reads an existing uniform and throws if missing. Names share state; this is not a component-local uniform. |
useUniforms | Related or instance-scoped uniforms: useUniforms({ uAmount: 0 }, scope) or useUniforms(creator, scope). Read with useUniforms(scope); omit arguments to read the registry. |
useNodes | Register shared TSL graphs: useNodes(creator, scope). Read with useNodes(scope) or useNodes(). Reserve it for graphs with actual shared consumers. |
useLocalNodes | Compose a component's material/compute nodes: useLocalNodes(creator). Returns the creator's object without registering it. Can include non-node helpers when needed. |
useBuffers | Register buffer resources: useBuffers(creator, scope); read with useBuffers(scope) or useBuffers(). Creator returns named attributes, typed arrays, or supported TSL storage nodes. |
useGPUStorage | Register storage textures: useGPUStorage(creator, scope); read with useGPUStorage(scope) or useGPUStorage(). Use Three storage texture objects or supported storage nodes. |
useTexture | Suspense texture loading: useTexture(urlOrArrayOrRecord, { onLoad, cache }). Returns plain Three textures in the input shape, not TSL nodes. |
useTextures | Reactive texture registry: useTextures(registry => registry.get(key)). Does not load or suspend; a missing key returns undefined. |
useRenderPipeline | One declarative postprocessing pipeline per root: useRenderPipeline(mainCB, setupCB). No arguments gives access to the root pipeline state. |
Keep CPU layouts, scratch vectors, matrices, and static source arrays in ordinary React memoization. A memo is not a disposal mechanism. Do not move private, correctly owned uniform() objects into a shared registry without a reason.
Uniforms and node composition
Name uniforms with the uName convention, such as uAmount or uTime. Update hook-returned uniforms through .value; mutate vector/color values with .set() or .copy() when appropriate. Use effects for prop changes and useFrame for animation. Avoid React state per frame and refs that retain an obsolete uniform after a rebuild.
For repeated instances, use a stable unique scope. When deriving one from useId(), prefix a letter and strip non-alphanumeric characters: const scope = 'effect' + useId().replace(/[^a-zA-Z0-9]/g, ''). Avoid generated WGSL names containing __ or beginning with a digit.
Creator callbacks receive CreatorState. Its uniforms, nodes, buffers, and gpuStorage are scoped wrappers: uniforms.uTime reads a root entry and uniforms.scope('effect').uAmount reads a scoped entry. textures is a Map of plain textures, so read with textures.get(key) and wrap with TSL texture(...) when sampling.
Mount Parent beneath the Canvas. It creates and updates uAmount in the scene scope; Child reads that same uniform inside useLocalNodes, without passing it through props. This example assumes one parent; repeated independent parents need unique scopes.
'use client'
import { useFrame, useLocalNodes, useUniforms } from '@react-three/fiber/webgpu'
import type { FC } from 'react'
import { normalLocal, positionLocal } from 'three/tsl'
import type { UniformNode } from 'three/webgpu'
type Uniforms = { uAmount: UniformNode<'float', number> }
const Parent: FC = () => {
const { uAmount } = useUniforms({ uAmount: 0 }, 'scene')
useFrame(({ elapsed }) => {
uAmount.value = Math.sin(elapsed) * 0.1
})
return <Child />
}
const Child: FC = () => {
const { positionNode } = useLocalNodes(({ uniforms }) => {
const { uAmount } = uniforms.scope<Uniforms>('scene')
return { positionNode: positionLocal.add(normalLocal.mul(uAmount)) }
})
return (
<mesh>
<sphereGeometry />
<meshBasicNodeMaterial color="orange" positionNode={positionNode} />
</mesh>
)
}- Inline local creators are fine for small graphs; they may run again on each React render. Stabilize expensive creators with
useCallbackand include every captured graph input in its dependencies. - Changing a uniform value does not rebuild a graph. Compose with the uniform node, not a captured
.value. Use TSLselectorIfinsideFnfor GPU-time branching; a JavaScriptifselects a graph at creation time. - Creator registration can be staged until a layout effect. Reader hooks in another component can initially return an empty scope. Check readiness before using external entries; a TypeScript generic does not prove registration happened. Keep hooks unconditional and establish the resource owner before consumers.
- Infer types from creators. Use reader-mode schema generics and
.scope<Schema>(name)only at shared boundaries; importCreatorState,UniformNode, and other library types instead of redefining them. useNodesis a registry, not a dependency-array memo. Verify rebuild semantics before expecting prop or callback changes to replace registered graphs.
Buffers, storage, and rebuilds
// Inside a scene component; count and instanceKey are supplied by its owner:
const scope = `particles${instanceKey}Count${count}`
const createBuffers = useCallback(
() => ({
positions: instancedArray(count, 'vec4'),
}),
[count],
)
const { positions } = useBuffers(createBuffers, scope)
// Import instancedArray from three/tsl and useBuffers from the WebGPU entry.
// Compute: positions.element(instanceIndex); rendering: positions.toAttribute().xyz.
const createStorage = useCallback(
() => ({
field: new StorageTexture(width, height),
}),
[width, height],
)
const storage = useGPUStorage(createStorage, `field${instanceKey}W${width}H${height}`)
// Import StorageTexture from three/webgpu and useGPUStorage from the WebGPU entry.These are registration examples, not complete ownership/cleanup implementations. Use scopes that distinguish independent owners and every allocation-defining input (count, dimensions, layout, and source generation when applicable). Initialize replacement buffers before compute or drawing reads them, and rebind dependent graphs to the replacement resources.
| Hook family | Returned lifecycle utilities |
|---|---|
| Uniforms / nodes | removeUniforms / removeNodes, clearUniforms / clearNodes, rebuildUniforms / rebuildNodes |
| Buffers | removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers |
| GPU storage | removeStorage, clearStorage, rebuildStorage, disposeStorage |
Pass the scope explicitly to lifecycle utilities, e.g. disposeStorage(['field'], scope); do not assume they inherit the creating hook's scope. Removal, cache invalidation, and GPU disposal are different operations. Unscoped clear/rebuild can affect other consumers. Do not dispose shared resources from an arbitrary reader.
Rebuilds can replace resource identities. Refresh material/compute graphs and callbacks that captured the outgoing generation. A compute node's disposal does not imply its storage allocation was released; inspect the underlying attribute/texture and renderer ownership path.
For alpha.5 or uncertain lifecycle behavior, read [references/alpha-5.md](references/alpha-5.md) before implementing replacement or teardown. It records implementation limits that the generic API descriptions miss.
Texture loading and sharing
useTexture('/albedo.png', { onLoad: texture => { /* configure texture */ } }) loads through Suspense and enrolls by URL by default. Array and record inputs preserve their return shape. Configure shared textures consistently: changing color space or wrapping changes the object other consumers use. cache: false opts out of registry enrollment; it does not guarantee a fresh, uncached loader result.
Use useTexture.preload(url) to preload and useTexture.clear(url) to clear the loader cache. Cache clearing is not GPU disposal. Registry access uses get, has, add, dispose, and disposeAll; add supports procedural/render-target textures and belongs in owner lifecycle code, not as an unconditional render side effect. Registry reads do not themselves load or guarantee an ownership reference.
Mounted useTexture consumers do not subscribe to later registry replacements; use useTextures(r => r.get(key)) for that. dispose(key) respects active loader consumers unless forced; disposeAll() is a whole-registry teardown operation. Coordinate resource ownership rather than forcing disposal from a reader.
Render pipelines
useRenderPipeline(mainCB, setupCB) calls setup before main. Configure MRT on passes.scenePass in setup; compose its texture nodes into renderPipeline.outputNode in main. The return includes isReady, nullable renderPipeline, passes, rebuild, reset, and clearPasses.
- Use one configuring owner per root. R3F's default render job renders the pipeline; registering a
useFramejob in therenderphase takes over rendering and disables that default job. - Callback identity or captured prop changes alone do not rebuild the graph in alpha.5. Update numeric uniforms directly; call
rebuild()for structural changes. CheckisReadybefore using the returned pipeline. - The hook persists across unmount. In alpha.5,
reset()clears root state and disposes the cached scene pass, but not the pipeline or custom passes/effects. The configuring owner must release what it owns;clearPasses()alone is not disposal. - Keep explicit Three
RenderPipelineownership when several independent pipelines or manual switching are required. Do not introduce competing root owners.
Other application hooks and prop helpers
| API | Relevant v10 behavior |
|---|---|
useFrame(callback, options) | Timing is state.delta / state.elapsed in seconds, state.time in milliseconds, and state.frame; there is no state.clock. Options support phase, job ordering, FPS throttling, and enabled state; returns scheduler controls. Prefer explicit options to legacy numeric priority. |
useThree(selector) | Reactive root selection, e.g. s => s.renderer. Deep mutation of Three objects does not trigger React updates. In demand mode, request a frame with invalidate() after imperative visual changes. |
useStore() | The Canvas Zustand store for imperative reads/subscriptions. Unsubscribe listeners you create. |
useLoader(Loader, input, extensions?, onProgress?, cacheKey?) | Suspense asset loading with shared cache; supports preload/clear. Do not dispose a cached asset still used elsewhere. |
useEnvironment(options) | Loads environment textures using files/path/preset options; loading alone does not assign the scene environment. |
useRenderTarget(options) | Renderer-compatible target, canvas-sized by default; overloads accept a square size or width/height. Inspect resizing/disposal behavior before adding another owner. |
useGraph(object) | Memoized named object/material lookup from an Object3D; unrelated to TSL node registries. |
useInstanceHandle(ref) | Escape hatch into R3F reconciler internals; use only when public APIs cannot do the job. |
useBridge, useMutableCallback, useIsomorphicLayoutEffect | Infrastructure exports, not WebGPU resource hooks. Preserve existing integration usage; do not build resource ownership on these helpers. |
once and fromRef are prop helpers, not hooks. translate={once(0, 0, 1)} on a declarative geometry applies a mount-only transform; reconstruction runs it again. It does not memoize allocations. target={fromRef(targetRef)} resolves a ref-dependent prop after the ref is populated; it is not a per-frame subscription. Check installed JSX typings, especially for generic bufferGeometry transforms.
Verify the change
Run the target's type-check and narrowest relevant lint/runtime check. For resource changes, exercise two independent instances, an unrelated re-render, allocation resize/replacement, unmount/remount, and fast refresh if supported. Verify both rendered/compute results and actual cleanup; type-checking alone cannot prove GPU memory was released. Avoid material keys based on newly created node UUIDs, which force avoidable remounts.
Upstream references: TSL hooks, render pipeline, core hooks, v10 migration, TSL HMR. Prefer installed implementation and types when the moving v10 docs disagree.

