<!--
{
  "availability" : [
    "iOS: 18.0.0 -",
    "iPadOS: 18.0.0 -",
    "macCatalyst: 18.0.0 -",
    "macOS: 15.0.0 -",
    "tvOS: 26.0.0 -",
    "visionOS: 1.0.0 -"
  ],
  "documentType" : "symbol",
  "framework" : "RealityKit",
  "identifier" : "/documentation/RealityKit/PortalComponent",
  "metadataVersion" : "0.1.0",
  "role" : "Structure",
  "symbol" : {
    "kind" : "Structure",
    "modules" : [
      "RealityKit"
    ],
    "preciseIdentifier" : "s:17RealityFoundation15PortalComponentV"
  },
  "title" : "PortalComponent"
}
-->

# PortalComponent

A component that turns mesh surfaces into portals to a different world.

```
struct PortalComponent
```

## Overview

A RealityKit portal defines a way to look into a different, immersive world. You define an entity as a portal
when it also has a [`ModelComponent`](/documentation/RealityKit/ModelComponent) that contains a mesh part with [`PortalMaterial`](/documentation/RealityKit/PortalMaterial).

To create a portal, set its [`targetEntity`](/documentation/RealityKit/PortalComponent/targetEntity) property to an entity with a
[`WorldComponent`](/documentation/RealityKit/WorldComponent). Entities under that world only render within the portal.

### Quick start

On iOS 27, macOS 27, tvOS 27, visionOS 27, and later, [`makePortal(surfaceStyle:boundaryStyle:boundaryMode:)`](/documentation/RealityKit/PortalComponent/makePortal(surfaceStyle:boundaryStyle:boundaryMode:))
creates a configured portal entity and its target world entity in a single call:

```swift
let portal = PortalComponent.makePortal(
   surfaceStyle: .init(width: 0.5, height: 0.5),
   boundaryStyle: .infinitePlane(),
   boundaryMode: .clippingAndCrossing
)

content.add(portal.worldEntity)
content.add(portal.portalEntity)
```

To configure existing entities, use
[`configure(world:portalEntity:surfaceStyle:boundaryStyle:boundaryMode:)`](/documentation/RealityKit/PortalComponent/configure(world:portalEntity:surfaceStyle:boundaryStyle:boundaryMode:)).

### Manual construction

On earlier OS releases, or when you need direct control over the portal’s mesh, material, or
boundary geometry, set the components yourself:

```swift
let world = Entity()
world.components.set(WorldComponent())

let portal = Entity()
portal.components.set(
   ModelComponent(
       mesh: .generatePlane(width: 0.5, height: 0.5, cornerRadius: 0.1),
       materials: [PortalMaterial()]
   )
)
portal.components.set(PortalComponent(target: world))

content.add(world)
content.add(portal)
```

### Clipping and Crossing

You can enable clipping by configuring [`clippingMode`](/documentation/RealityKit/PortalComponent/clippingMode-swift.property) to something
other than [`PortalComponent.ClippingMode.disabled`](/documentation/RealityKit/PortalComponent/ClippingMode-swift.enum/disabled).
For example, you can use [`PortalComponent.ClippingMode.plane(_:)`](/documentation/RealityKit/PortalComponent/ClippingMode-swift.enum/plane(_:)) to clip portal content to an
infinite half-space, or [`PortalComponent.ClippingMode.volume(_:)`](/documentation/RealityKit/PortalComponent/ClippingMode-swift.enum/volume(_:)) to clip to a box-bounded
region of space. Clipping prevents portal world content from rendering beyond
the portal boundary and causing depth confusion.

Entities inside the portal world with a [`PortalCrossingComponent`](/documentation/RealityKit/PortalCrossingComponent) can freely
cross in and out of the portal boundary in any of the following platforms:

- iOS 18 and later
- macOS 15 and later
- visionOS 2 and later

