Getting Started
ELEMENTS can be easily added to any Unity project, running Unity 6 or above. Unity 6.3+ is recommended to take full advantage of UI Toolkit's shader support and in world UI support.
Installation
Install R3
ELEMENTS depends on R3. Install it from NuGet using NuGetForUnity — search for "R3" and install the latest stable version.
Install ELEMENTS
Add ELEMENTS via the Unity Package Manager using the following git URL:
https://github.com/1by3/ELEMENTS.git?path=Assets/ELEMENTSYou can pin a specific version by appending a tag:
https://github.com/1by3/ELEMENTS.git?path=Assets/ELEMENTS#1.0.0Your first Component
In ELEMENTS, your UI is made up of a series of "Components". A Component is a single class that contains both your state and your UI definition.
Building a Component
Start by making a class that inherits from Component. Override the Render method to return the element tree that defines your UI...
public class ExampleComponent : Component
{
protected override IElement Render()
{
return new VerticalGroup(
new Label("The button has been clicked 0 times."),
new Button(new Label("Click Me!"))
);
}
}We'll talk more about the elements used (VerticalGroup, Label, and Button) shortly. For now, let's talk about how we can make this static UI interactive with state.
Adding State
State lives directly on your Component as fields. Use R3 Observable and ReactiveProperty to make state that automatically updates the UI when it changes...
public class ExampleComponent : Component
{
public readonly ReactiveProperty<string> Name = new("");
protected override IElement Render()
{
return new VerticalGroup(
new Label("What's your name?"),
new TextField(Name),
new HorizontalGroup(
new Label("Hello, "),
new Label(Name)
)
);
}
}The code above creates a ReactiveProperty<string> which holds the user's name. By passing this into the TextField constructor, we bind the text in the text field to the property. By passing it into the Label constructor, we bind the text of the Label to it as well.
A common pattern you might encounter is the need to transform data in your Component. Take this counter example...
public class Counter : Component
{
public readonly ReactiveProperty<int> Count = new(0);
protected override IElement Render()
{
return new VerticalGroup(
new HorizontalGroup(
new Label("The current count is:"),
new Label(Count.Select(v => v.ToString()))
),
new HorizontalGroup(
new Button(new Label("Decrement"))
.OnClick(_ => Count.Value--),
new Button(new Label("Increment"))
.OnClick(_ => Count.Value++)
)
);
}
}Since Count is a ReactiveProperty<int>, it cannot be directly passed to Label which requires Observable<string>. Therefore, we use LINQ to transform the value into a string type.
Rendering your UI
ELEMENTS provides a variety of extension methods for rendering your UI. The default is to render your UI into a UIDocument...
public class GameUI : MonoBehaviour
{
[SerializeField] private UIDocument uiDocument;
private void OnEnable()
{
uiDocument.AddStyleSheet("ELEMENTS/DefaultStyles");
uiDocument.RenderElement(new ExampleComponent());
}
}A couple of notes...
- We loaded the default ELEMENTS stylesheet onto the UIDocument using the
AddStyleSheethelper. This is optional, but highly recommended for a good starting point with styles. For more on styling, including Tailwind-style utility classes, see Utility Classes. - We used the
OnEnablemethod. This is an important detail that allows your UI to update when Unity reloads your game if you make code changes. If your UI is disappearing on reload, this might be why.
If you prefer to not use a Component for any reason, you can also pass elements directly...
public class GameUI : MonoBehaviour
{
[SerializeField] private UIDocument uiDocument;
private void OnEnable()
{
uiDocument.AddStyleSheet("ELEMENTS/DefaultStyles");
uiDocument.RenderElement(
new VerticalGroup(
new Label("Hello World")
)
);
}
}Setting up an ElementPortal
If your UI uses floating elements like Dialog, Popover, Alert, or ContextMenu, you need an ElementPortal in your scene. The portal creates an overlay surface that these elements render into.
Add the ElementPortal component to a GameObject in your scene. It requires a UIDocument on the same GameObject — one will be added automatically.
When an ElementPortal is enabled, it becomes available as ElementPortal.Current. Floating elements like Dialog and Popover use this automatically, so you don't need to pass the portal reference manually.