@react-three/flex
Placing content in THREE.js is hard. @react-three/flex brings the webs flexbox spec to react-three-fiber. It is based on Yoga, Facebook's open source layout engine for react-native.
These demos are real, you can click them! They contain the full code, too.
Usage
Simply create layouts by wrapping your 3D objects in different <Box />
instances inside a <Flex />
container. This way they will be automatically placed in the 3D space following the flexbox specification just like in the DOM.
You can tweak the container and the boxes using standard CSS flex properties, like flexDirection
or justifyContent
for the container and flexGrow
for the boxes. There are also shorthands, like align
and justify
. See the props docs below for more info.
Anchors
When positioning items, react-three-flex
needs to know where the object anchor is: Yoga Layout expects the object position to be relative to the upper left corner, which is the same as the DOM expects.
Most THREE.js geometries, though, are positioned relative to the object center. To tell react-three-flex
that your <Box />
positioning is relative to the center you need to set the centerAnchor
prop to true.
If you nest <Box />
elements, though, you need to set it to false. See Nesting.
Stretching
By default @react-three/flex
controls elements position only. In some cases you may want to control element sizing too. Since @react-three/flex
has no information about how the inner content size works, you need to set your content size manually. To do so @react-three/flex
provides you the container size in two ways:
- Using a children render function:
- Using a hook:
Remember that the useFlexSize
hook works ONLY if your <Box/>
is outside the component.
Invalidation and Reflow
While the DOM's Flexbox has full control over all the changes of the tree, @react-three/flex
runs on React, hence it has no way to know if a children size or shape has changed. For performance reasons Flex layout calculation does not run every frame, and it has to be triggered manually in some cases.
What will trigger a reflow:
<Flexbox/>
props changes (alignItems, size, ...)<Box/>
props changes (flexGrow, margin, ...)<Flexbox/>
and<Box/>
rerenders with children differences
This will NOT cause a reflow!
For every other case (setting size with the useFrame
hook, performing react-spring
animation, or <Box/>
are not rerendered) you'll need to manually cause a reflow, using the useReflow()
hook. Reflows requests are batched every frame so you can call it from hundreds of components without performance issues.
Animation with useFrame():
<Box/>
outside of component:
Sizing
@react-three/flex
differs from DOM Flexbox in that it relies on a parent container for the root flex. It is required to specify its dimensions using size
prop for wrapping and to be responsive.
⚠️ WATCH OUT! Yoga flexbox engine uses whole integer numbers to perform layout calculation to preserve precision - @react-three/flex
multiplies every element size and flex prop by the scaleFactor
of the root flex container. By default it's 100
, and works well for small scenes. If you use a different scene scale, make sure to tweak it accordingly.
Axis Orientation
Another important difference with DOM Flexbox is that you have to specify the plane of the container in 3D. The elements will be positioned in the 2D plane given by the two axes, using width and height calculated along the two axes.
The 2D flex container width and height will be calculated by looking at the size
prop with respect of the chosen axes (100
for xy
and 200
for y
in this example).
The default plane is xy
, the other possibilites are yz
and xz
.
Margin and Padding
For every <Flex />
and <Box />
component you can specify the margin and padding like in DOM elements.
Nesting
Since a <Flex />
component works the same way as a DOM one, you can easily make complex layouts by nesting flex containers.
Measuring the container
When building responsive layouts you might need to synchronize the size of the 3D Flex container with the DOM, for example to synchronize scroll position or to modify the height of a scroll container.
To make it easier, you can use the onReflow
prop on the root <Flex>
component that will be called every time the flex layout is recalculated - e.g. when any content changes.
API
You can find a full list of props here.
Or you can pass a function as children:
Flexbox props
Both <Flex/>
and <Box />
components share the same Flexbox props API from Yoga. The library also provides string and number inputs for convenience and shorthands.
Example: