Skip to content

[RFC] What's the best API to override deep/nested elements? #21453

Description

@oliviertassinari

A while back, around the alpha & beta iterations on the v1 version, we had to decide what was the best way to override the nested components. It's meant to help developers customize the rendering. We can find #11204 and #10476 as a legacy.

up until v1

So far, the tradeoff on this problem has been:

  1. Provide a XXXComponent prop when overriding the nested element can be valuable for users. For instance:

https://github.com/mui-org/material-ui/blob/c6b95d2e773088af66823f8995c3e57508c82056/packages/material-ui/src/Modal/Modal.d.ts#L8

  1. Provide a XXXProps prop when providing a custom XXXComponent component is too cumbersome. For instance:

https://github.com/mui-org/material-ui/blob/c6b95d2e773088af66823f8995c3e57508c82056/packages/material-ui/src/Modal/Modal.d.ts#L9

However, this original design constraint is increasingly more challenged by the following:

Autocomplete

The Autocomplete mixes the renderXXX approach with the XXXComponent approach. cc @oliviertassinari

https://github.com/mui-org/material-ui/blob/c6b95d2e773088af66823f8995c3e57508c82056/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts#L164
https://github.com/mui-org/material-ui/blob/c6b95d2e773088af66823f8995c3e57508c82056/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts#L142

Date Picker

The DatePicker mixes the renderXXX approach with the XXXComponent approach. cc @dmtrKovalenko

renderLoading?: () => React.ReactNode;

https://github.com/mui-org/material-ui-pickers/blob/c834f5cac5930df099aaad806ceb3fe4ec1669db/lib/src/views/Calendar/Calendar.tsx#L43

ToolbarComponent?: React.ComponentType<ToolbarComponentProps>;

https://github.com/mui-org/material-ui-pickers/blob/c834f5cac5930df099aaad806ceb3fe4ec1669db/lib/src/typings/BasePicker.tsx#L61

Data Drid

The DataGrid goes with xXXComponent approach, however, the name is confusing. Sometimes it's a render prop, sometimes it's a React element, it's never a component as the name suggests cc @dtassone

  paginationComponent?: (props: PaginationProps) => React.ReactNode;
  loadingOverlayComponent?: React.ReactNode;
  noRowsOverlayComponent?: React.ReactNode;
  footerComponent?: (params: ComponentParams) => React.ReactNode;
  headerComponent?: (params: ComponentParams) => React.ReactNode;

https://github.com/mui-org/material-ui-x/blob/59d533642d5837ce6912fe88cbcdfc228f621594/packages/grid/x-grid-modules/src/models/gridOptions.tsx#L93-L97

Styled components

Styled components might request something brand new. cc @mnajdova
As the experimentation of #21104 showcases. If we want to keep the CSS specificity at it's lowest possible level (meaning one level) and expose unstyled components, we will have to expose an API to inject custom component. In older experimentation, I worked around the problem with a components prop.

components?: {
  Root: React.ElementType<React.HTMLAttributes<HTMLDivElement>>;
  Label: React.ElementType<React.HTMLAttributes<HTMLSpanElement>>;
}

https://github.com/oliviertassinari/material-ui/blob/153c1833fb204a28ee6712db1c2ac6a9308a163e/packages/material-ui/src/TableCell/TableCell.unstyled.js#L18

The problem

I think that it would be great to defines which API works best and in which cases. I would hope that by doing so, we can provide a consistent experience for the developers using the library to build applications and websites. I also think that by reducing the number of approaches we can reduce the learning curve for new users.

When should we use a component, when should we use a render prop, etc.?

Prior-arts

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking changeIntroduces changes that are not backward compatible.discussion

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions