Registering a Reality Composer Pro custom component
Expose a custom component to the Reality Composer Pro editor and RealityKit Scripting.
Overview
Reality Composer Pro’s built-in components cover common behavior, but your project’s own data — game state, gameplay parameters, or simulation values — doesn’t always map onto an existing component. You extend Reality Composer Pro by defining a custom component and registering it with Reality Composer Pro. The component then behaves like a built-in component in the editor and in the Inspector, and it becomes available to tools such as Script Graph’s Get Component, Set Component, and Has Component nodes.
This article walks through defining a custom RotationComponent (the same component used in ExampleApp/ContentView.swift) and then registering the component with Reality Composer Pro’s editor. After you register the component, designers can add and edit it visually. Registering it with RealityKit Scripting also lets JavaScript scripts running via ScriptingComponent read and write it.
[Image]
Review the Reality Composer Pro plugin interface
The RealityComposerPro Swift package defines the plugin protocol Reality Composer Pro loads at runtime:
RealityComposerProPluginConform to this and implement
setup(context:)to register your types.shutdown()is optional (default no-op).RealityComposerProContextPassed into
setup(context:). It exposes:registerComponent<ComponentType: Component & Codable>(_:)registerComponent<ComponentType: Component & Codable>(_:defaultValue:)registerAction<ActionType: EntityAction>(_:)(not used/out of scope for this article)registerSystem(_:)
passRetained()Boxes your plugin instance into an opaque pointer. Use
Unmanaged.passRetained(_:).toOpaque()from the standard library, not a protocol member.@_cdecl("createRealityComposerProPlugin")A free function marked with this attribute is the entry point Reality Composer Pro looks for when loading the plugin bundle.
registerComponent requires Component and Codable — Reality Composer Pro needs to serialize your component into .usda/scene files, which Inspectable (RealityKit Scripting’s requirement) doesn’t provide by itself. A component destined for both worlds needs to conform to both.
Define the custom rotation component
This mirrors the component from ExampleApp/ContentView.swift, with Codable added so you can also register it with Reality Composer Pro:
import RealityKit
import RealityKitScripting
public struct RotationComponent: Component, Codable {
var rotationSpeed: Float
var rotationAxis: SIMD3<Float> = .init(0, 1, 0)
var isPaused: Bool = false
// Wobble properties (exposed via extension in Overview.md's "Extending Existing Types")
var wobbleEnabled: Bool = false
var wobbleAmplitude: Float = 0.1
var wobbleSpeed: Float = 5.0
init(rotationSpeed: Float) {
self.rotationSpeed = rotationSpeed
}
}
extension RotationComponent: Inspectable {}Give it a scripting schema, following the static var schema convention used throughout RealityKit Scripting (for example, CoreComponent.schema in the Hot Reload example):
extension RotationComponent {
static var schema: TypeSchema<RotationComponent> {
TypeSchema<RotationComponent>("RotationComponent") {
Constructor(body: RotationComponent.init(rotationSpeed:))
StoredProperty("rotationSpeed", keyPath: \RotationComponent.rotationSpeed)
StoredProperty("rotationAxis", keyPath: \RotationComponent.rotationAxis)
StoredProperty("isPaused", keyPath: \RotationComponent.isPaused)
StoredProperty("wobbleEnabled", keyPath: \RotationComponent.wobbleEnabled)
StoredProperty("wobbleAmplitude", keyPath: \RotationComponent.wobbleAmplitude)
StoredProperty("wobbleSpeed", keyPath: \RotationComponent.wobbleSpeed)
}
}
}import RealityKit
import RealityKitScripting
import RealityKitScriptingMacros
@Scriptable
public struct RotationComponent: Component, Codable {
public var rotationSpeed: Float
public var rotationAxis: SIMD3<Float> = .init(0, 1, 0)
public var isPaused: Bool = false
public var wobbleEnabled: Bool = false
public var wobbleAmplitude: Float = 0.1
public var wobbleSpeed: Float = 5.0
public init(rotationSpeed: Float) {
self.rotationSpeed = rotationSpeed
}
}Register the custom component with the plugin
Implement RealityComposerProPlugin and register the component twice in setup(context:): once with the Reality Composer Pro context (editor support), and once with ScriptingRuntime (RealityKit Scripting, for scripting support). In the following example, RotationComponent.schema refers to the manually-defined schema. If you use @Scriptable (shown in the previous section), substitute RotationComponent.SchemaProvider.schema.
import RealityComposerPro
import RealityKitScripting
final class RotationPlugin: RealityComposerProPlugin {
func setup(context: any RealityComposerProContext) {
// 1. Register with Reality Composer Pro so designers can add/edit
// RotationComponent in the Reality Composer Pro Inspector, and
// so it round-trips through .usda/.reality scene files.
context.registerComponent(RotationComponent.self, defaultValue: RotationComponent(rotationSpeed: 1))
// 2. Register with RealityKitScripting so scripts (via ScriptingComponent)
// can read/write RotationComponent at runtime.
let scriptingConfig = ScriptingRuntime.Configuration { _ in
let module = Module("RotationPlugin") {
RotationComponent.schema
}
return [module]
}
do {
try ScriptingRuntime.addConfiguration(scriptingConfig)
} catch {
assertionFailure("Failed to add RealityKitScripting configuration: \(error)")
}
}
}
@_cdecl("createRealityComposerProPlugin")
public func createRealityComposerProPlugin() -> UnsafeMutableRawPointer {
Unmanaged.passRetained(RotationPlugin()).toOpaque()
}ScriptingRuntime.addConfiguration initializes ScriptingRuntime if needed and reloads immediately, so the schema is available as soon as the plugin loads — no separate startup step required.
Compare what each registration provides
The two registration calls serve different audiences and run at different times. See the following comparison.
Description |
|
|
|---|---|---|
Purpose | Exposes component to Reality Composer Pro | Exposes component to RealityKit Scripting (JavaScript via |
Who uses it | Designers (scene authoring) | Developers writing JavaScript scripts, at runtime |
What it enables | Component appears in picker/Inspector; properties editable visually; saved into scene file |
|
When it runs | Edit time | Runtime |
Underlying type |
|
|