You can enable the crossing feature by configuring [`crossingMode`](/documentation/RealityKit/PortalComponent/crossingMode-swift.property)
to something other than [`PortalComponent.CrossingMode.disabled`](/documentation/RealityKit/PortalComponent/CrossingMode-swift.enum/disabled).
Such as [`PortalComponent.CrossingMode.plane(_:)`](/documentation/RealityKit/PortalComponent/CrossingMode-swift.enum/plane(_:)) for an infinite-plane crossing, or
[`PortalComponent.CrossingMode.volume(_:)`](/documentation/RealityKit/PortalComponent/CrossingMode-swift.enum/volume(_:)) for a box-bounded crossing.

```swift
let world = Entity()
world.components.set(WorldComponent())

// Create an entity that doesn't cross beyond the portal bounds.
let notCrossing = Entity()

// Create an entity that crosses beyond the portal bounds.
let willCross = Entity()
willCross.components.set(PortalCrossingComponent())

world.addChild(notCrossing)
world.addChild(willCross)

// Set up a crossable portal, without a near clip.
let portal = Entity()
portal.components.set(
   ModelComponent(
       mesh: .generatePlane(width: 0.5, height: 0.5, cornerRadius: 0.1),
       materials: [PortalMaterial()]
   )
)
var portalComp = PortalComponent(target: world)
portalComp.clippingMode = .plane(.positiveZ)
portalComp.crossingMode = .plane(.positiveZ)
portal.components.set(portalComp)

content.add(world)
content.add(portal)
```

The spaceships below have a [`PortalCrossingComponent`](/documentation/RealityKit/PortalCrossingComponent).

![Four videos of space ships poking out of portals looking into outer space. Each video is in a grid formation. The top left video have both clipping and crossing mode disabled, so the space ship is being bounded by the portal geometry. The bottom left video have clipping mode set to plane and crossing mode disabled, so the space ship is clipped by the portal plane. The two videos on the right column have crossing mode enabled, so the spaceship is rendered outside the portal geometry.](videos/com.apple.RealityKit/portal-crossing-video~dark.mov)

The spaceships below *don’t* have a [`PortalCrossingComponent`](/documentation/RealityKit/PortalCrossingComponent).

![Four videos of space ships poking out of portals looking into outer space. Each video is in a grid formation. The two videos on the top row have clipping mode disabled so the space ships are bounded by the portal geometry. The two videos on the bottom row have clipping mode enabled so the space ships are clipped by the portal plane.](videos/com.apple.RealityKit/portal-clipping-video.mov)

### Lighting

You define the lighting in a portal world with [`ImageBasedLightComponent`](/documentation/RealityKit/ImageBasedLightComponent) and
[`ImageBasedLightReceiverComponent`](/documentation/RealityKit/ImageBasedLightReceiverComponent).

RealityKit provides a default IBL if you don’t specify one with [`ImageBasedLightReceiverComponent`](/documentation/RealityKit/ImageBasedLightReceiverComponent).

Contents within a portal world don’t receive real-world probe lighting. However, you can achieve a  similar
effect in the portal world using [`VirtualEnvironmentProbeComponent`](/documentation/RealityKit/VirtualEnvironmentProbeComponent).

You can configure this virtual probe lighting contribution with
[`EnvironmentLightingConfigurationComponent`](/documentation/RealityKit/EnvironmentLightingConfigurationComponent).

Dynamic lights, such as [`PointLightComponent`](/documentation/RealityKit/PointLightComponent) and [`DirectionalLightComponent`](/documentation/RealityKit/DirectionalLightComponent),
don’t cross world bounds.

Different lighting environments light the portal crossing entities based on which side of the portal they
are on:

- When inside the portal, the portal world’s lighting lights the entity.
- When outside the portal, the default world’s lighting lights the entity.

## Topics

### Defining the portal volume

[`Volume`](/documentation/RealityKit/PortalComponent/Volume)

A box-shaped region in portal-local space that defines a volumetric portal boundary.

### Configuring lighting blend

[`lightingBlendDistance`](/documentation/RealityKit/PortalComponent/lightingBlendDistance)

The distance over which crossing-entity lighting blends between the host scene and the portal world.

## Relationships

### Conforms To

[`Component`](/documentation/RealityKit/Component)

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)