Develop

Custom Components sample overview

Updated: May 8, 2026

Overview

This sample demonstrates how to define custom Entity Component System (ECS) components using XML schemas, implement systems that process those components, and use custom components in both runtime code and scenes authored in Meta Spatial Editor. If you want to extend the Spatial SDK’s built-in component library with domain-specific data and behavior, this sample shows you the complete pattern.

Learning objectives

Complete this guide to learn how to:
  • Define custom components using XML schema with typed attributes (entity references, booleans, enums, floats, vectors)
  • Implement systems that query and process custom components every frame
  • Register custom components and systems with the component and system managers
  • Attach custom components to entities at runtime via code
  • Author custom components on scene entities in Meta Spatial Editor
  • Bridge editor-authored components and code-defined components using config.json

Requirements

You need a Meta Quest device running Horizon OS and Android Studio configured for Spatial SDK development. For complete setup instructions, see Getting started with Spatial SDK. For build prerequisites and SDK version requirements, see the sample README.

Get started

Clone or download the Meta Spatial SDK Samples repository from GitHub. Open the CustomComponentsSample project in Android Studio, connect your Quest device, and run the app. The sample loads a 3D collaboration room with a robot that tracks a basketball and a welcome panel that rotates to face the viewer.
For detailed build and deployment instructions, see the sample README.

Explore the sample

The sample includes custom component definitions, system implementations, scene files, and Compose UI panels.
File / SceneWhat it demonstratesKey concepts
Custom component schema with typed attributes and enum definition
XML component schema, EntityAttribute, BooleanAttribute, EnumAttribute, FloatAttribute, Vector3Attribute, default values
Registration of custom components and systems, loading scenes, attaching components at runtime
componentManager.registerComponent(), systemManager.registerSystem(), GLXF inflation, runtime component attachment
ECS system that processes LookAt components every frame
SystemBase, execute(), Query.where, smooth rotation with slerp(), viewer pose
Custom debug metrics for tracking rotation state
OVRMetricsGroup, position overlays, pitch/yaw/roll graphs
Compose UI panel with spatial theme
ComposeViewPanelRegistration, SpatialTheme, UISet library
Scene-authored custom component on panel node, grabbable entities
Editor-authored LookAt component, lookAtHead: true, Y-axis rotation, Grabbable components
Configuration linking Meta Spatial Editor to custom component schemas
spatial.editor.customComponentXmlsPath, editor-code bridge

Runtime behavior

When you run the sample, you see a 3D collaboration room environment with a robot model and a basketball. The robot continuously rotates to face the basketball, smoothly interpolating its orientation rather than snapping instantly. You can grab and move the basketball (it has a Grabbable component), and the robot tracks it in real time. A welcome panel displays “Custom Components Sample” with descriptive text and always faces your head using Y-axis-only rotation, so it stays upright while smoothly rotating to follow you as you move around the room.

Key concepts

Component definition via XML schema

The sample defines the LookAt component in LookAt.xml using an XML schema. The schema specifies typed attributes with default values:
<Component name="LookAt">
  <EntityAttribute name="target" />
  <BooleanAttribute name="lookAtHead" defaultValue="false" />
  <EnumAttribute name="axis" defaultValue="LookAtAxis.ALL" />
  <!-- plus FloatAttribute "speed" and Vector3Attribute "offset" -->
The XML defines a LookAtAxis enum with two values (ALL and Y) and several typed attributes: an entity reference for the target, a boolean for tracking the viewer’s head, an enum for rotation constraints, a float for interpolation speed, and a Vector3 for position offset. The Spatial SDK Gradle plugin generates a Kotlin class from this schema at build time.

System implementation

The LookAtSystem extends SystemBase and overrides execute(), which the SDK calls every frame. The system queries for entities that have both LookAt and Transform components:
override fun execute() {
  Query.where { has(LookAt.id, Transform.id) }.eval().forEach { entity ->
    val lookAt = entity.getComponent<LookAt>()
    // ... compute rotation and update transform
The system reads the LookAt component to determine the target, calculates the desired rotation using Quaternion.lookRotation() or Quaternion.lookRotationAroundY() based on the axis enum, and applies smooth interpolation via slerp(). It writes the updated rotation back to the entity’s Transform component using entity.setComponent().

Registration

The sample registers the custom component and system in CustomComponentsSampleActivity.onCreate() after calling super.onCreate():
componentManager.registerComponent<LookAt>(LookAt.Companion)
systemManager.registerSystem(LookAtSystem())
Both componentManager and systemManager are inherited properties from AppSystemActivity. Registration makes the component type available to both runtime code and the Meta Spatial Editor. The component must be registered before any entities using it are loaded or created, which is why registration happens early in onCreate().

Dual usage: runtime versus scene-authored components

The sample demonstrates two patterns for using custom components. At runtime, the activity attaches a LookAt component to the robot entity after loading the scene:
robot.setComponent(LookAt(basketBall, offset = Vector3(x = 0f, y = 180f, z = 0f)))
Scene-authored usage appears in Main.scene, where the panel node has a LookAt component with lookAtHead: true and axis: 1 (Y-axis only). This demonstrates that custom components work identically whether attached in code or authored in the editor.

config.json bridging editor and code

The config.json file tells Meta Spatial Editor where to find custom component XML schemas:
"spatial.editor.customComponentXmlsPath": ["../src/main/components/"]
This configuration enables the editor to discover the LookAt component definition and expose it in the component palette. Without this bridge, the editor cannot author custom components defined in your codebase. If you add new component XML files to a different directory, update this path to include them.

Extend the sample

  • Modify the LookAt.xml schema to add a maxDistance float attribute that disables rotation when the target is beyond a threshold, and implement the distance check in LookAtSystem.execute().
  • Create a second custom component (for example, Orbit) that makes an entity circle around a target at a configurable radius and speed, with a corresponding OrbitSystem.
  • Add new components to the robot or basketball in Main.scene using Meta Spatial Editor to see how editor-authored and code-authored components interact on the same entity.