Public components are considered all components exported from
@material-ui/core
or@material-ui/lab
.Internal components are considered all components that are not exported from the packages, but only used in some public component. There is no need to have
sx
prop on these components
- export interface
{ComponentName}classes
and add comment for generating api docs (for internal components, may or may not expose classes but don't need comment) - export interface
{ComponentName}Props
- always export props interface (use
interface
overtype
) from the component file - provide
sx
only for public component
Public component
// Foo.tsx
export interface FooClasses {
/** Styles applied to the root element. */
root: string;
/** Styles applied to the foo element. */
foo: string;
/** Styles applied to the root element if `disabled=true`. */
disabled: string;
}
export interface FooProps {
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<FooClasses>;
// ...other props
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
}
internal component
// Bar.tsx
// if this internal component can accept classes as prop
export interface BarClasses {
root: string;
}
export interface BarProps {
classes?: Partial<BarClasses>;
}
- naming as
{ComponentName}ClassKey
- export if
classes
exists in props interface usingkeyof
// Foo.tsx
export type FooClassKey = keyof FooClasses;
// verify that FooClassKey is union of string literal
- export if
classes
exist in props interface from the component file - use
{Component}Classes
as type to preventing typo and missing classes - use
Private
prefix for internal component
Public component
// Foo.tsx
export function getFooUtilityClass(slot: string) {
return generateUtilityClass('MuiFoo', slot);
}
// make sure it has ClassKey as arg in generic
export const fooClasses: FooClasses = generateUtilityClasses('MuiFoo', ['root', 'foo', 'disabled']);
const useUtilityClasses = (styleProps: FooProps & { extraProp: boolean }) => {
// extraProp might be the key/value from react context that this component access
const { foo, disabled, classes } = styleProps;
const slots = {
root: ['root', foo && 'foo', disabled && 'disabled'],
};
return composeClasses(slots, getFooUtilityClass, classes);
};
internal component
// Bar.tsx
// in case that classes is not exposed.
// `classes` is used internally in this component
const classes = generateUtilityClasses('PrivateBar', ['root', 'bar']);
- naming using slot
{ComponentName}{Slot}
- use
skipSx
for internal component without specifyingname
,slot
andoverridesResolver
- to extend interface of the styled component, pass argument to generic
public component
const FooRoot = styled(
Typography,
{},
{
name: 'MuiFoo',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
},
)({
// styling
});
internal component
const BarRoot = styled(
Typography,
{},
{ skipSx: true },
)({
// styling
});
extends interface
const BarRoot = styled(
Typography,
{},
{ skipSx: true },
)<{ component?: React.ElementType }>({
// styling
});
// passing `component` to BarRoot is safe
// <BarRoot component="span" />
- prefer
function Component() {}
overReact.FC
- naming the render function in
React.forwardRef
(for devtools) useThemeProps
is needed only for public component- pass
styleProps
to StyledComponent for styling
public component
const Foo = React.forwardRef<HTMLSpanElement, FooProps>(function Foo(inProps, ref) => {
// pass args like this, otherwise will get error about theme at return section
const props = useThemeProps<Theme, FooProps, 'MuiFoo'>({
props: inProps,
name: 'MuiFoo',
});
const { children, className, ...other } = props
// ...implementation
const styleProps = { ...props, ...otherValue }
const classes = useUtilityClasses(styleProps);
return (
<FooRoot
ref={ref}
className={clsx(classes.root, className)}
styleProps={styleProps}
{...other}
>
{children}
</FooRoot>
)
})
internal component
const classes = generateUtilityClasses('PrivateBar', ['selected']);
const BarRoot = styled(
'div',
{},
{ skipSx: true },
)(({ theme }) => ({
[`&.${classes.selected}`]: {
color: theme.palette.text.primary,
},
}));
// if this component does not need React.forwardRef, don't use React.FC
const Bar = (props: BarProps) => {
const { className, selected, ...other } = props;
return <BarRoot className={clsx({ [classes.selected]: selected })} {...other} />;
};