ReactiveUI

Introduction

What Reactive is, why it beats BSML, and where to start.

Reactive is a UI library for Beat Saber mods. You write the view in C#, next to the code that drives it, as a tree of components. The values that change over time live in states, and whatever reads a state follows it. No markup, no code-behind, nothing to refresh by hand.

The whole idea fits in one component. A label shows a number, and a button increments it:

Counter.cs
class Counter : ReactiveComponent {
    protected override GameObject Construct() {
        var count = Remember(0);

        return new Layout {
            FlexController = {
                FlexDirection = FlexDirection.Column,
                AlignItems = Align.Center,
                JustifyContent = Justify.Center,
                Gap = 2.pt
            },

            Children = {
                new Label {
                    FontSize = 9f,
                    sText = count.Map(x => x.ToString())
                },

                new BsPrimaryTextButton {
                    Text = "+1",
                    OnClick = () => count.Value++
                }
            }
        }.Use();
    }
}

The button only changes count.Value. It doesn't know a label exists. The label reads the count through sText and follows on its own. Everything else in these docs is that idea applied to layout, input, animation and lists.

Why not BSML

BSML describes a view in XML and wires it up through a class behind it. That is fine for a static panel, and gets harder the more the UI has to change while it's open.

Reactivity

In BSML, a value that changes needs a [UIValue] property and a NotifyPropertyChanged call in every setter that affects it. In Reactive you bind once. Here one volume state drives the percentage, the meter, the slider and the warning below it, and none of them knows about the others:

A volume panel where dragging the slider changes the percentage, lights a meter from green to red and switches the warning below
VolumePanel.cs

Layout

Layout uses flexbox, the same model as the web, and when flexbox isn't enough, you can write your own controllers. Drag this slider to see blocks of different sizes wrap onto new rows and grow to fill them, which BSML's layout groups can't do:

Blocks of different sizes wrapping onto new rows and stretching to fill them as a slider narrows the panel
WrappingBlocks.cs

Animations

BSML has no animations of its own, so you write the loop yourself. Reactive can animate both a value and a target. An animated state eases a value toward its new one, and anything bound to it follows. An animation player plays an animation on a target, like fading, scaling or spinning a component. Try this grid, where each tile eases its scale and color as a wave runs across it:

A grid of tiles that pop and change color as a wave runs across them
TileWave.cs

Performance

BSML parses its markup on every mount, and its custom-list parses a new cell for each row that comes into view. A Table reuses its cells instead. In our PC benchmarks, jumping far through a table of 100000 rows is 16 times faster, and mounting is 2.8 to 7.8 times faster. Below you can see the buttons jump through 100000 rows, all drawn by the few cells that fit on screen:

Buttons scrolling a table of 100000 rows to row 50000, the end and back, drawn by the same 17 cells throughout
Leaderboard.cs

Performance Benchmarks has every number. You do give up BSML's hot reload and rebuild instead, though the Playground covers quick experiments.

Integration

BSML connects markup to code through strings, so a typo only shows up when the view is parsed. A Reactive view is plain C# in one class, so a typo is a compile error, and your IDE can navigate and rename through it.

Typing a binding in Rider, with the s-properties suggested as you type, then a misspelled sTxt marked as an error

In Debug, the Reactive compiler generates every s-property, so your IDE suggests them as you type. Release keeps only the ones you use, as Compiler Internals explains.

Where to start

Getting Started covers installing Reactive, and Coming from BSML covers migrating an existing UI.

On this page