React Clean UI

Customize / Extend

Learn how to customize existing themes or create your own.

React Clean UI themes are fully customizable.

The easiest way to create your own theme is to start from an existing one and override only the values you want to change.

Extending an Existing Theme

Use extendTheme() to create a new theme from any existing theme.

import {
    DefaultTheme,
    extendTheme,
} from "@rbxts/react-clean-ui";

const MyTheme = extendTheme(DefaultTheme, {
    colors: {
        intents: {
            primary: {
                default: {
                    backgroundColor: Color3.fromRGB(82, 113, 255),
                },
            },
        },
    },
});

Only the properties you specify are changed. Everything else continues to use the values from the original theme.

Creating a Theme

If you'd rather start from the default theme, use createTheme().

import { createTheme } from "@rbxts/react-clean-ui";

const MyTheme = createTheme({
    spacing: {
        md: 16,
    },

    radius: {
        md: 12,
    },
});

This works the same way as extendTheme(), except it always starts from the library's default theme.

Deep Merging

Themes are merged recursively.

That means you only need to override the settings you care about.

For example, changing a single color will not replace the rest of the theme—it simply updates that one value while leaving everything else unchanged.

const MyTheme = extendTheme(DefaultTheme, {
    typography: {
        heading: {
            weight: Enum.FontWeight.Bold,
        },
    },
});

Intents

theme.colors.intents defines the colour schemes selected by a component's intent prop: primary, secondary, success, warning, danger, and info. Each intent has a default scheme and optional state schemes such as hover, focus, and disabled.

A scheme can set:

  • textColor, backgroundColor, and borderColor
  • backgroundTransparency
  • borderThickness — overrides the component's border thickness, for components that read it (such as Button and Tabs titles)
  • boxShadow
  • typography
  • backgroundImage and backgroundGradient

Many components also have their own intents map under theme.components.<name>, which is layered on top of theme.colors.intents. A state scheme only needs the fields it changes; the rest come from the default scheme.

Fallback to primary

Every intent starts from the primary scheme, so an intent that a theme doesn't define simply looks like primary. secondary is the only optional intent in theme.colors.intents: a custom theme written before secondary existed still works, and intent="secondary" renders with that theme's primary colours until you add it.

const MyTheme = extendTheme(DefaultTheme, {
    colors: {
        intents: {
            secondary: {
                default: {
                    textColor: Color3.fromHex("#1D2433"),
                    backgroundColor: Color3.fromHex("#E4E7EC"),
                    borderColor: Color3.fromHex("#C5CAD3"),
                },
                hover: {
                    backgroundColor: Color3.fromHex("#D5D9E0"),
                },
            },
        },
    },
});

All built-in themes define secondary.

Typography outline

A typography style in theme.typography can set an outline (TypographyOutline: color, thickness, and transparency, all optional). Text drawn with that style then gets a contextual UIStroke around its glyphs, which helps text that sits over the 3D world.

const MyTheme = extendTheme(DefaultTheme, {
    typography: {
        title: {
            outline: {
                color: Color3.fromHex("#000000"),
                thickness: 2,
            },
        },
    },
});

Unset fields default to black, thickness 1, and transparency 0. A Text can override the theme outline field by field with an outline object, or turn it off with outline={false} (see Text).

The theme outline only affects text drawn through Text, including components that pass their typography to Text. Components that draw their own TextLabel ignore it. No built-in theme sets outline.

Radius

theme.radius defines the radius scale: sm, md, and lg, in pixels. Anywhere the library takes a corner radius, you can pass one of these names as a token instead of a size. This includes every cornerRadius key under theme.components (also the per-corner fields of card.header, card.footer, and tabs.button), Button's styleOverride.cornerRadius, the Corners decorator's radius, and Container's cornerRadius.

const MyTheme = extendTheme(DefaultTheme, {
    radius: {
        md: 12,
    },

    components: {
        input: {
            cornerRadius: "md",
        },
    },
});

A token resolves against the theme in the component's context, so it follows a nested theme provider. Changing radius.md updates every value that uses "md".

Reading radius values in your own components

A theme cornerRadius value, or any radius prop, can now be a token. Convert it with SizeHelper.toRadius(theme, radius), which resolves the token from theme.radius before converting the value to a UDim. SizeHelper.toUDim doesn't understand tokens, and it errors on "md". Code that passed a theme radius to SizeHelper.toUDim should switch to toRadius.

import React from "@rbxts/react";
import { CleanThemeContext, SizeHelper } from "@rbxts/react-clean-ui";

export function Panel() {
    const theme = React.useContext(CleanThemeContext);

    return (
        <frame Size={new UDim2(0, 200, 0, 100)}>
            <uicorner CornerRadius={SizeHelper.toRadius(theme, theme.components.box.cornerRadius)} />
        </frame>
    );
}

If all you need is a UICorner, render <Corners radius={...} /> instead, which takes a token or a size and renders nothing when the radius is unset or 0. SizeHelper.resolveRadius(theme, radius) returns the resolved size without converting it, for when you need to parse the value yourself.

Using Your Theme

Once you've created your theme, pass it to the ThemeProvider.

import { CleanUiProvider } from "@rbxts/react-clean-ui";

<CleanUiProvider theme={MyTheme}>
    <App />
</CleanUiProvider>;

Every React Clean UI component will automatically use your customized theme.

GitHub Repository

On this page