首页
/ Material UI 中通过 unstable_sxConfig 扩展与定制 sx prop 的实验性 API

Material UI 中通过 unstable_sxConfig 扩展与定制 sx prop 的实验性 API

2026-09-06 17:49:56作者:贡沫苏Truman

sx prop 是 Material UI(MUI System)中基于主题的系统化样式入口,默认支持 pmbgcolorborderRadius 等上百个工具类式的 key。但当你需要引入设计系统中的自定义语义 key(如 size),或者把某个 key 的取值范围约束到固定刻度(如只允许 sm/md/lg 的圆角)时,可以通过主题中的 unstable_sxConfig 实验性选项来扩展或改写 sx prop 的处理行为。本文基于仓库中 configure-the-sx-prop.md 文档展开,并对照 MUI System 源码说明每个配置项的真实处理链路,帮助你既能复制可运行的示例,又能理解其底层原理。

sx prop 的处理是配置驱动的

要理解 unstable_sxConfig 的作用位置,先看 sx 的核心实现。sx 样式函数由 unstable_createStyleFunctionSx() 创建,位于 styleFunctionSx.js

// packages/mui-system/src/styleFunctionSx/styleFunctionSx.js
export function unstable_createStyleFunctionSx() {
  function styleFunctionSx(props) {
    // ...
    const config = theme.unstable_sxConfig ?? defaultSxConfig; // L26

    function process(sxInput) {
      // 遍历 sx 对象的每个 key,逐 key 查找 config 中的处理规则
      for (const styleKey in sxObject) {
        // ...
        if (config[styleKey]) {
          setThemeValue(css, styleKey, value, theme, config); // L57
          continue;
        }
        // 未命中 config 的 key 会尝试按断点对象或嵌套样式处理
      }
    }
  }
}

关键点在第 26 行:sx 处理时优先读取 theme.unstable_sxConfig,找不到才回退到内置的 defaultSxConfig。也就是说,每个 key 如何映射 CSS,完全由一份 key → 处理规则的表驱动。默认表里登记了全部内置 key,例如:

// packages/mui-system/src/styleFunctionSx/defaultSxConfig.ts(节选)
const defaultSxConfig: SxConfig = {
  border: { themeKey: 'borders', transform: borderTransform },
  borderRadius: { themeKey: 'shape.borderRadius', style: borderRadius },
  bgcolor: { themeKey: 'palette', cssProperty: 'backgroundColor', transform: paletteTransform },
  p: { style: padding },
  gap: { style: gap },
  // ... 还有上百个 key
};

除了 sx prop 本身,组件级的系统 props(如 <Box p={4} />)也走同一份配置:extendSxProp.ts 中的 splitProps 会用 props?.theme?.unstable_sxConfig ?? defaultSxConfig 把传入的 props 拆分为"系统 props"(进入 sx 通道)与"其他 props"(透传给 DOM)。因此你扩展 config 后,组件 props 形式的系统 key 也会一并生效。

扩展 sx prop:新增自定义 key

文档给出的第一个场景:为 sx 增加一个新的 key。官方示例 ExtendTheSxProp.js 定义了一个 size key,表示"宽高相同的方形尺寸":

import { Box, handleBreakpoints } from '@mui/system';
import { createTheme, ThemeProvider } from '@mui/material/styles';

const customTheme = createTheme({
  unstable_sxConfig: {
    size: {
      style: (props) => {
        const { size, theme } = props;

        const styleFromPropValue = (propValueFinal) => {
          const value = theme.spacing(propValueFinal);

          return {
            width: value,
            height: value,
          };
        };

        // 添加对断点语法的 support
        return handleBreakpoints(props, size, styleFromPropValue);
      },
    },
  },
});

export default function ExtendTheSxProp() {
  return (
    <ThemeProvider theme={customTheme}>
      <Box sx={{ size: 10, border: 1 }} />
    </ThemeProvider>
  );
}

