Getting Started
This guide walks through everything from creating a new Unity project to rendering your first Fram3 element on screen.
See it in action first. The Fram3 repository includes a Storybook that demonstrates every element and feature of the framework. Open the project in Unity and run the
Storybookscene to browse live, interactive examples before writing any code yourself.
Requirements
- Unity 6000.3 or later
- UIToolkit (included with Unity — no extra installation needed)
Step 1 — Create a Unity project
Open Unity Hub and create a new project. Any template works; 3D (URP) or 2D are fine starting points. Fram3 only needs UIToolkit, which ships with Unity 6 by default.
Step 2 — Install the package
Open Packages/manifest.json in your project root (any text editor works) and add the Fram3 entry under dependencies:
{
"dependencies": {
"com.fram3.ui": "https://github.com/gabaudette/fram3.git?path=Packages/com.fram3.ui"
}
}Save the file. Unity will detect the change, fetch the package from GitHub, and compile it. Wait for the progress bar in the bottom-right corner to finish.
Alternatively, use the Package Manager window: open Window > Package Manager, click the + button, choose Add package from git URL, and paste:
https://github.com/gabaudette/fram3.git?path=Packages/com.fram3.uiStep 3 — Create a Panel Settings asset
UIToolkit requires a Panel Settings asset to control rendering resolution and scaling.
- In the Project window, right-click inside
Assets/and choose Create > UI Toolkit > Panel Settings. - Name it
PanelSettings(or any name you prefer). - Leave the defaults as-is for now. You can tune scale mode and reference resolution later.
Step 4 — Add a UIDocument to a scene GameObject
- Open or create a scene.
- In the Hierarchy window, right-click and choose Create Empty. Name it
UI. - With the
UIGameObject selected, click Add Component in the Inspector and add UI Document (search for “UI Document”). - In the UI Document component, assign the Panel Settings asset you created in the previous step to the Panel Settings field.
- Leave the Source Asset field empty — Fram3 manages the visual tree entirely in code.
Step 5 — Write your app root
Create a new C# script in Assets/ called MyApp.cs. This is the single entry point for your UI. Subclass AppRoot and override CreateRoot to return the root element of your interface:
using Fram3.UI.Core;
using Fram3.UI.Elements.Content;
using Fram3.UI.Elements.Layout;
using Fram3.UI.Rendering;
public sealed class MyApp : AppRoot
{
protected override Element CreateRoot() => new CounterElement();
}AppRoot is a MonoBehaviour that handles mounting, ticking, and disposing the renderer automatically. You only need to supply the root element.
Step 6 — Write your first element
Create a new C# script in Assets/ called CounterElement.cs:
using Fram3.UI.Core;
using Fram3.UI.Elements.Content;
using Fram3.UI.Elements.Layout;
public sealed class CounterElement : StatefulElement
{
public override State CreateState() => new CounterState();
private sealed class CounterState : State<CounterElement>
{
private int _count;
public override Element Build(BuildContext context)
{
return new Column
{
Children = new Element[]
{
new Text($"Count: {_count}"),
new Button(
label: "Increment",
onPressed: () => SetState(() => _count++)),
}
};
}
}
}StatefulElement is the base for elements that own mutable state. State holds the state and implements Build, which returns an immutable description of the UI. Calling SetState schedules a rebuild — only the elements that changed are patched in the UIToolkit tree.
Step 7 — Attach MyApp to the scene
- Select the
UIGameObject in the Hierarchy. - Click Add Component and search for
MyApp. - The component appears in the Inspector.
AppRootrequires aUIDocumenton the same GameObject, which you added in Step 4.
Hit Play. You should see a column with the text “Count: 0” and an “Increment” button. Clicking the button increments the count.
What just happened
AppRootdetected theUIDocumenton the same GameObject, applied full-screen sizing to the root visual element, created aRenderer, and calledCreateRoot()to mount the tree.- The renderer’s tick and dispose calls are wired to
UpdateandOnDestroyautomatically — no boilerplate required. Mountset the root element. From that point on, everySetStatecall triggers a diff between the new element tree and the previous one, and only the changed nodes are updated in the underlying visual tree.- No UXML, no USS files, no code generation — the entire UI is described in C#.
Next steps
- API Reference — full public API documentation