Create custom components
Updated: Aug 29, 2025
Custom components store domain-specific data that built-in components don’t provide. This guide walks you through creating an UpAndDown component that stores animation parameters, which you’ll then use with a custom system to create animated objects.
Before creating a custom component, ensure you have:
When to create custom components
First check if a
built-in component meets your needs. Spatial SDK provides extensive built-ins like
Transform,
Material,
Mesh,
Grabbable, and many others.
Create custom components for:
- Animation parameters like movement speed, amplitude, duration, easing curves
- Spatial behaviors like rotation patterns, scaling effects, color transitions, audio triggers
- Object relationships like animation chains, synchronized movements, collision responses
Avoid custom components for:
- Data that can be computed from existing components
- Behavior logic (that belongs in systems)
- Variations of existing built-in functionality
Create a custom component
Creating custom components requires three steps.
1. Define your data structure
Create an XML file describing your component’s attributes in the correct project location. You have two options for creating the component XML file:
Right-click on the app/src/main/components/ folder, select New > File, and create a new XML file with any unique name (such as TestComponent.xml).
Important: The packageName in the <ComponentSchema> element must match the package name in your Android manifest. Using a different package name will lead to build errors. The build process generates Kotlin code in build/generated/java and resource files in build/generated/res.
XML component definitions must be placed in your app’s app/src/main/components/ directory:
your-spatial-app/
├── app/
│ └── src/
│ └── main/
│ ├── components/ # ← Component XML files go here
│ │ ├── TaskItem.xml
│ │ ├── WorkflowState.xml
│ │ └── DocumentLink.xml
│ ├── java/
│ ├── res/
│ └── AndroidManifest.xml
├── build.gradle.kts
└── settings.gradle.kts
Each component gets its own XML file named after the component. For example, an UpAndDown component should be defined in UpAndDown.xml.
Here’s an example for creating animated objects that move up and down:
<?xml version="1.0" ?>
<ComponentSchema packageName="com.yourcompany.yourapp">
<Component name="UpAndDown">
<Description>
A component that stores animation parameters for objects that move up and down.
This component works with an UpAndDownSystem to create smooth vertical animations.
</Description>
<FloatAttribute name="amplitude" defaultValue="0.5f" />
<FloatAttribute name="speed" defaultValue="2.0f" />
<FloatAttribute name="offset" defaultValue="0.0f" />
<Vector3Attribute name="startPosition" defaultValue="0f, 0f, 0f" />
</Component>
</ComponentSchema>
Attributes:
- amplitude: How far the object moves up and down (in meters)
- speed: How fast the animation cycles (rotations per second)
- offset: Current animation progress (used internally by the system)
- startPosition: The base position from which the animation occurs
Choose meaningful names that describe the data purpose. Use appropriate attribute types and provide sensible defaults so components work out of the box (except for ExperimentalMapAttribute and EntityAttribute which don’t require default values).
2. Register your component
Register your component in onCreate() before creating any entities:
class MainActivity : AppSystemActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// REQUIRED: Register before using the component
componentManager.registerComponent<UpAndDown>(UpAndDown.Companion)
// Register related systems (you'll learn about this in the next tutorial)
systemManager.registerSystem(UpAndDownSystem())
}
}
Registration is required. Forgetting to register a custom component causes runtime crashes and is a common error.
3. Attach to entities and process with systems
Once your component is registered, you can attach it to entities and create systems to process the data. This is where your custom component becomes functional within the ECS architecture.
Use Entity.create() to instantiate entities that include your custom component alongside built-in components. This creates a complete spatial object with both standard functionality (position, rendering) and your custom data.
Systems query for entities that match a set of components and implement logic. Systems can read, modify, or respond to your component data.
// Create entities with your custom component
val animatedCube = Entity.create(
Transform(Pose(Vector3(0f, 1.5f, -2f))),
Mesh(mesh = Uri.parse("cube.gltf")),
Material(),
UpAndDown(
amplitude = 1.0f, // Move 1 meter up and down
speed = 0.5f, // Half a cycle per second
offset = 0.0f, // Start at bottom of animation
startPosition = Vector3(0f, 1.5f, -2f) // Base position
)
)
// Query and update in systems (you'll learn more about this in the "Create a custom system" tutorial)
class UpAndDownSystem : SystemBase() {
override fun execute() {
Query.where { has(UpAndDown.id, Transform.id) }.eval().forEach { entity ->
val upAndDown = entity.getComponent<UpAndDown>()
val transform = entity.getComponent<Transform>()
// Update animation offset and apply to transform
// (Full implementation shown in the system creation tutorial)
entity.setComponent(transform)
}
}
}
Key concepts:
- Entity composition: Combine your custom component with built-ins like
Transform and Panel for complete functionality - System queries: Use
Query.where { has(YourComponent.id) } to find entities with your component - Data modification: Call
entity.setComponent() after modifying component data to persist changes - Frame-by-frame processing: Systems execute every frame, enabling real-time responses to data changes
Enum attributes for states
Use enums when your component has predefined states:
<Enum name="AnimationState">
<EnumValue value="IDLE" />
<EnumValue value="PLAYING" />
<EnumValue value="PAUSED" />
<EnumValue value="FINISHED" />
</Enum>
<Component name="AnimationController">
<EnumAttribute name="state" defaultValue="AnimationState.IDLE" />
<FloatAttribute name="duration" defaultValue="1.0f" />
<BooleanAttribute name="loop" defaultValue="false" />
</Component>
Entity references for relationships
Link entities together using EntityAttribute:
<Component name="AnimationChain">
<EntityAttribute name="nextTarget" />
<StringAttribute name="transitionType" defaultValue="smooth" />
<FloatAttribute name="delay" defaultValue="0.0f" />
</Component>
- Forgetting registration: Runtime crash with “Component class not registered” error
- Wrong packageName: Generated component not found during build
- Missing rebuild: XML changes not reflected in generated code
Custom components let you model any domain-specific data your spatial application needs:
- Identify data gaps: What information do your entities need that built-ins don’t provide?
- Design simple schemas: Start with 1-3 attributes, add complexity later
- Register immediately: Make registration part of your standard setup
- Build supporting systems: Create systems that process your custom data
- Test thoroughly: Verify components work in isolation and with built-ins
By default, the build process searches for XML files in the app/src/main/components/ folder. If you need to use a different folder for your custom components, you can edit config.json in app/scenes/:
{
"spatial.editor.customComponentXmlsPath": [
"../src/main/components/",
],
"spatial.editor.libraryComponentXmlsPath": "../build/generated/components"
}
To specify your desired folders, edit the paths in the spatial.editor.customComponentXmlsPath JSON property. The Spatial Editor also uses config.json to find the custom components.
Library components folder
By default, the build process exports XML components to app/build/generated/components from libraries such as meta-spatial-sdk-toolkit. If you want to use a different folder, edit the spatial.editor.libraryComponentXmlsPath property in config.json.
Note: Do not modify the exported XML files in the library components folder, as they will be overwritten in the next build.
Custom components integrate seamlessly with Spatial SDK’s ECS architecture, giving you the flexibility to create any spatial experience while maintaining performance and maintainability.
Now that you’ve created an UpAndDown component, the next step is to create a system that processes and animates entities with this component: