---
# System prepended metadata

title: Ribbon performance

---

# Ribbon performance
Overall the Ribbon performance in initial renders is penalized by a variety of reasons. This writeup explores options for optimizing to create a better experience for customers.

Top major contributors include:

1. Multiple layers of wrapper components everywhere.
2. Animation layers in the React component heirarchy.
3. Multiple renders on startup
4. BaseButton, wrapped by AppBaseButton, AppToggleButton, BaseRibbonToggleButton, an Anonymous component for animations, and a memoization wrapper.
5. SplitButton contributing to possibly 10% of the overall time.
6. Extraneous css-in-js style overrides at every layer which are unneeded with the new button.

## Starting numbers

Running initial paint of ribbon at 6x slowness: **659ms**

![](https://i.imgur.com/mK8rBgJ.png)

The ToggleButton seems significantly slower than other basic AppButton renders at 19ms, with about 7ms in BaseButton. This could be because of initial style registration overhead. There are also 5 additional React layers to render one button, and every layer adds extra weight. These could be condensed into 2 or ideally 1:

![](https://i.imgur.com/nOkH4jq.png)

You can see (as expected) that subsequent buttons render faster:

![](https://i.imgur.com/Mg1PmDu.png)

...but split button is a pretty big problem:

![](https://i.imgur.com/0jtDUz1.png)

## Changes

So, let's gut this:

* Replace BaseButton with v8 Button
* Get rid of extraneous wrappers
* Remeasure

## Change 1: Replace AppButton rendering of BaseButton

Intial findings look interesting:

Down from ~559-659ms to ~425ms (with variance)

![](https://i.imgur.com/06xQn0p.png)

Button contributions now sink to miniscule. So I think we need to evaluate what the reasoning is for the copeous layers of stuff are here.

![](https://i.imgur.com/HJrpMTs.png)

Button now renders much cheaper better but the layers above it add up to 4.4ms button renders when it should be 2.8ms (which includes the svg render):

![](https://i.imgur.com/ZGJjKeK.png)

Oh wait; theres a `RibbonButton`, a `RibbonToggleButton`, and a `RibbonSplitButton` all which render various flavors of `AppButton`. Let's clean those up.

## Change 2: Style the Button with tokens using ThemeProvider

Should look like this:

![](https://i.imgur.com/qkcdnO8.png)

Actual:

![](https://i.imgur.com/QQN4D1e.png)

After ThemeProvider and custom theme is applied:

![](https://i.imgur.com/oZeRPFN.png)

TODOs: It's close, but not quite:

* Disabled icons get 40% opacity. We don't have a disabled opacity token for icons. Seems like we may need to add this.
* Button styles were applied generally; we need to target the Buttons using variant classes which define a configuration of tokens for a given button.

## Change 3: Reduce layers in RibbonButton/RibbonToggleButton/RibbonSplitButton

This button has a deep tree:

![](https://i.imgur.com/EzBcRrN.png)

Let's reduce complexity. Can we also remove the PropsAnimator stuff as well?

Before:

![](https://i.imgur.com/dXafDJf.png)

After:

![](https://i.imgur.com/kJhtFSJ.png)


Still renders a correctly styled button. Now do the same for toggle and split, which are separate implementations:

> TODO names
> 

## Final results

## Reference

## Ribbon anatomy

## Using compose in the Ribbon

I tried using `compose` to craft the `RibbonButton` from the new `Button`:

```tsx
export const RibbonButton = compose2<RibbonButtonInternalProps>(Button2, {
  displayName: 'RibbonButton',
  defaultProps: {
    icon: {
      as: p => {
        return <FontIcon {...p} iconName={p.children} />;
      }
    }
  }
});
```


Ran into points of friction:

A slot is:

A placeholder for some content. You can pass whatever you want to it.

### No way to access component state or original props from slot resolution

1. I want "as" to be derived from Button state. (if `href`, use 'a'; else 'button')

Proposal: `as` is a 


1. The `icon` prop in the RibbonButton is the name of the icon to render. Fluent UI Button expects this to be a shorthand prop, so we do some preprocessing to convert `icon="string"` into `icon={{ children: "string" }}`. This was hard to predict.
2. Even when this is figured out, it's hard to define the default render for the `icon` prop. I want to render `<FontIcon iconName={props.icon>/>`.

I expected something like this:

```
compose(Button, {
  defaultProps: {
    icon: (p: RibbonButtonProps) => <FontIcon {p.icon} />
  }
})
```

The issue is getting access to the original user props. Also, this would have replaced the container `span` and not the content of the span.

I have concerns with children functions:

How can I provide a function to replace the container vs the content?

```tsx
// For ribbon, I need access to the original unprocessed props, not the slot's incoming props...
icon: (propsForContent, options) => <FontIcon iconName={options.props.icon} />
}

// slot as an object
icon: {
  as: (p, options) => ..., // replace the container
  children: (p, options) => ..., // replace the content  
}

// slot as function should translate into "as" where it will be rendered
icon: FontIcon
icon: { as: FontIcon }


// slot as jsx (limited, can't access props) shoudl translate into "children"
icon: <FontIcon />
icon: { children: <FontIcon /> }

// if a user provides an object, 

// slot as function

// slo
```

4. 
5. So I wanted this:

4. We need a way to pre-filter incoming props before anything happens.
5. 
6. I ran into was that the `RibbonButton` passes in a string as the `icon` prop, indicating the icon they want to render. This gets interpretted as the children.

