Skip to main content

Roact JSX

note

The following guide assumes that you are already familiar with Roact.
Please refer to the Roact documentation for more information.

Introduction

While roblox-ts allows you to use Roact just like you would in Luau, it also supports a "JSX" shorthand form.

import Roact from "@rbxts/roact";

const element = (
<frame Size={new UDim2(1, 0, 1, 0)}>
<frame Key="Child" Size={new UDim2(1, 0, 1, 0)} />
</frame>
);

You can learn more about JSX here.

Tag Names

The "tag name" in JSX is the expression after the initial < character.
For example, frame is the tag name of <frame />.

You can use any Roblox UI class (host components) as a built-in JSX tag name by converting the name to lowercase.

  • Frameframe
  • UIListLayoutuilistlayout
  • ViewportFrameviewportframe
  • etc.

Tag names can also be custom class components or functional components.

Custom components must use PascalCase.

import Roact from "@rbxts/roact";

interface MyComponentProps {
value: string;
}

function MyComponent(props: MyComponentProps) {
return <textlabel Text={props.value} />;
}

const element = <MyComponent value="foobar" />;

Tag names can also be a property access expression to use components which are nested inside of objects or namespaces.

import Roact from "@rbxts/roact";

interface MyComponentProps {
value: string;
}

function MyComponent(props: MyComponentProps) {
return <textlabel Text={props.value} />;
}

const Components = {
MyComponent: MyComponent,
};

const element = <Components.MyComponent value="foobar" />;

Special Attributes

Key Attribute

The Key attribute controls what your UI Instance will be named in the DataModel. This is the same as the "Child" key in this Luau example:

Roact.createElement("Frame", {
Child = Roact.createElement("Frame", {}),
})
import Roact from "@rbxts/roact";

const element = (
<frame>
<frame Key="Child" />
</frame>
);

If an element is given the Key attribute and it not a child of another element, it will be wrapped in a Roact.Fragment.

import Roact from "@rbxts/roact";

const element = <frame Key="Child" />;

Ref Attribute

The Ref attribute directly maps to the [Roact.Ref] key in Luau.

import Roact from "@rbxts/roact";

const ref = Roact.createRef<Frame>();
const element = <frame Ref={ref} />;

Change Attribute

The Change attribute takes an object which maps property name -> changed function. The changed function value will be given a reference rbx to the rendered UI instance.

Note the double curly braces {{ and }}.

import Roact from "@rbxts/roact";

const element = (
<frame
Change={{
Position: (rbx) => print(`${rbx.GetFullName()} changed Position!`),
}}
/>
);

Event Attribute

The Event attribute takes an object which maps property name -> event connection function. The event connection function value will be given a reference rbx to the rendered UI instance followed by the rest of the event arguments.

Note the double curly braces {{ and }}.

import Roact from "@rbxts/roact";

const element = (
<textbutton
Event={{
MouseButton1Down: (rbx, x, y) =>
print(`${rbx.GetFullName()} was clicked at (${x}, ${y})`),
}}
/>
);

Spreading into Attributes

You can spread objects into attributes using the form {...exp} where exp is an object.
This is useful for creating reusable preset lists of properties.

import Roact from "@rbxts/roact";

const MyStyle: Partial<WritableInstanceProperties<Frame>> = {
BackgroundColor3: new Color3(0, 0, 0),
BackgroundTransparency: 0.5,
};

const element = <frame {...MyStyle} />;

Spreading into Children

You can spread arrays into children using the form {...exp} where exp is a ReadonlyArray<T>.

import Roact from "@rbxts/roact";

const listItems = new Array<Roact.Element>();
for (let i = 0; i < 10; i++) {
listItems.push(<textbutton Text={`Button ${i}`} />);
}

const element = <frame>{...listItems}</frame>;

Using Values as Children

You can use values for children using the form {exp}. This is useful for programmatically creating children. The allowed values are:

  • Roact.Element
  • ReadonlyArray<T>
  • ReadonlyMap<K, V>
  • boolean
  • undefined

boolean and undefined values do not actually get put into the children props, but allowing the values here is useful for creating conditional children values.

import Roact from "@rbxts/roact";

let condition = false;

const element = (
<frame>
{condition && <frame />}
{condition ? <frame /> : undefined}
</frame>
);

Fragments

To create a Fragment with JSX you can either use the tag name Roact.Fragment

import Roact from "@rbxts/roact";

const fragment = <Roact.Fragment></Roact.Fragment>;

Or, you can use the shorthand form:

import Roact from "@rbxts/roact";

const fragment = <></>;

Extending the default JSX elements

By default the JSX supports all gui objects, however this may be limiting in cases where you want to manage other instances using Roact. Which elements are supported by JSX is determined by a global JSX namespace, to allow for more instances to be created this way you need to extend this global namespace. It is recommended to define this extension of the namespace in one central place for all instances.

Note the JSX.IntrinsicElement<T extends Instance> expects the type for an instance to be passed into it to allow for the properties & events to be typed correctly. Also note that, by convention, all Roblox instances should be lowercased.

declare global {
namespace JSX {
interface IntrinsicElements {
// Your instances into here
proximityprompt: JSX.IntrinsicElement<ProximityPrompt>;
folder: JSX.IntrinsicElement<Folder>;
}
}
}