要点解析:

  • size: 10 的语义10 会经过 theme.spacing(10) 换算(默认 spacing 因子为 4,即 40px),最终生成 width: 40px; height: 40px
  • 为什么必须调用 handleBreakpointsstyle 函数是"最大自由度"的配置项,引擎不会替你处理断点——如果直接返回 styleFromPropValue(size)sx={{ size: { xs: 4, md: 8 } }} 这种响应式写法就无法工作。handleBreakpoints(props, propValue, styleFromPropValue) 由 MUI System 从 breakpoints.ts 导出,并在 index.js 中作为公开 API 导出,仓库内 borderRadiusgapmaxWidth 等内置 style 函数(见 borders.tscssGrid.tssizing.ts)都是同样的写法,自定义 key 对齐这一模式即可。
  • 文档目录中还提供了对应的 TypeScript 版本 ExtendTheSxProp.tsx,用于演示如何在类型层面同步扩展 SxProps,使 size 在 IDE 中获得类型提示。

覆盖已有行为:把 borderRadius 约束到固定刻度

文档的第二个场景:某些设计系统要求圆角只能取特定档位,而不是任意数字。默认配置中 borderRadius{ themeKey: 'shape.borderRadius', style: borderRadius }(见 defaultSxConfig.ts 第 36 行),允许任意数字、并支持响应式与嵌套。示例 ChangeTheBehaviorSxProp.js 通过替换该 key 的配置,让取值直接映射到 shape 主题上的命名刻度:

import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import { createTheme, ThemeProvider } from '@mui/material/styles';

const theme = createTheme({
  unstable_sxConfig: {
    // 现在可以在 sx 中使用 borderRadius key,
    // 直接从 palette(shape)取值
    borderRadius: {
      themeKey: 'shape',
    },
  },
  shape: {
    sm: 4,
    md: 8,
    lg: 12,
  },
});

export default function ChangeTheBehaviorSxProp() {
  return (
    <Stack direction="row" sx={{ gap: 1 }}>
      <ThemeProvider theme={theme}>
        <Box sx={{ borderRadius: 'sm', border: 1, p: 4 }} />
        <Box sx={{ borderRadius: 'md', border: 1, p: 4 }} />
        <Box sx={{ borderRadius: 'lg', border: 1, p: 4 }} />
      </ThemeProvider>
    </Stack>
  );
}

覆盖后 sx={{ borderRadius: 'sm' }} 的处理路径变为:themeKey: 'shape' 命中,引擎通过 getPath(theme, 'shape') 取出整个 shape 对象,再按 key 'sm' 查到 4,最终输出 border-radius: 4px。由于没有再提供 style/transform,数字与刻度的换算规则完全由你的 shape 主题决定——这正是"限制取值范围"的实现方式。

API 详解:unstable_sxConfig 的四个配置项

unstable_sxConfig 是一个 key 到配置对象的映射,类型定义见 SxConfigRecord

// packages/mui-system/src/styleFunctionSx/defaultSxConfig.ts
export interface SxConfigRecord {
  cssProperty?: keyof React.CSSProperties | false | undefined;
  /** dot access in `Theme`(主题对象上的点路径) */
  themeKey?: string | undefined;
  transform?: TransformFunction | undefined;
  style?: SimpleStyleFunction<any> | undefined;
}

export type SxConfig = Record<string, SxConfigRecord>;

