Canvas initializes the three.js rendering context and is the root of your 3D scene. Every <T.*> and <Entity/> component must be a child of it.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
camera | Partial<Props<Camera>> | Camera | new PerspectiveCamera() | The scene camera — partial props for the default camera, or an instance you built. |
gl | object | ((canvas: HTMLCanvasElement) => Renderer) | Renderer | new WebGLRenderer({ alpha: true }) | How the renderer is built. See The gl prop. |
scene | Partial<Props<Scene>> | Scene | new Scene() | Settings for the scene, or an existing Scene. |
raycaster | Partial<Props<EventRaycaster>> | EventRaycaster | Raycaster | new CursorRaycaster() | The raycaster used for pointer events. |
shadows | boolean | "basic" | "percentage" | "soft" | "variance" | WebGLRenderer["shadowMap"] | off | Enables shadows. See Rendering defaults for the string mapping. |
orthographic | boolean | false | Use an OrthographicCamera for the default camera. |
linear | boolean | false | Use a linear output color space instead of sRGB. |
flat | boolean | false | Disable tone mapping (NoToneMapping). |
frameloop | "always" | "demand" | "never" | "always" | When to render: every frame, only on request, or never. |
fallback | JSX.Element | — | Shown while content loads asynchronously. |
style | JSX.CSSProperties | — | CSS for the canvas container. |
class | string | — | CSS class for the canvas container. |
ref | RefWithCleanup<Context> | — | Receives the Context once the renderer is created, so code outside <Canvas> can reach it. A callback ref may return a cleanup that runs when the Canvas unmounts; createXR uses this. |
| event handlers | Partial<CanvasEventHandlers> | — | Any event handler, firing after the event bubbles through the whole scene (e.g. onClick, onClickMissed). |
Exact type
interface CanvasProps extends ParentProps<Partial<CanvasEventHandlers>> { ref?: RefWithCleanup<Context> // Context | ((context: Context) => void | (() => void)) camera?: Partial<Props<PerspectiveCamera> | Props<OrthographicCamera>> | Camera fallback?: JSX.Element gl?: // The flat-object branch collapses to `never` when Register narrows // ResolvedRenderer away from WebGLRenderer — see "Narrowing the // renderer type project-wide" below. | (WebGLRenderer extends ResolvedRenderer ? Partial<Props<WebGLRenderer> & WebGLRendererParameters> : never) | ((canvas: HTMLCanvasElement) => ResolvedRenderer) | ResolvedRenderer scene?: Partial<Props<Scene>> | Scene raycaster?: Partial<Props<EventRaycaster>> | EventRaycaster | Raycaster shadows?: boolean | "basic" | "percentage" | "soft" | "variance" | WebGLRenderer["shadowMap"] orthographic?: boolean linear?: boolean flat?: boolean frameloop?: "never" | "demand" | "always" style?: JSX.CSSProperties class?: string // Plus all event handlers (Partial<CanvasEventHandlers>)}CanvasProps is exported as a type: import type { CanvasProps } from "solid-three".
Usage
<Canvas camera={{ position: [0, 0, 5], fov: 75 }} shadows="soft" gl={{ antialias: true }} onClickMissed={() => console.log("Clicked empty space")}> {/* Your 3D scene */}</Canvas>Rendering defaults
A few defaults are derived rather than passed directly:
-
Tone mapping —
ACESFilmicToneMapping, orNoToneMappingwhenflatis set. -
Output color space —
SRGBColorSpace, orLinearSRGBColorSpacewhenlinearis set. -
Shadow type —
shadows={true}usesPCFSoftShadowMap. The string values map to three's constants:shadowsShadow map "basic"BasicShadowMap"percentage"PCFShadowMap"soft"PCFSoftShadowMap"variance"VSMShadowMap
Tone-mapping and color-space defaults are applied only to renderers that expose those fields — they're skipped silently for renderers like SVGRenderer.
The gl prop
gl controls how the renderer is built. It accepts three shapes:
| Shape | Example | Use it for |
|---|---|---|
| Properties object | gl={{ antialias: true, toneMapping: ACESFilmicToneMapping }} | Tweaking the default WebGLRenderer. Mixes constructor flags (antialias, alpha, powerPreference) with instance props (toneMapping). |
| Factory | gl={canvas => new WebGPURenderer({ canvas })} | Choosing a different renderer — WebGPURenderer, SVGRenderer, CSS2DRenderer, a custom one. |
| Instance | gl={myRenderer} | A renderer you already built. |
With the properties object, instance props stay reactive — set them after construction and update them anytime. Constructor flags are the exception, below.
Constructor args are applied once
Constructor-only flags — antialias, alpha, powerPreference, depth, stencil, preserveDrawingBuffer — are passed to new WebGLRenderer({ ... }) once, at first construction, and never re-read. Updating them later does not rebuild the renderer or recreate the WebGL context, and it can't: canvas.getContext("webgl2") is idempotent — the browser returns the same context, with the original flags, on every call. solid-three logs a warning when it detects a reactive change to one of these flags.
If you genuinely need to change one at runtime, unmount and remount the <Canvas> — for example, gate it behind a <Show> keyed on the value you're swapping. That gives you a fresh <canvas> element, and therefore a fresh context.
Custom renderers
gl accepts anything in the Renderer union: three's WebGLRenderer or WebGPURenderer, three's DOM-based renderers (SVGRenderer, CSS2DRenderer, CSS3DRenderer), or a custom renderer matching the structural RendererLike interface.
import { WebGPURenderer } from "three/webgpu";<Canvas gl={canvas => new WebGPURenderer({ canvas })}>{/* scene */}</Canvas>solid-three awaits renderer.init() before the first frame, so you don't need to await WebGPU setup yourself.
Narrowing the renderer type project-wide
By default useThree().gl is typed as the open SupportedRenderer union. Declare your concrete renderer once and the type narrows everywhere — both useThree().gl reads and <Canvas gl> assignments:
import type { WebGPURenderer } from "three/webgpu"
declare module "solid-three" { interface Register { renderer: WebGPURenderer }}After this:
function Scene() { const three = useThree() three.gl.init() // ✓ no narrowing needed — `gl` is WebGPURenderer three.gl.toneMapping // ✓ typed}
<Canvas gl={canvas => new WebGPURenderer({ canvas })}> {/* ✓ */}<Canvas gl={canvas => new WebGLRenderer({ canvas })}> {/* ✗ type error */}<Canvas gl={{ toneMapping: ACESFilmicToneMapping }}> {/* ✗ type error — the config shorthand only builds a default WebGLRenderer */}Renderer types
/** * Anything <Canvas> accepts as a renderer: a concrete three renderer * (gets full three typing for `xr` / `shadowMap` etc.) or a custom * structural `RendererLike`. */type SupportedRenderer = WebGLRenderer | WebGPURenderer | RendererLike
/** Effective renderer type — narrowed by user `Register` augmentation if provided. */type ResolvedRenderer = Register extends { renderer: infer R } ? R : WebGLRenderer
/** * Module-augmentation point. Declare your concrete renderer choice in a * project-local `.d.ts` and `useThree().gl`, `Context.gl`, and the * `<Canvas gl>` prop all type-narrow project-wide. */interface Register {}
/** * Minimal structural interface for renderers — three's DOM-based renderers * (SVGRenderer, CSS2DRenderer, CSS3DRenderer) and user-built renderers fit * this. Concrete three renderers (WebGLRenderer, WebGPURenderer) * structurally satisfy it too, but the `SupportedRenderer` union prefers their * exact types so WebGL-specific surface remains reachable. */interface RendererLike { render(scene: any, camera: any): void setSize(width: number, height: number, updateStyle?: boolean): void domElement: Element setPixelRatio?(value: number): void getPixelRatio?(): number xr?: WebGLRenderer["xr"] | WebGPURenderer["xr"] shadowMap?: WebGLRenderer["shadowMap"] | WebGPURenderer["shadowMap"] init?(): Promise<void> hasInitialized?(): boolean}See also
useThree— read the renderer, scene, camera, and more from inside the canvas.- Raycasters — the raycaster types accepted by the
raycasterprop. - Events overview — the handlers you can pass to
<Canvas>.
Last updated: 6/8/26, 11:20 AM