diff --git a/docs/Admin.md b/docs/Admin.md
index 4f102ebd8a0..870f010f790 100644
--- a/docs/Admin.md
+++ b/docs/Admin.md
@@ -275,7 +275,10 @@ See the [Theming documentation](./Theming.md#using-a-custom-menu) for more detai
 MUI supports [theming](https://mui.com/customization/themes). This lets you customize the look and feel of an admin by overriding fonts, colors, and spacing. You can provide a custom MUI theme by using the `theme` prop:
 
 ```jsx
+import { defaultTheme } from 'react-admin';
+
 const theme = {
+    ...defaultTheme,
     palette: {
         type: 'dark', // Switching the dark mode on is a single property value change.
     },
@@ -290,7 +293,7 @@ const App = () => (
 
 ![Dark theme](./img/dark-theme.png)
 
-For more details on predefined themes and custom themes, refer to the [MUI Customization documentation](https://mui.com/customization/themes/).
+For more details on predefined themes and custom themes, refer to [the Theming chapter](./Theming.md#global-theme-overrides) of the react-admin documentation.
 
 ## `layout`
 
diff --git a/docs/Inputs.md b/docs/Inputs.md
index ceb711ff038..d6fd506e08c 100644
--- a/docs/Inputs.md
+++ b/docs/Inputs.md
@@ -268,6 +268,37 @@ import { FormDataConsumer } from 'react-admin';
  );
 ```
 
+### Overriding The Input Variant
+
+MUI offers [3 variants for text fields](https://mui.com/material-ui/react-text-field/#basic-textfield): `outlined`, `filled`, and `standard`. The default react-admin theme uses the `filled` variant. If you want your application to use another variant, override the `<Admin theme>` prop with a [custom theme](./Theming.md#global-theme-overrides), as follows:
+
+```jsx
+import { defaultTheme } from 'react-admin';
+
+const theme = {
+    ...defaultTheme,
+    components: {
+        ...defaultTheme.components,
+        MuiTextField: {
+            defaultProps: {
+                variant: 'outlined' as const,
+            },
+        },
+        MuiFormControl: {
+            defaultProps: {
+                variant: 'outlined' as const,
+            },
+        },
+    }
+};
+
+const App = () => (
+    <Admin theme={theme}>
+        // ...
+    </Admin>
+);
+```
+
 ## Writing Your Own Input Component
 
 If you need a more specific input type, you can write it directly in React. You'll have to rely on react-hook-form's [useController](https://react-hook-form.com/api/usecontroller) hook, to handle the value update cycle.
diff --git a/docs/Reference.md b/docs/Reference.md
index 7cab5a7d666..d09ac47ed50 100644
--- a/docs/Reference.md
+++ b/docs/Reference.md
@@ -203,7 +203,6 @@ title: "Reference"
 * `useSortState`
 * [`useStore`](./useStore.md)
 * [`useStoreContext`](./useStoreContext.md)
-* [`useStyles`](./Theming.md#overriding-a-component-style)
 * `useSuggestions`
 * [`useTheme`](./Theming.md#changing-the-theme-programmatically)
 * [`useTour`](https://marmelab.com/ra-enterprise/modules/ra-tour)<img class="icon" src="./img/premium.svg" />
diff --git a/docs/Theming.md b/docs/Theming.md
index d9caacc9ae8..618fdc2a923 100644
--- a/docs/Theming.md
+++ b/docs/Theming.md
@@ -7,47 +7,68 @@ title: "Theming"
 
 Whether you need to adjust a CSS rule for a single component, or change the color of the labels in the entire app, you're covered!
 
-## Overriding A Component Style
+## `sx`: Overriding A Component Style
 
-Every react-admin component exposes an `sx` property from MUI besides providing a `className` property, which is always applied to the root element.
+All react-admin components expose an `sx` property, which allows to customize the component style. It uses the CSS-in-JS solution offered by MUI, [MUI System](https://mui.com/system/basics/#the-sx-prop). This `sx` prop is kind of like [React's `style` prop](https://reactjs.org/docs/dom-elements.html#style), but it's more powerful.
 
-Here is an example customizing an `EditButton` component inside a `Datagrid`, using the `sx` property from MUI:
+{% raw %}
+```jsx
+// It supports all CSS properties, plus some shorthand for common CSS properties,
+// e.g. pt for paddingTop
+<CardContent sx={{ pt: 2 }} />
+
+// It allows to style pseudo-elements, like :hover or :last-child
+<CardContent sx={{ pt: 2, "&:last-child": { pb: 2 } }} />
+
+// It allows responsive values without media queries:
+<Box
+    sx={{
+        width: {
+            xs: 100, // theme.breakpoints.up('xs')
+            sm: 200, // theme.breakpoints.up('sm')
+            md: 300, // theme.breakpoints.up('md')
+            lg: 400, // theme.breakpoints.up('lg')
+            xl: 500, // theme.breakpoints.up('xl')
+        },
+    }}
+>
+    This box has a responsive width.
+</Box>
+
+// It allows to style children, e.g. to style the <Avatar> inside a <Card>:
+<Card sx={{ '& .MuiAvatar': { width: 48, height: 48 } }} />
+
+// It allows to use theme variables, like the spacing or the palette colors:
+<Card sx={{ bgcolor: "grey.200" }} />
+
+// It offers property name autocompletion in IDEs thanks to TypeScript
+```
+{% endraw %}
+
+Check [The MUI documentation on the `sx` prop](https://mui.com/material-ui/customization/how-to-customize/#1-one-off-customization) for more information.
+
+Here is an example: Customizing the `<EditButton>` inside a `<Datagrid>`, using the `sx` prop:
 
 {% raw %}
 ```jsx
-import * as React from 'react';
 import { NumberField, List, Datagrid, TextField, EditButton } from 'react-admin';
 
-const MyEditButton = (props) => (
-    <EditButton
-        sx={{
-            fontWeight: "bold",
-            // This is CSS-in-JS syntax to target a deeper element using css selector, here the svg icon for this button
-            "& svg": { color: "orange" },
-        }}
-        {...props}
-    />
-);
-
 export const ProductList = () => (
     <List>
         <Datagrid>
             <TextField source="sku" />
             <TextField source="price" />
-            <MyEditButton />
+            <EditButton sx={{ fontWeight: "bold" }}/>
         </Datagrid>
     </List>
 );
 ```
 {% endraw %}
 
-For some components, you may want to override not only the root component style, but also the style of components inside the root. In this case, you can take advantage of the `sx` property to customize the CSS API that the component uses internally.
-
-Here is an example using the `sx` property of the `<Datagrid>` component:
+Here is another example, illustrating the ability to customize a specific part of a component - here, only the header of a `<Datagrid>`:
 
 {% raw %}
 ```jsx
-import * as React from 'react';
 import {
     BooleanField,
     Datagrid,
@@ -62,14 +83,11 @@ import Icon from '@mui/icons-material/Person';
 
 export const VisitorIcon = Icon;
 
-// The `Datagrid` component uses MUI System, and supports overriding styles through the `sx` property 
 export const PostList = () => (
     <List>
         <Datagrid
             sx={{
-                "&.RaDatagrid-table": { // No space between & and .
-                    backgroundColor: "Lavender",
-                },
+                backgroundColor: "Lavender",
                 "& .RaDatagrid-headerCell": {
                     backgroundColor: "MistyRose",
                 },
@@ -92,9 +110,289 @@ This example results in:
 
 ![Visitor List with customized CSS classes](./img/list_with_customized_css.png)
 
-Take a look at a component documentation and source code to know which classes are available for styling. For instance, you can have a look at the [Datagrid CSS documentation](./Datagrid.md#sx-css-api).
+To guess the name of the subclass to use (like `.RaDatagrid-headerCell` above) for customizing a component, you can use the developer tools of your browser:
+
+![Developer tools](./img/sx-class-name.png)
+
+The react-admin documentation for individual components also lists the classes available for styling. For instance, here is the [Datagrid CSS documentation](./Datagrid.md#sx-css-api):
+
+![Datagrid CSS documentation](./img/sx-documentation.png)
+
+## Reusable Components
+
+To reuse the same style overrides in different locations across your application, create a reusable component using [the MUI `styled()` utility](https://mui.com/system/styled/). It's a function that creates a new component based on a source component and custom styles. The basinc syntax is `styled(Component)(styles) => Component` (where `styles` forllows the same syntax as the `sx` prop).
+
+For instance, to create a custom `<Datagrid>` component with the header style defined in the previous section:
+
+```jsx
+// in src/MyDatagrid.js
+import { styled } from '@mui/system';
+import { Datagrid } from 'react-admin';
+
+export const MyDatagrid = styled(Datagrid)({
+    backgroundColor: "Lavender",
+    "& .RaDatagrid-headerCell": {
+        backgroundColor: "MistyRose",
+    },
+});
+```
+
+You can then use this component instead of react-admin's `<Datagrid>` component:
+
+{% raw %}
+```diff
+// in src/post/PostList.js
+import {
+    BooleanField,
+-   Datagrid,
+    DateField,
+    EditButton,
+    List,
+    NumberField,
+    TextField,
+    ShowButton,
+} from 'react-admin';
+import Icon from '@mui/icons-material/Person';
+
+export const VisitorIcon = Icon;
+
++import { MyDatagrid } from '../MyDatagrid';
+
+export const PostList = () => (
+    <List>
+-       <Datagrid
+-           sx={{
+-               backgroundColor: "Lavender",
+-               "& .RaDatagrid-headerCell": {
+-                   backgroundColor: "MistyRose",
+-               },
+-           }}
+-       >
++       <MyDatagrid>
+            <TextField source="id" />
+            <TextField source="title" />
+            <DateField source="published_at" sortByOrder="DESC" />
+            <BooleanField source="commentable" sortable={false} />
+            <NumberField source="views" sortByOrder="DESC" />
+            <EditButton />
+            <ShowButton />
++       </MyDatagrid>
+-       </Datagrid>
+    </List>
+);
+```
+{% endraw %}
+
+Again, to guess the name of the subclass to use (like `.RaDatagrid-headerCell` above) for customizing a component, you can use the developer tools of your browser, or check the react-admin documentation for individual components (e.g. the [Datagrid CSS documentation](./Datagrid.md#sx-css-api)).
+
+**Tip**: If you need more control over the HTML code, you can also create your own [Field](./Fields.md#writing-your-own-field-component) and [Input](./Inputs.md#writing-your-own-input-component) components.
+
+## Global Theme Overrides
+
+If you want to override the style of a component across the entire application, you can use a custom theme, leveraging [the MUI Theming support](https://mui.com/material-ui/customization/theming/).
+
+React-admin already wraps the app with [a MUI `<ThemeProvider>`](https://mui.com/material-ui/customization/theming/#themeprovider). Pass a custom `theme` to the `<Admin>` component to override the style of the entire application:
+
+```jsx
+const theme = { ... };
+
+const App = () => (
+    <Admin theme={theme}>
+        // ...
+    </Admin>
+);
+```
+
+Leveraging this technique, you can create a custom theme that overrides the style of a component for the entire application.
+
+For instance, to create a custom theme that overrides the style of the `<Datagrid>` component:
+
+```jsx
+import { defaultTheme } from 'react-admin';
+
+const theme = {
+    ...defaultTheme,
+    components: {
+        ...defaultTheme.components,
+        RaDatagrid: {
+            root: {
+                backgroundColor: "Lavender",
+                "& .RaDatagrid-headerCell": {
+                    backgroundColor: "MistyRose",
+                },
+            }
+        }
+    }
+};
+
+const App = () => (
+    <Admin theme={theme}>
+        // ...
+    </Admin>
+);
+```
+
+There are 2 important gotchas here:
+
+- Don't forget to merge your custom style overrides with the ones from react-admin's `defaultTheme`, otherwise the application will have the default MUI theme (most notably, outlined text inputs)
+- Custom style overrides must live under a `root` key. Then, the style override syntax is the same as the one used for the `sx` prop.
+
+Note that you don't need to call `createTheme` yourself. React-admin will do it for you.
+
+Again, to guess the name of the subclass to use (like `.RaDatagrid-headerCell` above) for customizing a component, you can use the developer tools of your browser, or check the react-admin documentation for individual components (e.g. the [Datagrid CSS documentation](./Datagrid.md#sx-css-api)).
+
+You can use this technique to override not only styles, but also default for components. That's how react-admin applies the `filled` variant to all `TextField` components. So for instance, to change the variant to `outlined`, create a custom theme as follows:
+
+```jsx
+import { defaultTheme } from 'react-admin';
+
+const theme = {
+    ...defaultTheme,
+    components: {
+        ...defaultTheme.components,
+        MuiTextField: {
+            defaultProps: {
+                variant: 'outlined',
+            },
+        },
+        MuiFormControl: {
+            defaultProps: {
+                variant: 'outlined',
+            },
+        },
+    }
+};
+```
+
+**Tip**: TypeScript will be picky when overriding the `variant` `defaultProp`. To avoid compilation errors, type the `variant` value as `const`:
+
+```ts
+import { defaultTheme } from 'react-admin';
+
+const theme = {
+    ...defaultTheme,
+    components: {
+        ...defaultTheme.components,
+        MuiTextField: {
+            defaultProps: {
+                variant: 'outlined' as const,
+            },
+        },
+        MuiFormControl: {
+            defaultProps: {
+                variant: 'outlined' as const,
+            },
+        },
+    }
+};
+```
+
+## Writing a Custom Theme
+
+MUI theming also allows to change the default palette, typography, colors, etc. This is very useful to change the react-admin style to match the branding of your company.
+
+For instance, here is how to override the default react-admin colors and fonts:
+
+```jsx
+import { defaultTheme } from 'react-admin';
+import indigo from '@mui/material/colors/indigo';
+import pink from '@mui/material/colors/pink';
+import red from '@mui/material/colors/red';
+
+const myTheme = {
+    ...defaultTheme,
+    palette: {
+        primary: indigo,
+        secondary: pink,
+        error: red,
+        contrastThreshold: 3,
+        tonalOffset: 0.2,
+    },
+    typography: {
+        // Use the system font instead of the default Roboto font.
+        fontFamily: ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Arial', 'sans-serif'].join(','),
+    },
+};
+```
+
+A `theme` object can contain the following keys:
+
+* `breakpoints`
+* `direction`
+* `mixins`
+* `components`
+* `palette`
+* `props`
+* `shadows`
+* `spacing`
+* `transitions`
+* `typography`
+* `zIndex`
+
+**Tip**: Check [MUI default theme documentation](https://mui.com/customization/default-theme/) to see the default values and meaning for these keys.
 
-If you need more control over the HTML code, you can also create your own [Field](./Fields.md#writing-your-own-field-component) and [Input](./Inputs.md#writing-your-own-input-component) components.
+Once your theme is defined, pass it to the `<Admin>` component, in the `theme` prop.
+
+```jsx
+const App = () => (
+    <Admin theme={myTheme} dataProvider={...}>
+        // ...
+    </Admin>
+);
+```
+
+## Light and Dark Themes
+
+MUI ships two base themes: light and dark. React-admin uses the light one by default. To use the dark theme, create a custom theme object with a `mode: 'dark'` palette, and pass it as the `<Admin theme>` prop:
+
+```jsx
+import { defaultTheme } from 'react-admin';
+const theme = {
+    ...defaultTheme,
+    palette: {
+        mode: 'dark', // Switching the dark mode on is a single property value change.
+    },
+};
+
+const App = () => (
+    <Admin theme={theme} dataProvider={...}>
+        // ...
+    </Admin>
+);
+```
+
+![Dark theme](./img/dark-theme.png)
+
+If you want to let users choose between the light and dark themes, check the next section.
+
+## Changing the Theme Programmatically
+
+You can define several themes (usually a light and a dark theme), and let the user choose between them.
+
+React-admin provides the `useTheme` hook to read and update the theme programmatically. It uses the same syntax as `useState`:
+
+```jsx
+import { defaultTheme, useTheme } from 'react-admin';
+import { Button } from '@mui/material';
+
+const lightTheme = defaultTheme;
+const darkTheme = {
+    ...defaultTheme,
+    palette: {
+        mode: 'dark',
+    },
+};
+
+const ThemeToggler = () => {
+    const [theme, setTheme] = useTheme();
+
+    return (
+        <Button onClick={() => setTheme(theme.palette.mode === 'dark' ? lightTheme : darkTheme)}>
+            {theme.palette.mode === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}
+        </Button>
+    );
+}
+```
 
 ## Conditional Formatting
 
@@ -237,116 +535,6 @@ export const PostList = () => {
 
 **Tip**: Previous versions of react-admin shipped a `<Responsive>` component to do media queries. This component is now deprecated. Use `useMediaQuery` instead.
 
-## Using a Predefined Theme
-
-MUI also supports [complete theming](https://mui.com/customization/themes) out of the box. MUI ships two base themes: light and dark. React-admin uses the light one by default. To use the dark one, pass it to the `<Admin>` component, in the `theme` prop.
-
-```jsx
-const theme = {
-    palette: {
-        mode: 'dark', // Switching the dark mode on is a single property value change.
-    },
-};
-
-const App = () => (
-    <Admin theme={theme} dataProvider={simpleRestProvider('http://path.to.my.api')}>
-        // ...
-    </Admin>
-);
-```
-
-![Dark theme](./img/dark-theme.png)
-
-## Writing a Custom Theme
-
-If you need more fine-tuning, you'll need to write your own `theme` object, following [MUI themes documentation](https://mui.com/customization/themes/).
-
-For instance, here is how to override the default react-admin theme:
-
-```jsx
-import { defaultTheme } from 'react-admin';
-import merge from 'lodash/merge';
-import indigo from '@mui/material/colors/indigo';
-import pink from '@mui/material/colors/pink';
-import red from '@mui/material/colors/red';
-
-const myTheme = merge({}, defaultTheme, {
-    palette: {
-        primary: indigo,
-        secondary: pink,
-        error: red,
-        contrastThreshold: 3,
-        tonalOffset: 0.2,
-    },
-    typography: {
-        // Use the system font instead of the default Roboto font.
-        fontFamily: ['-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Arial', 'sans-serif'].join(','),
-    },
-    components: {
-        MuiButton: { // override the styles of all instances of this component
-            styleOverrides: {
-                root: { // Name of the rule
-                    color: 'white', // Some CSS
-                },
-            },
-        },
-    },
-});
-```
-
-A `theme` object can contain the following keys:
-
-* `breakpoints`
-* `direction`
-* `mixins`
-* `components`
-* `palette`
-* `props`
-* `shadows`
-* `spacing`
-* `transitions`
-* `typography`
-* `zIndex`
-
-**Tip**: Check [MUI default theme documentation](https://mui.com/customization/default-theme/) to see the default values and meaning for these keys.
-
-Once your theme is defined, pass it to the `<Admin>` component, in the `theme` prop.
-
-```jsx
-const App = () => (
-    <Admin theme={myTheme} dataProvider={simpleRestProvider('http://path.to.my.api')}>
-        // ...
-    </Admin>
-);
-```
-
-## Changing the Theme Programmatically
-
-React-admin provides the `useTheme` hook to read and update the theme programmatically. It uses the same syntax as `useState`:
-
-```jsx
-import { defaultTheme, useTheme } from 'react-admin';
-import { Button } from '@mui/material';
-
-const lightTheme = defaultTheme;
-const darkTheme = {
-    ...defaultTheme,
-    palette: {
-        mode: 'dark',
-    },
-};
-
-const ThemeToggler = () => {
-    const [theme, setTheme] = useTheme();
-
-    return (
-        <Button onClick={() => setTheme(theme.palette.mode === 'dark' ? lightTheme : darkTheme)}>
-            {theme.palette.mode === 'dark' ? 'Switch to light theme' : 'Switch to dark theme'}
-        </Button>
-    );
-}
-```
-
 ## Using a Custom Layout
 
 Instead of the default layout, you can use your own component as the admin layout. Just use the `layout` prop of the `<Admin>` component:
@@ -356,7 +544,7 @@ Instead of the default layout, you can use your own component as the admin layou
 import MyLayout from './MyLayout';
 
 const App = () => (
-    <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
+    <Admin layout={MyLayout} dataProvider={...}>
         // ...
     </Admin>
 );
@@ -506,7 +694,7 @@ const theme = {
 };
 
 const App = () => (
-    <Admin theme={theme} dataProvider={simpleRestProvider('http://path.to.my.api')}>
+    <Admin theme={theme} dataProvider={...}>
         // ...
     </Admin>
 );
@@ -724,7 +912,7 @@ Then, use this layout in the `<Admin>` with the `layout` prop:
 import MyLayout from './MyLayout';
 
 const App = () => (
-    <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
+    <Admin layout={MyLayout} dataProvider={...}>
         // ...
     </Admin>
 );
@@ -856,7 +1044,7 @@ Then, use this layout in the `<Admin>` `layout` prop:
 import { Layout }  from './Layout';
 
 const App = () => (
-    <Admin layout={Layout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
+    <Admin layout={Layout} dataProvider={...}>
         // ...
     </Admin>
 );
@@ -1125,7 +1313,7 @@ Then, use this layout in the `<Admin>` `layout` prop:
 import MyLayout from './MyLayout';
 
 const App = () => (
-    <Admin layout={MyLayout} dataProvider={simpleRestProvider('http://path.to.my.api')}>
+    <Admin layout={MyLayout} dataProvider={...}>
         // ...
     </Admin>
 );
diff --git a/docs/Upgrade.md b/docs/Upgrade.md
index 614f9675cf9..0acd63ee993 100644
--- a/docs/Upgrade.md
+++ b/docs/Upgrade.md
@@ -3081,6 +3081,51 @@ If you created a custom app (without the `<Admin>` component), you may have used
 
 ## Miscellaneous
 
+### Custom Themes Must Extend `defaultTheme`
+
+If you pass a custom object to `<Admin theme>` to change the look and feel of your application, the object must now extend the `defaultTheme` object, or the inputs will default to the `outlined` variant instead of the `filled` variant:
+
+```diff
++import { defaultTheme } from 'react-admin';
+
+const theme = {
++   ...defaultTheme,
+    components: {
++       ...defaultTheme.components,
+        RaDatagrid: {
+            root: {
+                backgroundColor: "Lavender",
+                "& .RaDatagrid-headerCell": {
+                    backgroundColor: "MistyRose",
+                },
+            }
+        }
+    }
+};
+```
+
+Alternately, set the default `variant`  for `MuiTextField` to `filled`:
+
+```diff
+const theme = {
+    components: {
+        RaDatagrid: {
+            root: {
+                backgroundColor: "Lavender",
+                "& .RaDatagrid-headerCell": {
+                    backgroundColor: "MistyRose",
+                },
+            }
+        }
++       MuiTextField: {
++           defaultProps: {
++               variant: 'filled',
++           }
++       }
+    }
+};
+```
+
 ### `useNotify` Now Takes An Options Object
 
 When a component has to display a notification, developers may want to tweak the type, duration, translation arguments, or the ability to undo the action. The callback returned by `useNotify()` used to accept a long series of argument, but the syntax wasn't very intuitive. To improve the developer experience, these options are now part of an `options` object, passed as second argument.
diff --git a/docs/img/sx-class-name.png b/docs/img/sx-class-name.png
new file mode 100644
index 00000000000..161dd46428a
Binary files /dev/null and b/docs/img/sx-class-name.png differ
diff --git a/docs/img/sx-documentation.png b/docs/img/sx-documentation.png
new file mode 100644
index 00000000000..51945b1f867
Binary files /dev/null and b/docs/img/sx-documentation.png differ
diff --git a/examples/crm/src/App.tsx b/examples/crm/src/App.tsx
index 378b1e308a6..8ac44946694 100644
--- a/examples/crm/src/App.tsx
+++ b/examples/crm/src/App.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { Admin, Resource, ListGuesser } from 'react-admin';
+import { Admin, Resource, ListGuesser, defaultTheme } from 'react-admin';
 import { dataProvider } from './dataProvider';
 import { authProvider } from './authProvider';
 import Layout from './Layout';
@@ -15,6 +15,7 @@ const App = () => (
         layout={Layout}
         dashboard={Dashboard}
         theme={{
+            ...defaultTheme,
             palette: {
                 background: {
                     default: '#fafafb',
diff --git a/examples/demo/src/layout/themes.ts b/examples/demo/src/layout/themes.ts
index 7a124360ad8..d8bfcb25eec 100644
--- a/examples/demo/src/layout/themes.ts
+++ b/examples/demo/src/layout/themes.ts
@@ -1,3 +1,5 @@
+import { defaultTheme } from 'react-admin';
+
 export const darkTheme = {
     palette: {
         primary: {
@@ -12,6 +14,7 @@ export const darkTheme = {
         width: 200,
     },
     components: {
+        ...defaultTheme.components,
         RaMenuItemLink: {
             styleOverrides: {
                 root: {
@@ -30,32 +33,6 @@ export const darkTheme = {
                 },
             },
         },
-        MuiButtonBase: {
-            defaultProps: {
-                // disable ripple for perf reasons
-                disableRipple: true,
-            },
-            styleOverrides: {
-                root: {
-                    '&:hover:active::after': {
-                        // recreate a static ripple color
-                        // use the currentColor to make it work both for outlined and contained buttons
-                        // but to dim the background without dimming the text,
-                        // put another element on top with a limited opacity
-                        content: '""',
-                        display: 'block',
-                        width: '100%',
-                        height: '100%',
-                        position: 'absolute',
-                        top: 0,
-                        right: 0,
-                        backgroundColor: 'currentColor',
-                        opacity: 0.3,
-                        borderRadius: 'inherit',
-                    },
-                },
-            },
-        },
     },
 };
 
@@ -82,6 +59,7 @@ export const lightTheme = {
         width: 200,
     },
     components: {
+        ...defaultTheme.components,
         RaMenuItemLink: {
             styleOverrides: {
                 root: {
@@ -103,32 +81,6 @@ export const lightTheme = {
                 },
             },
         },
-        MuiButtonBase: {
-            defaultProps: {
-                // disable ripple for perf reasons
-                disableRipple: true,
-            },
-            styleOverrides: {
-                root: {
-                    '&:hover:active::after': {
-                        // recreate a static ripple color
-                        // use the currentColor to make it work both for outlined and contained buttons
-                        // but to dim the background without dimming the text,
-                        // put another element on top with a limited opacity
-                        content: '""',
-                        display: 'block',
-                        width: '100%',
-                        height: '100%',
-                        position: 'absolute',
-                        top: 0,
-                        right: 0,
-                        backgroundColor: 'currentColor',
-                        opacity: 0.3,
-                        borderRadius: 'inherit',
-                    },
-                },
-            },
-        },
         MuiAppBar: {
             styleOverrides: {
                 colorSecondary: {
@@ -147,22 +99,5 @@ export const lightTheme = {
                 },
             },
         },
-        MuiFilledInput: {
-            styleOverrides: {
-                root: {
-                    backgroundColor: 'rgba(0, 0, 0, 0.04)',
-                    '&$disabled': {
-                        backgroundColor: 'rgba(0, 0, 0, 0.04)',
-                    },
-                },
-            },
-        },
-        MuiSnackbarContent: {
-            styleOverrides: {
-                root: {
-                    border: 'none',
-                },
-            },
-        },
     },
 };
diff --git a/packages/ra-ui-materialui/src/defaultTheme.ts b/packages/ra-ui-materialui/src/defaultTheme.ts
index 9deab6a8c35..29a8f77f8d5 100644
--- a/packages/ra-ui-materialui/src/defaultTheme.ts
+++ b/packages/ra-ui-materialui/src/defaultTheme.ts
@@ -75,6 +75,20 @@ export const defaultTheme = {
                 },
             },
         },
+        MuiTextField: {
+            defaultProps: {
+                variant: 'filled' as const,
+                margin: 'dense' as const,
+                size: 'small' as const,
+            },
+        },
+        MuiFormControl: {
+            defaultProps: {
+                variant: 'filled' as const,
+                margin: 'dense' as const,
+                size: 'small' as const,
+            },
+        },
     },
 };
 
diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
index 7789db87af6..e5551c6c967 100644
--- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
+++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
@@ -143,7 +143,7 @@ export const AutocompleteInput = <
         label,
         limitChoicesToValue,
         matchSuggestion,
-        margin = 'dense',
+        margin,
         fieldState: fieldStateOverride,
         filterToQuery = DefaultFilterToQuery,
         formState: formStateOverride,
@@ -159,13 +159,13 @@ export const AutocompleteInput = <
         resource: resourceProp,
         shouldRenderSuggestions,
         setFilter,
-        size = 'small',
+        size,
         source: sourceProp,
         suggestionLimit,
         TextFieldProps,
         translateChoice,
         validate,
-        variant = 'filled',
+        variant,
         ...rest
     } = props;
 
diff --git a/packages/ra-ui-materialui/src/input/DateInput.tsx b/packages/ra-ui-materialui/src/input/DateInput.tsx
index 57e4ce20c54..4e89164b092 100644
--- a/packages/ra-ui-materialui/src/input/DateInput.tsx
+++ b/packages/ra-ui-materialui/src/input/DateInput.tsx
@@ -39,12 +39,12 @@ export const DateInput = ({
     source,
     resource,
     helperText,
-    margin = 'dense',
+    margin,
     onBlur,
     onChange,
     parse,
     validate,
-    variant = 'filled',
+    variant,
     ...rest
 }: DateInputProps) => {
     const { field, fieldState, formState, id, isRequired } = useInput({
diff --git a/packages/ra-ui-materialui/src/input/DateTimeInput.tsx b/packages/ra-ui-materialui/src/input/DateTimeInput.tsx
index c2c26786dc4..50468432aa3 100644
--- a/packages/ra-ui-materialui/src/input/DateTimeInput.tsx
+++ b/packages/ra-ui-materialui/src/input/DateTimeInput.tsx
@@ -26,14 +26,14 @@ export const DateTimeInput = ({
     format = formatDateTime,
     label,
     helperText,
-    margin = 'dense',
+    margin,
     onBlur,
     onChange,
     source,
     resource,
     parse = parseDateTime,
     validate,
-    variant = 'filled',
+    variant,
     ...rest
 }: DateTimeInputProps) => {
     const { field, fieldState, formState, id, isRequired } = useInput({
diff --git a/packages/ra-ui-materialui/src/input/NullableBooleanInput.tsx b/packages/ra-ui-materialui/src/input/NullableBooleanInput.tsx
index b2eb83257e1..fef331ecefc 100644
--- a/packages/ra-ui-materialui/src/input/NullableBooleanInput.tsx
+++ b/packages/ra-ui-materialui/src/input/NullableBooleanInput.tsx
@@ -16,14 +16,14 @@ export const NullableBooleanInput = (props: NullableBooleanInputProps) => {
         format = getStringFromBoolean,
         helperText,
         label,
-        margin = 'dense',
+        margin,
         onBlur,
         onChange,
         parse = getBooleanFromString,
         resource,
         source,
         validate,
-        variant = 'filled',
+        variant,
         nullLabel = 'ra.boolean.null',
         falseLabel = 'ra.boolean.false',
         trueLabel = 'ra.boolean.true',
diff --git a/packages/ra-ui-materialui/src/input/NumberInput.tsx b/packages/ra-ui-materialui/src/input/NumberInput.tsx
index 0d82db6e3f8..66a9de41ef8 100644
--- a/packages/ra-ui-materialui/src/input/NumberInput.tsx
+++ b/packages/ra-ui-materialui/src/input/NumberInput.tsx
@@ -26,7 +26,7 @@ export const NumberInput = ({
     format = convertNumberToString,
     helperText,
     label,
-    margin = 'dense',
+    margin,
     onBlur,
     onChange,
     parse = convertStringToNumber,
@@ -36,7 +36,7 @@ export const NumberInput = ({
     min,
     max,
     validate,
-    variant = 'filled',
+    variant,
     inputProps: overrideInputProps,
     ...rest
 }: NumberInputProps) => {
diff --git a/packages/ra-ui-materialui/src/input/ResettableTextField.tsx b/packages/ra-ui-materialui/src/input/ResettableTextField.tsx
index fa9697b1e00..bfb71e179b5 100644
--- a/packages/ra-ui-materialui/src/input/ResettableTextField.tsx
+++ b/packages/ra-ui-materialui/src/input/ResettableTextField.tsx
@@ -23,8 +23,8 @@ export const ResettableTextField = forwardRef(
             value,
             resettable,
             disabled,
-            variant = 'filled',
-            margin = 'dense',
+            variant,
+            margin,
             className,
             ...rest
         } = props;
diff --git a/packages/ra-ui-materialui/src/input/SelectArrayInput.tsx b/packages/ra-ui-materialui/src/input/SelectArrayInput.tsx
index f7946d062fe..f521e21598a 100644
--- a/packages/ra-ui-materialui/src/input/SelectArrayInput.tsx
+++ b/packages/ra-ui-materialui/src/input/SelectArrayInput.tsx
@@ -95,7 +95,7 @@ export const SelectArrayInput = (props: SelectArrayInputProps) => {
         label,
         isFetching: isFetchingProp,
         isLoading: isLoadingProp,
-        margin = 'dense',
+        margin,
         onBlur,
         onChange,
         onCreate,
@@ -106,7 +106,7 @@ export const SelectArrayInput = (props: SelectArrayInputProps) => {
         source: sourceProp,
         translateChoice,
         validate,
-        variant = 'filled',
+        variant,
         ...rest
     } = props;
 
diff --git a/packages/ra-ui-materialui/src/list/filter/FilterLiveSearch.tsx b/packages/ra-ui-materialui/src/list/filter/FilterLiveSearch.tsx
index 3fe98b3a4cf..8c2875c09d2 100644
--- a/packages/ra-ui-materialui/src/list/filter/FilterLiveSearch.tsx
+++ b/packages/ra-ui-materialui/src/list/filter/FilterLiveSearch.tsx
@@ -28,7 +28,7 @@ export const FilterLiveSearch = memo(
         sx?: SxProps;
         variant?: 'filled' | 'outlined';
     }) => {
-        const { source = 'q', variant = 'filled', ...rest } = props;
+        const { source = 'q', variant, ...rest } = props;
         const { filterValues, setFilters } = useListFilterContext();
         const translate = useTranslate();