<Portal>
A component that renders its children as children of an object that can exist anywhere in your Threlte application.
You can use the prop id to render into a <PortalTarget>.
Although Portals are extremely helpful in certain situations, it can be hard to reason about them at times. It’s recommended to use them sparingly.
You might not need <Portal>
Section titled “You might not need <Portal>”Some objects such as the THREE.DirectionalLightHelper
need to be added to the scene itself to be functional.
In this case, the portal target is easily accessible, and passing the scene object to the <T> component’s attach property can accomplish everything we need.
<script lang="ts"> import { Canvas } from '@threlte/core' import Scene from './Scene.svelte'</script>
<div> <Canvas> <Scene /> </Canvas></div>
<style> div { height: 100%; background-color: rgb(47 125 198 / 0.2); }</style><script lang="ts"> import { T, useThrelte } from '@threlte/core' import { Grid, OrbitControls, TransformControls } from '@threlte/extras'
const { scene } = useThrelte()</script>
<T.PerspectiveCamera position={[10, 10, 10]} makeDefault fov={30}> <OrbitControls enableZoom={false} /></T.PerspectiveCamera>
<Grid />
<!-- Red main light --><T.DirectionalLight color="#FE3D00" intensity={1} position={[1.5, 2, 0.5]}> {#snippet children({ ref })} <T.DirectionalLightHelper attach={scene} args={[ref]} > {#snippet children({ ref: helperA })} <TransformControls object={ref} onobjectChange={() => helperA.update()} /> {/snippet} </T.DirectionalLightHelper> {/snippet}</T.DirectionalLight>
<!-- Blue rim light --><T.DirectionalLight intensity={0.5} color="#2F7DC6" position={[-1, -2, 1]}> {#snippet children({ ref })} <T.DirectionalLightHelper attach={scene} args={[ref]} > {#snippet children({ ref: helperB })} <TransformControls object={ref} onobjectChange={() => helperB.update()} /> {/snippet} </T.DirectionalLightHelper> {/snippet}</T.DirectionalLight>
<T.Mesh position.y={0.5}> <T.SphereGeometry /> <T.MeshStandardMaterial color="white" /></T.Mesh>Rendering to a <PortalTarget>
Section titled “Rendering to a <PortalTarget>”For more complex cases where it’s hard to query the target, using <Portal> may be an easier solution.
You can define where a <Portal> should render its children by using the component <PortalTarget>.
<script lang="ts"> import { Canvas } from '@threlte/core' import Scene from './Scene.svelte'</script>
<div> <Canvas> <Scene /> </Canvas></div>
<style> div { height: 100%; background-color: rgb(47 125 198 / 0.2); }</style><script lang="ts"> import { T, useTask } from '@threlte/core' import { Grid, OrbitControls, Portal, PortalTarget } from '@threlte/extras' import { MathUtils } from 'three'
let posX = $state(Math.sin(Date.now() / 1000) * 4)
useTask(() => { posX = Math.sin(Date.now() / 1000) * 4 })</script>
<T.PerspectiveCamera position={[10, 10, 10]} makeDefault fov={30}> <OrbitControls maxPolarAngle={85 * MathUtils.DEG2RAD} minPolarAngle={20 * MathUtils.DEG2RAD} maxAzimuthAngle={45 * MathUtils.DEG2RAD} minAzimuthAngle={-45 * MathUtils.DEG2RAD} enableZoom={false} /></T.PerspectiveCamera>
<Grid />
<T.DirectionalLight position={[5, 10, 3]} />
<T.Object3D position.x={posX} position.y={0.5}> <PortalTarget id="trail" /></T.Object3D>
<Portal id="trail"> <T.Mesh> <T.BoxGeometry /> <T.MeshStandardMaterial color="#FE3D00" /> </T.Mesh>
<T.Group position.y={1}> <PortalTarget id="top" /> </T.Group></Portal>
<Portal id="top"> <T.Mesh> <T.BoxGeometry /> <T.MeshStandardMaterial color="#2F7DC6" /> </T.Mesh></Portal>