Lab 037 – Portal in a Window
Learning the basics of how to use PortalComponent to render one scene inside another.
Overview
Is portal fever in the air? Recently both Tom K. and Todd H. shared some interesting exampled with portals. I think portals could be useful in Dark Spaces, so it’s time to learn the basics.
Apple has a great example project and tutorial to cover this topic. This lab is a simplified version of that project. I wanted to reduce it to as few pieces as possible to see how it works.
I used a Window to render the portal in this lab, but we could also use these ideas in Volumes or Spaces. Let’s build the portal step by step.
We’ll start with a RealityView for our window, then add an entity that will serve as the root for content outside of the portal.
struct Lab037: View {
var body: some View {
RealityView { content in
let rootEntity = Entity()
content.add(rootEntity)
}
}
}Next, we’ll add an entity that will act as the root for the content that will appear inside the portal. We’ll use the WorldComponent on this entity.
let portalContentRoot = Entity()
portalContentRoot.components.set(WorldComponent())
rootEntity.addChild(portalContentRoot)Then we need something to render the portal. We’ll use a plane mesh and PortalMaterial. We also need to add a PortalComponent that targets the portalContentRoot.
let portalEntity = ModelEntity(
mesh: .generatePlane(width: 0.4, height: 0.2, cornerRadius: 0.03),
materials: [PortalMaterial()]
)
portalEntity.components.set(PortalComponent(target: portalContentRoot))
rootEntity.addChild(portalEntity)Finally, we need some content for the portal. We’ll use one of the existing lab scenes from Reality Composer Pro.
guard let scene = try? await Entity(named: "TeleportLabs", in: realityKitContentBundle) else { return }
portalContentRoot.addChild(scene)When we put it all together, we get a delightful result. We’ll explore some ideas for using portals in future labs.
Video Demo
visionOS Simulator demo of a portal in a window
Full Lab Code
struct Lab037: View {
var body: some View {
RealityView { content in
// 1. The root for our scene *outside* of the portal
let rootEntity = Entity()
content.add(rootEntity)
// 2. The root for the content that will appear *inside* the portal
let portalContentRoot = Entity()
portalContentRoot.components.set(WorldComponent())
rootEntity.addChild(portalContentRoot)
// 3. An entity that will render the portal
// We need to use PortalMaterial
let portalEntity = ModelEntity(
mesh: .generatePlane(width: 0.4, height: 0.2, cornerRadius: 0.03),
materials: [PortalMaterial()]
)
portalEntity.position.z = -0.175
// We also need to add a PortalComponent that targets the portalContentRoot
portalEntity.components.set(PortalComponent(target: portalContentRoot))
rootEntity.addChild(portalEntity)
// 4. We'll load some content to add to the portalContentRoot
guard let scene = try? await Entity(named: "TeleportLabs", in: realityKitContentBundle) else { return }
portalContentRoot.addChild(scene)
scene.position.y = -1.4
}
}
}Support our work so we can continue to bring you new examples and articles.

Follow Step Into Vision