The Scene component is a container that groups multiple shapes and other scenes together, allowing you to organize and transform them as a unit.
Basic Scene Usage
Scenes act as groups that can contain Shape children:
import { SwCanvas , Scene , Shape , Rect , Circle } from '@thorvg/react-fiber' ;
function App () {
return (
< SwCanvas width = { 600 } height = { 400 } >
< Scene >
< Shape fill = { [ 255 , 100 , 100 , 255 ] } >
< Rect x = { 50 } y = { 50 } width = { 100 } height = { 100 } />
</ Shape >
< Shape fill = { [ 100 , 100 , 255 , 255 ] } >
< Circle x = { 200 } y = { 100 } radius = { 50 } />
</ Shape >
</ Scene >
</ SwCanvas >
);
}
Scene Props
Scenes support transform properties and opacity. See the type definitions in the source code.
X-axis translation in pixels.
Y-axis translation in pixels.
Rotation angle in degrees.
Opacity value from 0 (transparent) to 255 (opaque).
Why Use Scenes?
Group Transforms Apply transforms to multiple shapes at once without affecting their individual transforms.
Organize Structure Create logical groupings for complex graphics, like UI components or illustration layers.
Manage Opacity Control opacity of multiple shapes together.
Composition Build complex graphics from reusable scene components.
Transforms on a Scene apply to all its children:
< SwCanvas width = { 500 } height = { 400 } >
< Scene x = { 100 } y = { 100 } rotation = { 15 } >
< Shape fill = { [ 255 , 150 , 100 , 255 ] } >
< Rect x = { 0 } y = { 0 } width = { 80 } height = { 80 } />
</ Shape >
< Shape fill = { [ 100 , 200 , 255 , 255 ] } >
< Circle x = { 100 } y = { 40 } radius = { 40 } />
</ Shape >
</ Scene >
</ SwCanvas >
In this example, both shapes are positioned and rotated together as a group.
Nested Scenes
Scenes can contain other scenes, creating a hierarchy:
< SwCanvas width = { 600 } height = { 400 } >
< Scene x = { 50 } y = { 50 } >
< Shape fill = { [ 255 , 100 , 100 , 255 ] } >
< Rect x = { 0 } y = { 0 } width = { 100 } height = { 100 } />
</ Shape >
< Scene x = { 150 } y = { 0 } rotation = { 45 } >
< Shape fill = { [ 100 , 255 , 100 , 255 ] } >
< Rect x = { - 25 } y = { - 25 } width = { 50 } height = { 50 } />
</ Shape >
< Shape fill = { [ 100 , 100 , 255 , 255 ] } >
< Circle x = { 0 } y = { 40 } radius = { 20 } />
</ Shape >
</ Scene >
</ Scene >
</ SwCanvas >
Transforms accumulate through the hierarchy - the inner scene’s position and rotation are relative to its parent scene.
Nested scenes are useful for creating complex compositions where different parts need independent transforms that also move together as a larger group.
Scene Opacity
Control the opacity of all children at once:
import { useState } from 'react' ;
import { SwCanvas , Scene , Shape , Rect , Circle } from '@thorvg/react-fiber' ;
function FadingGroup () {
const [ opacity , setOpacity ] = useState ( 255 );
return (
< div >
< SwCanvas width = { 400 } height = { 300 } >
< Scene opacity = { opacity } >
< Shape fill = { [ 255 , 100 , 100 , 255 ] } >
< Rect x = { 50 } y = { 50 } width = { 100 } height = { 100 } />
</ Shape >
< Shape fill = { [ 100 , 100 , 255 , 255 ] } >
< Circle x = { 150 } y = { 100 } radius = { 50 } />
</ Shape >
</ Scene >
</ SwCanvas >
< input
type = "range"
min = "0"
max = "255"
value = { opacity }
onChange = { e => setOpacity ( Number ( e . target . value )) }
/>
</ div >
);
}
Scene opacity is multiplied with the opacity of individual shapes. If a scene has opacity 128 (50%) and a child shape has opacity 128, the resulting opacity is 25%.
Reusable Scene Components
Scenes enable component-based composition:
import { Scene , Shape , Rect , Circle } from '@thorvg/react-fiber' ;
function House ({ x , y , color } : { x : number ; y : number ; color : [ number , number , number , number ] }) {
return (
< Scene x = { x } y = { y } >
{ /* House body */ }
< Shape fill = { color } >
< Rect x = { 0 } y = { 20 } width = { 60 } height = { 50 } />
</ Shape >
{ /* Roof */ }
< Shape fill = { [ 150 , 75 , 0 , 255 ] } >
< Path
commands = { [ PathCommand . MoveTo , PathCommand . LineTo , PathCommand . LineTo , PathCommand . Close ] }
points = { [
{ x: 30 , y: 0 },
{ x: 0 , y: 20 },
{ x: 60 , y: 20 }
] }
/>
</ Shape >
{ /* Window */ }
< Shape fill = { [ 200 , 220 , 255 , 255 ] } >
< Rect x = { 20 } y = { 35 } width = { 20 } height = { 20 } />
</ Shape >
</ Scene >
);
}
function Village () {
return (
< SwCanvas width = { 600 } height = { 400 } >
< House x = { 50 } y = { 100 } color = { [ 200 , 100 , 100 , 255 ] } />
< House x = { 150 } y = { 120 } color = { [ 100 , 150 , 200 , 255 ] } />
< House x = { 280 } y = { 90 } color = { [ 150 , 200 , 100 , 255 ] } />
</ SwCanvas >
);
}
Dynamic Scenes
Scenes work seamlessly with React’s dynamic rendering:
import { useState } from 'react' ;
import { SwCanvas , Scene , Shape , Circle } from '@thorvg/react-fiber' ;
function DynamicDots () {
const [ dots , setDots ] = useState ([
{ x: 100 , y: 100 , color: [ 255 , 0 , 0 , 255 ] as [ number , number , number , number ] },
{ x: 200 , y: 150 , color: [ 0 , 255 , 0 , 255 ] as [ number , number , number , number ] },
]);
const addDot = () => {
setDots ( prev => [
... prev ,
{
x: Math . random () * 400 ,
y: Math . random () * 300 ,
color: [
Math . floor ( Math . random () * 255 ),
Math . floor ( Math . random () * 255 ),
Math . floor ( Math . random () * 255 ),
255
] as [ number , number , number , number ]
}
]);
};
return (
< div >
< SwCanvas width = { 400 } height = { 300 } >
< Scene >
{ dots . map (( dot , i ) => (
< Shape key = { i } fill = { dot . color } x = { dot . x } y = { dot . y } >
< Circle x = { 0 } y = { 0 } radius = { 20 } />
</ Shape >
)) }
</ Scene >
</ SwCanvas >
< button onClick = { addDot } > Add Dot </ button >
</ div >
);
}
Scene Order and Layering
Shapes and scenes are rendered in the order they appear in the component tree. Later children are drawn on top:
< SwCanvas width = { 400 } height = { 300 } >
{ /* This red square is drawn first (bottom layer) */ }
< Shape fill = { [ 255 , 0 , 0 , 255 ] } x = { 50 } y = { 50 } >
< Rect x = { 0 } y = { 0 } width = { 100 } height = { 100 } />
</ Shape >
{ /* This blue circle is drawn second (middle layer) */ }
< Shape fill = { [ 0 , 0 , 255 , 255 ] } x = { 100 } y = { 100 } >
< Circle x = { 0 } y = { 0 } radius = { 60 } />
</ Shape >
{ /* This green square is drawn last (top layer) */ }
< Shape fill = { [ 0 , 255 , 0 , 255 ] } x = { 150 } y = { 150 } >
< Rect x = { 0 } y = { 0 } width = { 100 } height = { 100 } />
</ Shape >
</ SwCanvas >
Scenes follow the same layering rules - shapes within later scenes appear on top of shapes in earlier scenes.
Scenes are implemented efficiently in the reconciler (the reconciler implementation):
if ( parentInstance . scene && childInstance . paint ) {
parentInstance . scene . push ( childInstance . paint );
}
Lightweight Scenes have minimal overhead - they’re simple containers in the rendering tree.
Efficient Updates Only changed properties trigger re-renders, not the entire scene.
Common Patterns
Create reusable graphic components using Scenes: function Button ({ x , y , label } : { x : number ; y : number ; label : string }) {
return (
< Scene x = { x } y = { y } >
< Shape fill = { [ 100 , 150 , 255 , 255 ] } >
< Rect x = { 0 } y = { 0 } width = { 120 } height = { 40 } rx = { 5 } ry = { 5 } />
</ Shape >
{ /* Add text rendering when available */ }
</ Scene >
);
}
Animate entire groups by animating the Scene transform: function AnimatedGroup () {
const [ rotation , setRotation ] = useState ( 0 );
useEffect (() => {
const interval = setInterval (() => {
setRotation ( r => ( r + 1 ) % 360 );
}, 16 );
return () => clearInterval ( interval );
}, []);
return (
< Scene x = { 200 } y = { 150 } rotation = { rotation } >
< Shape fill = { [ 255 , 100 , 100 , 255 ] } >
< Rect x = { - 50 } y = { - 20 } width = { 100 } height = { 40 } />
</ Shape >
< Shape fill = { [ 100 , 100 , 255 , 255 ] } >
< Circle x = { 0 } y = { 0 } radius = { 10 } />
</ Shape >
</ Scene >
);
}
Show/hide entire groups: function ConditionalScene ({ showDetails } : { showDetails : boolean }) {
return (
< SwCanvas width = { 400 } height = { 300 } >
< Shape fill = { [ 255 , 100 , 100 , 255 ] } >
< Rect x = { 50 } y = { 50 } width = { 100 } height = { 100 } />
</ Shape >
{ showDetails && (
< Scene x = { 200 } y = { 100 } >
< Shape fill = { [ 100 , 200 , 100 , 255 ] } >
< Circle x = { 0 } y = { 0 } radius = { 30 } />
</ Shape >
< Shape fill = { [ 100 , 100 , 200 , 255 ] } >
< Rect x = { 50 } y = { - 15 } width = { 60 } height = { 30 } />
</ Shape >
</ Scene >
) }
</ SwCanvas >
);
}
Build layout helpers using Scenes: function HStack ({ children , spacing = 10 , x = 0 , y = 0 } : {
children : React . ReactNode [];
spacing ?: number ;
x ?: number ;
y ?: number ;
}) {
return (
< Scene x = { x } y = { y } >
{ React . Children . map ( children , ( child , i ) => (
< Scene x = { i * spacing } y = { 0 } >
{ child }
</ Scene >
)) }
</ Scene >
);
}
// Usage:
< HStack spacing = { 120 } x = { 50 } y = { 100 } >
< Shape fill = { [ 255 , 0 , 0 , 255 ] } >< Rect width = { 100 } height = { 100 } /></ Shape >
< Shape fill = { [ 0 , 255 , 0 , 255 ] } >< Circle radius = { 50 } /></ Shape >
< Shape fill = { [ 0 , 0 , 255 , 255 ] } >< Rect width = { 100 } height = { 100 } /></ Shape >
</ HStack >
Scenes can only contain Shape components and other Scene components. They cannot directly contain geometry children like Rect, Circle, or Path.