Skip to main content

Switching the UI library

Only change files inside apps/foundation/src/ui/. Consumer code keeps importing from @foundation/ui. This guide demonstrates the swap using three popular libraries (MUI, Chakra, React Aria), with example contract→library mapping and step-by-step implementation per tab.

How to switch

  1. Leave unchanged: all types.ts, tokens.ts, index.ts, and consumer code.
  2. Update mapping.ts with the new library's size/variant/color scale.
  3. Replace each component's index.tsx — import from the new library, pass mapped props from ../mapping, and drop or adjust props the library doesn't support.

Per-library: contract + implementation

Pick a library tab. Each tab shows the contract mapping (what the library expects) and implementation steps for that library.

Contract → Radix Themes expects

Our contractRadix Themes expects
SemanticSize: xs, sm, md, lgsize: "1", "2", "3", "4"
SemanticSizeCompact: sm, md, lgsize: "1", "2", "3"
ButtonVariant: solid, subtle, surface, outline, transparentvariant: subtle→soft, transparent→ghost; rest same
IconButtonVariant: solid, subtle, outline, transparentvariant: subtle→soft, transparent→ghost; rest same
AccentColor: gray, blue, red, orange, greencolor: same names
CardVariant: surface, classic, transparentvariant: surface, classic, ghost (transparent→ghost)

Implementation

  1. mapping.ts — Map our contract to Radix's scale and variant names. Add mappers for Card, IconButton, etc. per the contract table.
// Full example — same shape for other libs: map contract → library types (see their docs for exact types)
export function mapSemanticSize(size: SemanticSize): "1" | "2" | "3" | "4" {
const map: Record<SemanticSize, "1" | "2" | "3" | "4"> = { xs: "1", sm: "2", md: "3", lg: "4" };
return map[size];
}

export function mapButtonVariant(variant: ButtonVariant): "solid" | "soft" | "surface" | "outline" | "ghost" {
switch (variant) {
case "subtle": return "soft";
case "transparent": return "ghost";
default: return variant;
}
}
  1. Button/index.tsx — Import from @radix-ui/themes, re-export types from ./types, pass mapped size/variant/color, forward ref and spread rest.
import { forwardRef } from "react";
import { Button as RadixButton } from "@radix-ui/themes";
import type { ButtonProps } from "./types";
import { mapSemanticSize, mapButtonVariant } from "../mapping";

export type * from "./types";

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ size, variant, color, ...rest }, ref) => (
<RadixButton
ref={ref}
size={mapSemanticSize(size)}
variant={mapButtonVariant(variant)}
color={color}
{...rest}
/>
)
);

Other components (Card, IconButton, etc.): same pattern—add mappers in mapping.ts and wire them in that component's index.tsx.

Radix Themes — confirm prop names for your version.