Data Grid - Components usage
Learn how to customize the Data Grid with composable components.
Introduction
By default, the <DataGrid />
component includes all of the interfaces necessary for users to interact with the built-in features. Where more flexibility is needed over what gets rendered, the Data Grid package includes a set of highly customizable components:
Composition
The components are composable and consist of several parts. For example, Toolbar
and ToolbarButton
are parts of the Toolbar component.
The snippet below is an example of how to assemble components to create a custom toolbar.
import { Toolbar, ToolbarButton, FilterPanelTrigger } from '@mui/x-data-grid';
function CustomToolbar() {
return (
<Toolbar>
<FilterPanelTrigger render={<ToolbarButton />}>Filters</FilterPanelTrigger>
</Toolbar>
);
}
The custom toolbar can then be passed to the toolbar
slot:
function App() {
return <DataGrid slots={{ toolbar: CustomToolbar }} showToolbar />;
}
Customization
The component parts are highly customizable, built to integrate with any design system, and any styling method.
CSS classes
The className
prop can be used to apply styles:
<FilterPanelTrigger className="text-blue-600 underline" />
Some components also provide internal state that can be used to conditionally apply classes:
<FilterPanelTrigger
className={(state) => (state.open ? 'text-blue-600' : 'text-gray-900')}
/>
Render prop
The render
prop provides control over how the components are rendered. It serves three main purposes:
- Replacing the default rendered element
- Providing access to props and internal state
- Merging functionality between two or more components
Custom elements
You can use the render
prop to replace the default rendered element with your own:
<FilterPanelTrigger render={<CustomButton />} />
Render function
When you need access to the component's props or internal state, you can pass a function to the render
prop, as shown in the example below. This pattern is particularly useful when you need to:
- Use internal state for custom rendering
- Access and handle component props (like event handlers)
- Transform props before passing them to your custom element
// Access props
<FilterPanelTrigger
render={(props) => <ToolbarButton onClick={props.onClick}>Filters</ToolbarButton>}
/>
// Access state
<FilterPanelTrigger
render={(props, state) => (
<ToolbarButton {...props}>
{state.open ? 'Close filter panel' : 'Open filter panel'}
</ToolbarButton>
)}
/>
Prop forwarding
Any props passed to the components will be forwarded directly to their corresponding HTML element. For example:
<Toolbar className="my-toolbar" aria-label="Grid toolbar" />
// Rendered HTML:
<div role="toolbar" class="my-toolbar" aria-label="Grid toolbar" />
Providing children
You can provide children to the rendered element in two ways:
// Method 1: Children inside the rendered component
<FilterPanelTrigger render={<CustomButton>Filters</CustomButton>} />
// Method 2: Children of <FilterPanelTrigger />
<FilterPanelTrigger render={<CustomButton />}>Filters</FilterPanelTrigger>
Both methods above will produce the same result, as the component automatically passes its children to the rendered element.
Avoiding nested elements
A common use case for the render
prop is when composing components that render the same HTML element. For example, both FilterPanelTrigger
and ToolbarButton
render a button element. The render
prop in this example merges the functionality of both components whilst ensuring that only one button gets rendered:
// ❌ This creates nested button elements
<FilterPanelTrigger>
<ToolbarButton>
Filters
</ToolbarButton>
</FilterPanelTrigger>
// ✅ This merges the props and renders a single button
<FilterPanelTrigger render={<ToolbarButton />}>
Filters
</FilterPanelTrigger>