文档 API 部分列出的四个属性与源码中 setThemeValuestyleFunctionSx.js 第 90–142 行)的处理顺序一一对应:

  1. cssProperty(string | false,可选):声明输出到哪个 CSS 属性,默认与 key 同名。典型例子是 bgcolor,其 cssProperty'backgroundColor'。取 false 时表示该 key 展开为任意样式片段(如默认的 typographydisplayPrint)。
  2. themeKey(string,可选):主题对象的点路径,如 'shape.borderRadius''palette'。引擎用 getPath(theme, themeKey) 取到映射表后,按 key 查值;因此 borderRadius: { themeKey: 'shape' } 就能把 'sm' 这类命名刻度解析出来。
  3. transform(函数,可选):签名 (cssValue, userValue) => number | string | React.CSSProperties | CSSObject,在最终写回 CSS 前对解析出的值做转换,典型用途如默认配置中 palettepaletteTransform(把 'primary.main' 之类路径解析为实际色值)和 borderborderTransform
  4. style(函数,可选):签名 (props) => CSSObject,接收 { [key]: 用户值, theme },返回完整样式对象,自由度最高。源码中它的优先级最高——setThemeValue 里先判断 style,存在则直接 merge(css, style(...)) 后返回(第 109–119 行),不再走 themeKey/transform 路径。正因为它绕过了引擎的断点处理,文档和示例都强调要自行处理断点值(用 handleBreakpoints)。

需要注意 stylethemeKey/transform 的组合语义:给了 style 就只走 style;未给时 themeKey + transform + cssProperty 三者共同完成"查主题 → 变换 → 写 CSS 属性"的链路,并且该链路内部通过 iterateBreakpoints 天然支持响应式对象值。

合并机制:你的配置如何与默认配置合并

createThemeunstable_sxConfig 的处理在 createTheme.js 第 39–42 行

muiTheme.unstable_sxConfig = {
  ...defaultSxConfig,
  ...other?.unstable_sxConfig,
};

这是按 key 的浅合并,带来两条实用结论:

  • 新增 key 不影响内置 keysize 这类新 key 只是往合并后的 config 里多加一项,pmbgcolor 等全部保留,所以扩展示例中 <Box sx={{ size: 10, border: 1 }} />border 依然是默认边框语义。
  • 同名 key 完全替换而非深合并:示例中 borderRadius: { themeKey: 'shape' } 会整体覆盖默认的 { themeKey: 'shape.borderRadius', style: borderRadius },原有的数字/响应式支持即被移除——这正是"改变已有行为"的底层原因。ThemeOptionsTheme 的类型声明中也保留了该字段(见 createTheme.d.ts 第 35、51 行),而 Material 层的 createTheme(如 createThemeNoVars.js)会将其透传给 MUI System 的建题逻辑。

适用前提与注意事项

  • 实验性(unstable)前缀的含义unstable_sxConfigunstable_sx 均带 unstable_ 前缀,位于 system 文档的实验性 API 分类下,意味着接口随版本演进可能变化,生产使用前建议锁定版本并回归验证样式输出。
  • 依赖主题上下文styleFunctionSx 中 config 取自 props.theme第 26 行),因此自定义 config 只在 ThemeProvider 提供的主题上生效;脱离主题的用法(如直接调用 theme.unstable_sx)则依赖 createTheme 时合并进主题对象的配置(createTheme.js 第 43–48 行)。
  • style 函数需自备断点能力:如前所述,使用 style 时必须调用 handleBreakpoints,否则 sx={{ size: { xs: 4, md: 8 } }} 的响应式对象不会被展开。
  • 源码中的遗留特判setThemeValue 中保留了 themeKey === 'typography' && value === 'inherit' 的兼容分支(第 103–107 行,代码注释标注待移除),说明该通道仍在迭代,进一步印证其实验性定位。

小结

unstable_sxConfig 提供了 MUI System 样式通道的两个对称能力:往 key → 规则表里加行(新增 size 这类语义 key),或改写某一行(把 borderRadius 收敛为固定刻度)。四个配置项中,themeKey/cssProperty/transform 组合适合"查主题 + 值变换"的常规映射,style 适合输出任意样式但需自行处理断点。结合 defaultSxConfig.ts 中内置 key 的写法,可以直接照着仓库源码为自己的设计系统补齐缺失的语义 key。

登录后查看全文
热门项目推荐
相关项目推荐