diff --git a/docs/SimpleForm.md b/docs/SimpleForm.md
index 480d6166428..8bf0b8cb948 100644
--- a/docs/SimpleForm.md
+++ b/docs/SimpleForm.md
@@ -492,3 +492,64 @@ export const UserCreate = () => {
 }
 ```
 {% endraw %}
+
+## Configurable
+
+You can let end users customize the fields displayed in the `<SimpleForm>` by using the `<SimpleFormConfigurable>` component instead.
+
+![SimpleFormConfigurable](./img/SimpleFormConfigurable.gif)
+
+```diff
+import {
+    Edit,
+-   SimpleForm,
++   SimpleFormConfigurable,
+    TextInput,
+} from 'react-admin';
+
+const PostEdit = () => (
+    <Edit>
+-       <SimpleForm>
++       <SimpleFormConfigurable>
+            <TextInput source="title" />
+            <TextInput source="author" />
+            <TextInput source="year" />
+-       </SimpleForm>
++       </SimpleFormConfigurable>
+    </Edit>
+);
+```
+
+When users enter the configuration mode and select the `<SimpleForm>`, they can show / hide SimpleForm inputs.
+
+By default, `<SimpleFormConfigurable>` renders all child inputs. But you can also omit some of them by passing an `omit` prop containing an array of input sources:
+
+```jsx
+// By default, hide the author input
+// users can choose to show it in configuration mode
+const PostEdit = () => (
+    <Edit>
+        <SimpleFormConfigurable omit={['author']}>
+            <TextInput source="title" />
+            <TextInput source="author" />
+            <TextInput source="year" />
+        </SimpleFormConfigurable>
+    </Edit>
+);
+```
+
+If you render more than one `<SimpleFormConfigurable>` in the same page, you must pass a unique `preferenceKey` prop to each one:
+
+```jsx
+const PostEdit = () => (
+    <Edit>
+        <SimpleFormConfigurable preferenceKey="posts.simpleForm">
+            <TextInput source="title" />
+            <TextInput source="author" />
+            <TextInput source="year" />
+        </SimpleFormConfigurable>
+    </Edit>
+);
+```
+
+`<SimpleFormConfigurable>` accepts the same props as `<SimpleForm>`.
diff --git a/docs/img/SimpleFormConfigurable.gif b/docs/img/SimpleFormConfigurable.gif
new file mode 100644
index 00000000000..46192fe033e
Binary files /dev/null and b/docs/img/SimpleFormConfigurable.gif differ
diff --git a/examples/simple/src/comments/CommentCreate.tsx b/examples/simple/src/comments/CommentCreate.tsx
index 854c69f2ca3..3a75ad9dc71 100644
--- a/examples/simple/src/comments/CommentCreate.tsx
+++ b/examples/simple/src/comments/CommentCreate.tsx
@@ -4,7 +4,7 @@ import {
     Create,
     DateInput,
     TextInput,
-    SimpleForm,
+    SimpleFormConfigurable,
     required,
     minLength,
 } from 'react-admin'; // eslint-disable-line import/no-unresolved
@@ -15,7 +15,7 @@ const defaultSort = { field: 'title', order: 'ASC' };
 
 const CommentCreate = () => (
     <Create redirect={false}>
-        <SimpleForm>
+        <SimpleFormConfigurable>
             <PostReferenceInput
                 source="post_id"
                 reference="posts"
@@ -26,7 +26,7 @@ const CommentCreate = () => (
             <TextInput source="author.name" validate={minLength(10)} />
             <DateInput source="created_at" defaultValue={now} />
             <TextInput fullWidth source="body" multiline />
-        </SimpleForm>
+        </SimpleFormConfigurable>
     </Create>
 );
 
diff --git a/examples/simple/src/posts/PostCreate.tsx b/examples/simple/src/posts/PostCreate.tsx
index 50c9a480bf0..40d9f1c1b8d 100644
--- a/examples/simple/src/posts/PostCreate.tsx
+++ b/examples/simple/src/posts/PostCreate.tsx
@@ -17,7 +17,7 @@ import {
     ReferenceInput,
     SaveButton,
     SelectInput,
-    SimpleForm,
+    SimpleFormConfigurable,
     SimpleFormIterator,
     TextInput,
     Toolbar,
@@ -103,7 +103,7 @@ const PostCreate = () => {
     const dateDefaultValue = useMemo(() => new Date(), []);
     return (
         <Create redirect="edit">
-            <SimpleForm
+            <SimpleFormConfigurable
                 toolbar={<PostCreateToolbar />}
                 defaultValues={defaultValues}
             >
@@ -193,7 +193,7 @@ const PostCreate = () => {
                         </SimpleFormIterator>
                     </ArrayInput>
                 )}
-            </SimpleForm>
+            </SimpleFormConfigurable>
         </Create>
     );
 };
diff --git a/examples/simple/src/tags/TagCreate.tsx b/examples/simple/src/tags/TagCreate.tsx
index 1e96b259b70..3c8537aebc1 100644
--- a/examples/simple/src/tags/TagCreate.tsx
+++ b/examples/simple/src/tags/TagCreate.tsx
@@ -2,8 +2,7 @@
 import * as React from 'react';
 import {
     Create,
-    SimpleForm,
-    TextField,
+    SimpleFormConfigurable,
     TextInput,
     required,
     TranslatableInputs,
@@ -11,12 +10,11 @@ import {
 
 const TagCreate = () => (
     <Create redirect="list">
-        <SimpleForm>
-            <TextField source="id" />
+        <SimpleFormConfigurable>
             <TranslatableInputs locales={['en', 'fr']}>
                 <TextInput source="name" validate={[required()]} />
             </TranslatableInputs>
-        </SimpleForm>
+        </SimpleFormConfigurable>
     </Create>
 );
 
diff --git a/examples/simple/src/tags/TagEdit.tsx b/examples/simple/src/tags/TagEdit.tsx
index 7b7804bb3d6..3def44f7f07 100644
--- a/examples/simple/src/tags/TagEdit.tsx
+++ b/examples/simple/src/tags/TagEdit.tsx
@@ -3,7 +3,7 @@ import * as React from 'react';
 import { useParams } from 'react-router';
 import {
     Edit,
-    SimpleForm,
+    SimpleFormConfigurable,
     TextField,
     TextInput,
     required,
@@ -19,12 +19,12 @@ const TagEdit = () => {
     return (
         <>
             <Edit redirect="list">
-                <SimpleForm warnWhenUnsavedChanges>
+                <SimpleFormConfigurable warnWhenUnsavedChanges>
                     <TextField source="id" />
                     <TranslatableInputs locales={['en', 'fr']}>
                         <TextInput source="name" validate={[required()]} />
                     </TranslatableInputs>
-                </SimpleForm>
+                </SimpleFormConfigurable>
             </Edit>
             <ResourceContextProvider value="posts">
                 <List
diff --git a/packages/ra-core/src/i18n/TranslationMessages.ts b/packages/ra-core/src/i18n/TranslationMessages.ts
index b48afdf9bff..8636df93e8d 100644
--- a/packages/ra-core/src/i18n/TranslationMessages.ts
+++ b/packages/ra-core/src/i18n/TranslationMessages.ts
@@ -179,15 +179,23 @@ export interface TranslationMessages extends StringMap {
         configurable?: {
             customize: string;
             configureMode: string;
-            Datagrid: {
-                unlabeled: string;
-            };
             inspector: {
                 title: string;
                 content: string;
                 reset: string;
+                hideAll: string;
+                showAll: string;
+            };
+            Datagrid: {
+                title: string;
+                unlabeled: string;
+            };
+            SimpleForm: {
+                title: string;
+                unlabeled: string;
             };
             SimpleList: {
+                title: string;
                 primaryText: string;
                 secondaryText: string;
                 tertiaryText: string;
diff --git a/packages/ra-language-english/src/index.ts b/packages/ra-language-english/src/index.ts
index d7c35cf0d7c..83d12eed79e 100644
--- a/packages/ra-language-english/src/index.ts
+++ b/packages/ra-language-english/src/index.ts
@@ -180,15 +180,23 @@ const englishMessages: TranslationMessages = {
         configurable: {
             customize: 'Customize',
             configureMode: 'Configure this page',
-            Datagrid: {
-                unlabeled: 'Unlabeled column #%{column}',
-            },
             inspector: {
                 title: 'Inspector',
                 content: 'Hover the application UI elements to configure them',
                 reset: 'Reset Settings',
+                hideAll: 'Hide All',
+                showAll: 'Show All',
+            },
+            Datagrid: {
+                title: 'Datagrid',
+                unlabeled: 'Unlabeled column #%{column}',
+            },
+            SimpleForm: {
+                title: 'Form',
+                unlabeled: 'Unlabeled input #%{input}',
             },
             SimpleList: {
+                title: 'List',
                 primaryText: 'Primary text',
                 secondaryText: 'Secondary text',
                 tertiaryText: 'Tertiary text',
diff --git a/packages/ra-language-french/src/index.ts b/packages/ra-language-french/src/index.ts
index f07e287ddc7..960c7aa949b 100644
--- a/packages/ra-language-french/src/index.ts
+++ b/packages/ra-language-french/src/index.ts
@@ -186,15 +186,23 @@ const frenchMessages: TranslationMessages = {
         configurable: {
             customize: 'Personnaliser',
             configureMode: 'Configurer cette page',
-            Datagrid: {
-                unlabeled: 'Colonne sans label #%{column}',
-            },
             inspector: {
                 title: 'Inspecteur',
                 content: 'Sélectionner un composant pour le configurer',
                 reset: 'Réinitialiser',
+                hideAll: 'Masquer tout',
+                showAll: 'Afficher tout',
+            },
+            Datagrid: {
+                title: 'Tableau',
+                unlabeled: 'Colonne #%{column}',
+            },
+            SimpleForm: {
+                title: 'Formulaire',
+                unlabeled: 'Champ #%{input}',
             },
             SimpleList: {
+                title: 'Liste',
                 primaryText: 'Texte principal',
                 secondaryText: 'Texte secondaire',
                 tertiaryText: 'Texte annexe',
diff --git a/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.spec.tsx b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.spec.tsx
new file mode 100644
index 00000000000..877722aa3b8
--- /dev/null
+++ b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.spec.tsx
@@ -0,0 +1,47 @@
+import * as React from 'react';
+import { render, screen, fireEvent } from '@testing-library/react';
+import expect from 'expect';
+
+import { Basic, Omit, PreferenceKey } from './SimpleFormConfigurable.stories';
+
+describe('<SimpleFormConfigurable>', () => {
+    const enterConfigurationMode = async () => {
+        screen.getByLabelText('Configure mode').click();
+        await screen.findByText('Inspector');
+        fireEvent.mouseOver(screen.getAllByDisplayValue('War and Peace')[0]);
+        await screen.getByTitle('ra.configurable.customize').click();
+        await screen.findByText('Form');
+    };
+    it('should render a form with configurable inputs', async () => {
+        render(<Basic />);
+        await enterConfigurationMode();
+        expect(screen.queryByDisplayValue('Leo Tolstoy')).not.toBeNull();
+        screen.getAllByLabelText('Author')[0].click();
+        expect(screen.queryByDisplayValue('Leo Tolstoy')).toBeNull();
+        screen.getAllByLabelText('Author')[0].click();
+        expect(screen.queryByDisplayValue('Leo Tolstoy')).not.toBeNull();
+    });
+    describe('omit', () => {
+        it('should not render omitted inputs by default', async () => {
+            render(<Omit />);
+            expect(screen.queryByLabelText('Author')).toBeNull();
+            expect(screen.queryByDisplayValue('Leo Tolstoy')).toBeNull();
+            await enterConfigurationMode();
+            screen.getByLabelText('Author').click();
+            expect(screen.queryByDisplayValue('Leo Tolstoy')).not.toBeNull();
+        });
+    });
+    describe('preferenceKey', () => {
+        it('should allow two ConfigurableDatagrid not to share the same preferences', async () => {
+            render(<PreferenceKey />);
+            expect(screen.queryAllByDisplayValue('War and Peace')).toHaveLength(
+                2
+            );
+            await enterConfigurationMode();
+            screen.getAllByLabelText('Title')[0].click();
+            expect(screen.queryAllByDisplayValue('War and Peace')).toHaveLength(
+                1
+            );
+        });
+    });
+});
diff --git a/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.stories.tsx b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.stories.tsx
new file mode 100644
index 00000000000..e9991171e8b
--- /dev/null
+++ b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.stories.tsx
@@ -0,0 +1,99 @@
+import * as React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { PreferencesEditorContextProvider, I18nContextProvider } from 'ra-core';
+import { ThemeProvider, createTheme, Box, Paper } from '@mui/material';
+import { QueryClientProvider, QueryClient } from 'react-query';
+import polyglotI18nProvider from 'ra-i18n-polyglot';
+import en from 'ra-language-english';
+
+import { Inspector, InspectorButton } from '../preferences';
+import { NumberInput, TextInput } from '../input';
+import { SimpleFormConfigurable } from './SimpleFormConfigurable';
+import { defaultTheme } from '../defaultTheme';
+
+export default { title: 'ra-ui-materialui/forms/SimpleFormConfigurable' };
+
+const data = {
+    id: 1,
+    title: 'War and Peace',
+    author: 'Leo Tolstoy',
+    year: 1869,
+};
+
+const Wrapper = ({ children }) => (
+    <QueryClientProvider client={new QueryClient()}>
+        <ThemeProvider theme={createTheme(defaultTheme)}>
+            <PreferencesEditorContextProvider>
+                <MemoryRouter>
+                    <Inspector />
+                    <Box display="flex" justifyContent="flex-end">
+                        <InspectorButton />
+                    </Box>
+                    <Paper sx={{ width: 600, m: 2 }}>{children}</Paper>
+                </MemoryRouter>
+            </PreferencesEditorContextProvider>
+        </ThemeProvider>
+    </QueryClientProvider>
+);
+
+export const Basic = () => (
+    <Wrapper>
+        <SimpleFormConfigurable record={data} resource="books">
+            <TextInput source="title" fullWidth />
+            <TextInput source="author" />
+            <NumberInput source="year" />
+        </SimpleFormConfigurable>
+    </Wrapper>
+);
+
+export const Omit = () => (
+    <Wrapper>
+        <SimpleFormConfigurable
+            record={data}
+            resource="books2"
+            omit={['author']}
+        >
+            <TextInput source="title" fullWidth />
+            <TextInput source="author" />
+            <NumberInput source="year" />
+        </SimpleFormConfigurable>
+    </Wrapper>
+);
+
+export const PreferenceKey = () => (
+    <Wrapper>
+        <SimpleFormConfigurable
+            record={data}
+            resource="books3"
+            preferenceKey="pref1"
+        >
+            <TextInput source="title" fullWidth />
+            <TextInput source="author" />
+            <NumberInput source="year" />
+        </SimpleFormConfigurable>
+        <SimpleFormConfigurable
+            record={data}
+            resource="books3"
+            preferenceKey="pref2"
+        >
+            <TextInput source="title" fullWidth />
+            <TextInput source="author" />
+            <NumberInput source="year" />
+        </SimpleFormConfigurable>
+    </Wrapper>
+);
+
+const translations = { en };
+const i18nProvider = polyglotI18nProvider(locale => translations[locale], 'en');
+
+export const I18N = () => (
+    <I18nContextProvider value={i18nProvider}>
+        <Wrapper>
+            <SimpleFormConfigurable record={data} resource="books">
+                <TextInput source="title" fullWidth />
+                <TextInput source="author" />
+                <NumberInput source="year" />
+            </SimpleFormConfigurable>
+        </Wrapper>
+    </I18nContextProvider>
+);
diff --git a/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.tsx b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.tsx
new file mode 100644
index 00000000000..adaf0f2da69
--- /dev/null
+++ b/packages/ra-ui-materialui/src/form/SimpleFormConfigurable.tsx
@@ -0,0 +1,128 @@
+import * as React from 'react';
+import {
+    useResourceContext,
+    usePreference,
+    useStore,
+    useTranslate,
+} from 'ra-core';
+
+import { Configurable } from '../preferences';
+import { SimpleForm, SimpleFormProps } from './SimpleForm';
+import { SimpleFormEditor } from './SimpleFormEditor';
+
+export const SimpleFormConfigurable = ({
+    preferenceKey,
+    omit,
+    ...props
+}: SimpleFormConfigurableProps) => {
+    const translate = useTranslate();
+    const resource = useResourceContext(props);
+    const finalPreferenceKey = preferenceKey || `${resource}.simpleForm`;
+
+    const [availableInputs, setAvailableInputs] = useStore<
+        SimpleFormConfigurableColumn[]
+    >(`preferences.${finalPreferenceKey}.availableInputs`, []);
+
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const [_, setOmit] = useStore<string[]>(
+        `preferences.${finalPreferenceKey}.omit`,
+        omit
+    );
+
+    React.useEffect(() => {
+        // first render, or the preference have been cleared
+        const inputs = React.Children.map(props.children, (child, index) =>
+            React.isValidElement(child)
+                ? {
+                      index: String(index),
+                      source: child.props.source,
+                      label:
+                          child.props.source || child.props.label
+                              ? child.props.label
+                              : translate(
+                                    'ra.configurable.SimpleForm.unlabeled',
+                                    {
+                                        input: index,
+                                        _: `Unlabeled input #%{input}`,
+                                    }
+                                ),
+                  }
+                : null
+        ).filter(column => column != null);
+        if (JSON.stringify(inputs) !== JSON.stringify(availableInputs)) {
+            setAvailableInputs(inputs);
+            setOmit(omit);
+        }
+    }, [availableInputs]); // eslint-disable-line react-hooks/exhaustive-deps
+
+    return (
+        <Configurable
+            editor={<SimpleFormEditor />}
+            preferenceKey={finalPreferenceKey}
+            sx={{
+                display: 'block',
+                '&.RaConfigurable-editMode': {
+                    margin: '2px',
+                },
+            }}
+        >
+            <SimpleFormWithPreferences {...props} />
+        </Configurable>
+    );
+};
+
+export interface SimpleFormConfigurableProps extends SimpleFormProps {
+    /**
+     * Key to use to store the user's preferences for this SimpleForm.
+     *
+     * Set to '[resource].simpleForm' by default. Pass a custom key if you need
+     * to display more than one SimpleFormConfigurable per resource.
+     */
+    preferenceKey?: string;
+    /**
+     * columns to hide by default
+     *
+     * @example
+     * // by default, hide the id and author columns
+     * // users can choose to show show them in configuration mode
+     * const PostEdit = () => (
+     *     <Edit>
+     *         <SimpleFormConfigurable omit={['id', 'author']}>
+     *             <TextInput source="id" />
+     *             <TextInput source="title" />
+     *             <TextInput source="author" />
+     *             <TextInput source="year" />
+     *         </SimpleFormConfigurable>
+     *     </Edit>
+     * );
+     */
+    omit?: string[];
+}
+
+export interface SimpleFormConfigurableColumn {
+    index: string;
+    source: string;
+    label?: string;
+}
+
+/**
+ * This SimpleForm filters its children depending on preferences
+ */
+const SimpleFormWithPreferences = ({ children, ...props }: SimpleFormProps) => {
+    const [availableInputs] = usePreference('availableInputs', []);
+    const [omit] = usePreference('omit', []);
+    const [inputs] = usePreference(
+        'inputs',
+        availableInputs
+            .filter(input => !omit?.includes(input.source))
+            .map(input => input.index)
+    );
+    const childrenArray = React.Children.toArray(children);
+    return (
+        <SimpleForm {...props}>
+            {inputs === undefined
+                ? children
+                : inputs.map(index => childrenArray[index])}
+        </SimpleForm>
+    );
+};
diff --git a/packages/ra-ui-materialui/src/form/SimpleFormEditor.tsx b/packages/ra-ui-materialui/src/form/SimpleFormEditor.tsx
new file mode 100644
index 00000000000..521dc759b44
--- /dev/null
+++ b/packages/ra-ui-materialui/src/form/SimpleFormEditor.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react';
+import { useSetInspectorTitle } from 'ra-core';
+
+import { FieldsSelector } from '../preferences';
+
+export const SimpleFormEditor = () => {
+    useSetInspectorTitle('ra.inspector.SimpleForm.title', { _: 'Form' });
+
+    return <FieldsSelector name="inputs" availableName="availableInputs" />;
+};
diff --git a/packages/ra-ui-materialui/src/form/index.tsx b/packages/ra-ui-materialui/src/form/index.tsx
index df2af5c602e..4ae6349a3c5 100644
--- a/packages/ra-ui-materialui/src/form/index.tsx
+++ b/packages/ra-ui-materialui/src/form/index.tsx
@@ -2,6 +2,7 @@ export * from './TabbedForm';
 export * from './FormTab';
 export * from './FormTabHeader';
 export * from './SimpleForm';
+export * from './SimpleFormConfigurable';
 export * from './TabbedForm';
 export * from './TabbedFormTabs';
 export * from './TabbedFormView';
diff --git a/packages/ra-ui-materialui/src/list/SimpleList/SimpleListConfigurable.tsx b/packages/ra-ui-materialui/src/list/SimpleList/SimpleListConfigurable.tsx
index 3f4c81c8018..af12fa48329 100644
--- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleListConfigurable.tsx
+++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleListConfigurable.tsx
@@ -14,11 +14,7 @@ export const SimpleListConfigurable = ({
         <Configurable
             editor={<SimpleListEditor />}
             preferenceKey={preferenceKey || `${resource}.SimpleList`}
-            sx={{
-                display: 'block',
-                '& .MuiBadge-root': { display: 'flex' },
-                '& ul': { flex: 1 },
-            }}
+            sx={{ display: 'block' }}
         >
             <SimpleListWithPreferences {...props} />
         </Configurable>
diff --git a/packages/ra-ui-materialui/src/list/SimpleList/SimpleListEditor.tsx b/packages/ra-ui-materialui/src/list/SimpleList/SimpleListEditor.tsx
index 52f2a18b737..2804571d1a9 100644
--- a/packages/ra-ui-materialui/src/list/SimpleList/SimpleListEditor.tsx
+++ b/packages/ra-ui-materialui/src/list/SimpleList/SimpleListEditor.tsx
@@ -21,7 +21,7 @@ export const SimpleListEditor = (props: SimpleListEditorProps) => {
         defaultTertiatyText = '',
     } = props;
 
-    useSetInspectorTitle('ra.inspector.simple_list', { _: 'List' });
+    useSetInspectorTitle('ra.inspector.SimpleList.title', { _: 'List' });
     const translate = useTranslate();
 
     const primaryTextField = usePreferenceInput(
diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.spec.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.spec.tsx
index b9b41281e3d..2f2e534a00a 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.spec.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.spec.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { render, screen } from '@testing-library/react';
+import { render, screen, fireEvent } from '@testing-library/react';
 import expect from 'expect';
 
 import { Basic, Omit, PreferenceKey } from './DatagridConfigurable.stories';
@@ -9,7 +9,8 @@ describe('<DatagridConfigurable>', () => {
         render(<Basic />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        await screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Leo Tolstoy'));
+        await screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Datagrid');
         expect(screen.queryByText('1869')).not.toBeNull();
         screen.getByLabelText('Year').click();
@@ -21,7 +22,8 @@ describe('<DatagridConfigurable>', () => {
         render(<Basic />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        await screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Leo Tolstoy'));
+        await screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Datagrid');
         expect(screen.queryByText('War and Peace')).not.toBeNull();
         screen.getByLabelText('Original title').click();
@@ -33,7 +35,8 @@ describe('<DatagridConfigurable>', () => {
         render(<Basic />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        await screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Leo Tolstoy'));
+        await screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Datagrid');
         expect(screen.queryByText('Leo Tolstoy')).not.toBeNull();
         screen.getByLabelText('Author').click();
@@ -48,7 +51,8 @@ describe('<DatagridConfigurable>', () => {
             expect(screen.queryByText('War and Peace')).toBeNull();
             screen.getByLabelText('Configure mode').click();
             await screen.findByText('Inspector');
-            await screen.getAllByTitle('ra.configurable.customize')[0].click();
+            fireEvent.mouseOver(screen.getByText('Leo Tolstoy'));
+            await screen.getByTitle('ra.configurable.customize').click();
             await screen.findByText('Datagrid');
             screen.getByLabelText('Original title').click();
             expect(screen.queryByText('War and Peace')).not.toBeNull();
@@ -60,7 +64,8 @@ describe('<DatagridConfigurable>', () => {
             expect(screen.queryAllByText('War and Peace')).toHaveLength(2);
             screen.getByLabelText('Configure mode').click();
             await screen.findByText('Inspector');
-            await screen.getAllByTitle('ra.configurable.customize')[0].click();
+            fireEvent.mouseOver(screen.getAllByText('Leo Tolstoy')[0]);
+            await screen.getByTitle('ra.configurable.customize').click();
             await screen.findByText('Datagrid');
             screen.getByLabelText('Original title').click();
             expect(screen.queryAllByText('War and Peace')).toHaveLength(1);
diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.tsx
index df6e4401be2..36acb54ed5c 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.tsx
@@ -41,10 +41,10 @@ export const DatagridConfigurable = ({
             'DatagridConfigurable does not support the optimized prop'
         );
     }
-    const resource = useResourceContext(props);
-    const finalPreferenceKey = preferenceKey || `${resource}.datagrid`;
 
     const translate = useTranslate();
+    const resource = useResourceContext(props);
+    const finalPreferenceKey = preferenceKey || `${resource}.datagrid`;
 
     const [availableColumns, setAvailableColumns] = useStore<
         ConfigurableDatagridColumn[]
@@ -86,13 +86,7 @@ export const DatagridConfigurable = ({
         <Configurable
             editor={<DatagridEditor />}
             preferenceKey={finalPreferenceKey}
-            sx={{
-                display: 'block',
-                '& .MuiBadge-root': { display: 'flex' },
-                '& .RaDatagrid-root': { flex: 1 },
-                '& .MuiBadge-badge': { zIndex: 2 },
-                minHeight: 2,
-            }}
+            sx={{ display: 'block', minHeight: 2 }}
         >
             <DatagridWithPreferences {...props} />
         </Configurable>
diff --git a/packages/ra-ui-materialui/src/list/datagrid/DatagridEditor.tsx b/packages/ra-ui-materialui/src/list/datagrid/DatagridEditor.tsx
index 7c35df32294..68b0ce05ff8 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/DatagridEditor.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/DatagridEditor.tsx
@@ -1,74 +1,10 @@
 import * as React from 'react';
-import { usePreference, useSetInspectorTitle, useTranslate } from 'ra-core';
-import { Box, Button } from '@mui/material';
+import { useSetInspectorTitle } from 'ra-core';
 
-import { ConfigurableDatagridColumn } from './DatagridConfigurable';
-import { FieldEditor } from './FieldEditor';
+import { FieldsSelector } from '../../preferences';
 
 export const DatagridEditor = () => {
-    const translate = useTranslate();
-    useSetInspectorTitle('ra.inspector.datagrid', { _: 'Datagrid' });
+    useSetInspectorTitle('ra.inspector.Datagrid.title', { _: 'Datagrid' });
 
-    const [availableColumns] = usePreference<ConfigurableDatagridColumn[]>(
-        'availableColumns',
-        []
-    );
-    const [omit] = usePreference('omit', []);
-
-    const [columns, setColumns] = usePreference(
-        'columns',
-        availableColumns
-            .filter(column => !omit?.includes(column.source))
-            .map(column => column.index)
-    );
-
-    const handleToggle = event => {
-        if (event.target.checked) {
-            // add the column at the right position
-            setColumns(
-                availableColumns
-                    .filter(
-                        column =>
-                            column.index === event.target.name ||
-                            columns.includes(column.index)
-                    )
-                    .map(column => column.index)
-            );
-        } else {
-            setColumns(columns.filter(index => index !== event.target.name));
-        }
-    };
-
-    const handleHideAll = () => {
-        setColumns([]);
-    };
-    const handleShowAll = () => {
-        setColumns(availableColumns.map(column => column.index));
-    };
-    return (
-        <div>
-            {availableColumns.map(column => (
-                <FieldEditor
-                    key={column.index}
-                    source={column.source}
-                    label={column.label}
-                    index={column.index}
-                    selected={columns.includes(column.index)}
-                    onToggle={handleToggle}
-                />
-            ))}
-            <Box display="flex" justifyContent="space-between" mx={-0.5} mt={1}>
-                <Button size="small" onClick={handleHideAll}>
-                    {translate('ra.inspector.datagrid.hideAll', {
-                        _: 'Hide All',
-                    })}
-                </Button>
-                <Button size="small" onClick={handleShowAll}>
-                    {translate('ra.inspector.datagrid.showAll', {
-                        _: 'Show All',
-                    })}
-                </Button>
-            </Box>
-        </div>
-    );
+    return <FieldsSelector name="columns" availableName="availableColumns" />;
 };
diff --git a/packages/ra-ui-materialui/src/list/datagrid/SelectColumnsButton.tsx b/packages/ra-ui-materialui/src/list/datagrid/SelectColumnsButton.tsx
index a578159f6dd..eeabac918b3 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/SelectColumnsButton.tsx
+++ b/packages/ra-ui-materialui/src/list/datagrid/SelectColumnsButton.tsx
@@ -11,7 +11,7 @@ import {
 } from '@mui/material';
 import ViewWeekIcon from '@mui/icons-material/ViewWeek';
 
-import { FieldEditor } from './FieldEditor';
+import { FieldToggle } from '../../preferences';
 import { ConfigurableDatagridColumn } from './DatagridConfigurable';
 import { styled } from '@mui/material/styles';
 
@@ -123,7 +123,7 @@ export const SelectColumnsButton = props => {
             >
                 <Box p={1}>
                     {availableColumns.map(column => (
-                        <FieldEditor
+                        <FieldToggle
                             key={column.index}
                             source={column.source}
                             label={column.label}
diff --git a/packages/ra-ui-materialui/src/preferences/Configurable.spec.tsx b/packages/ra-ui-materialui/src/preferences/Configurable.spec.tsx
index 1e08e06b3bf..fbf5c71d984 100644
--- a/packages/ra-ui-materialui/src/preferences/Configurable.spec.tsx
+++ b/packages/ra-ui-materialui/src/preferences/Configurable.spec.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { screen, render, waitFor } from '@testing-library/react';
+import { screen, render, waitFor, fireEvent } from '@testing-library/react';
 import expect from 'expect';
 
 import { Basic, Unmount } from './Configurable.stories';
@@ -9,7 +9,8 @@ describe('Configurable', () => {
         render(<Basic />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Lorem ipsum'));
+        screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Text block');
     });
 
@@ -17,7 +18,8 @@ describe('Configurable', () => {
         render(<Basic />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        await screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Lorem ipsum'));
+        screen.getByTitle('ra.configurable.customize').click();
         expect(
             (screen.getByLabelText('Background color') as HTMLInputElement)
                 .value
@@ -28,7 +30,8 @@ describe('Configurable', () => {
         render(<Basic />);
         screen.getByText('Today');
         screen.getByLabelText('Configure mode').click();
-        screen.getAllByTitle('ra.configurable.customize')[1].click();
+        fireEvent.mouseOver(screen.getByText('Sales'));
+        screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Sales block');
         screen.getByLabelText('Show date').click();
         expect(screen.queryByText('Today')).toBeNull();
@@ -38,7 +41,8 @@ describe('Configurable', () => {
         render(<Basic />);
         screen.getByText('Today');
         screen.getByLabelText('Configure mode').click();
-        screen.getAllByTitle('ra.configurable.customize')[1].click();
+        fireEvent.mouseOver(screen.getByText('Sales'));
+        screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Sales block');
         screen.getByLabelText('Show date').click();
         screen.getByLabelText('ra.action.close').click();
@@ -49,7 +53,8 @@ describe('Configurable', () => {
         render(<Unmount />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Lorem ipsum'));
+        screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Text block');
         screen.getByText('toggle text block').click();
         await waitFor(() => {
@@ -62,7 +67,8 @@ describe('Configurable', () => {
         render(<Unmount />);
         screen.getByLabelText('Configure mode').click();
         await screen.findByText('Inspector');
-        screen.getAllByTitle('ra.configurable.customize')[0].click();
+        fireEvent.mouseOver(screen.getByText('Lorem ipsum'));
+        screen.getByTitle('ra.configurable.customize').click();
         await screen.findByText('Text block');
         screen.getByText('toggle sales block').click();
         await waitFor(() => {
diff --git a/packages/ra-ui-materialui/src/preferences/Configurable.tsx b/packages/ra-ui-materialui/src/preferences/Configurable.tsx
index aae736ec00e..488792831a0 100644
--- a/packages/ra-ui-materialui/src/preferences/Configurable.tsx
+++ b/packages/ra-ui-materialui/src/preferences/Configurable.tsx
@@ -1,11 +1,11 @@
 import * as React from 'react';
-import { useRef, useEffect, cloneElement, ReactElement } from 'react';
+import { useRef, useEffect, useState, cloneElement, ReactElement } from 'react';
 import {
     usePreferencesEditor,
     PreferenceKeyContextProvider,
     useTranslate,
 } from 'ra-core';
-import { alpha, Badge } from '@mui/material';
+import { alpha, Popover } from '@mui/material';
 import { styled, SxProps } from '@mui/material/styles';
 import SettingsIcon from '@mui/icons-material/Settings';
 import clsx from 'clsx';
@@ -50,6 +50,10 @@ export const Configurable = (props: ConfigurableProps) => {
 
     const isEditorOpen = prefixedPreferenceKey === currentPreferenceKey;
     const editorOpenRef = useRef(isEditorOpen);
+    const wrapperRef = useRef(null);
+    const [isCustomizeButtonVisible, setIsCustomizeButtonVisible] = useState(
+        false
+    );
 
     useEffect(() => {
         editorOpenRef.current = isEditorOpen;
@@ -84,6 +88,14 @@ export const Configurable = (props: ConfigurableProps) => {
         setPreferenceKey(prefixedPreferenceKey);
     };
 
+    const handleShowButton = event => {
+        setIsCustomizeButtonVisible(true);
+    };
+
+    const handleHideButton = () => {
+        setIsCustomizeButtonVisible(false);
+    };
+
     return (
         <PreferenceKeyContextProvider value={prefixedPreferenceKey}>
             <Root
@@ -92,26 +104,55 @@ export const Configurable = (props: ConfigurableProps) => {
                     isEditorOpen && ConfigurableClasses.editorActive
                 )}
                 sx={sx}
+                ref={wrapperRef}
+                onMouseEnter={isEnabled ? handleShowButton : undefined}
+                onMouseLeave={isEnabled ? handleHideButton : undefined}
             >
-                <Badge
-                    badgeContent={
-                        <SettingsIcon
-                            // @ts-ignore
-                            fontSize="12px"
-                        />
-                    }
-                    componentsProps={{
-                        badge: {
-                            title: translate(openButtonLabel),
-                            onClick: handleOpenEditor,
-                        },
-                    }}
-                    color="warning"
-                    invisible={!isEnabled}
-                >
-                    {children}
-                </Badge>
+                {children}
             </Root>
+            <Popover
+                open={isEnabled && (isCustomizeButtonVisible || isEditorOpen)}
+                sx={{
+                    pointerEvents: 'none',
+                    '& .MuiPaper-root': {
+                        pointerEvents: 'auto',
+                        borderRadius: 10,
+                        padding: '2px',
+                        lineHeight: 0,
+                        backgroundColor: 'warning.light',
+                        color: 'warning.contrastText',
+                        '&:hover': {
+                            cursor: 'pointer',
+                        },
+                    },
+                }}
+                anchorEl={wrapperRef.current}
+                anchorOrigin={{
+                    vertical: 'top',
+                    horizontal: 'right',
+                }}
+                transformOrigin={{
+                    vertical: 'center',
+                    horizontal: 'center',
+                }}
+                onClose={handleHideButton}
+                PaperProps={{
+                    elevation: 1,
+                    onMouseEnter: handleShowButton,
+                    onMouseLeave: handleHideButton,
+                    title: translate(openButtonLabel),
+                    onClick: handleOpenEditor,
+                }}
+                disableAutoFocus
+                disableRestoreFocus
+                disableScrollLock
+                marginThreshold={8}
+            >
+                <SettingsIcon
+                    // @ts-ignore
+                    fontSize="12px"
+                />
+            </Popover>
         </PreferenceKeyContextProvider>
     );
 };
@@ -128,7 +169,6 @@ const PREFIX = 'RaConfigurable';
 
 export const ConfigurableClasses = {
     editMode: `${PREFIX}-editMode`,
-    button: `${PREFIX}-button`,
     editorActive: `${PREFIX}-editorActive`,
 };
 
@@ -136,25 +176,16 @@ const Root = styled('span', {
     name: PREFIX,
     overridesResolver: (props, styles) => styles.root,
 })(({ theme }) => ({
-    [`& .MuiBadge-badge`]: {
-        visibility: 'hidden',
-        pointerEvents: 'none',
-        padding: 0,
-    },
-    [`&.${ConfigurableClasses.editMode}:hover > .MuiBadge-root > .MuiBadge-badge`]: {
-        visibility: 'visible',
-        pointerEvents: 'initial',
-        cursor: 'pointer',
-    },
-    [`&.${ConfigurableClasses.editMode} > .MuiBadge-root > :not(.MuiBadge-badge)`]: {
+    position: 'relative',
+    display: 'inline-block',
+    [`&.${ConfigurableClasses.editMode}`]: {
         transition: theme.transitions.create('outline'),
         outline: `${alpha(theme.palette.warning.main, 0.3)} solid 2px`,
     },
-    [`&.${ConfigurableClasses.editMode}:hover > .MuiBadge-root > :not(.MuiBadge-badge)`]: {
+    [`&.${ConfigurableClasses.editMode}:hover `]: {
         outline: `${alpha(theme.palette.warning.main, 0.5)} solid 2px`,
     },
-
-    [`&.${ConfigurableClasses.editMode}.${ConfigurableClasses.editorActive} > .MuiBadge-root > :not(.MuiBadge-badge), &.${ConfigurableClasses.editMode}.${ConfigurableClasses.editorActive}:hover > .MuiBadge-root > :not(.MuiBadge-badge)`]: {
+    [`&.${ConfigurableClasses.editMode}.${ConfigurableClasses.editorActive} , &.${ConfigurableClasses.editMode}.${ConfigurableClasses.editorActive}:hover `]: {
         outline: `${theme.palette.warning.main} solid 2px`,
     },
 }));
diff --git a/packages/ra-ui-materialui/src/list/datagrid/FieldEditor.tsx b/packages/ra-ui-materialui/src/preferences/FieldToggle.tsx
similarity index 92%
rename from packages/ra-ui-materialui/src/list/datagrid/FieldEditor.tsx
rename to packages/ra-ui-materialui/src/preferences/FieldToggle.tsx
index e82eb4a4379..f0175d44962 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/FieldEditor.tsx
+++ b/packages/ra-ui-materialui/src/preferences/FieldToggle.tsx
@@ -3,9 +3,9 @@ import { FieldTitle, useResourceContext } from 'ra-core';
 import { Switch, Typography } from '@mui/material';
 
 /**
- * UI to edit a field in a DatagridEditor
+ * UI to enable/disable a field
  */
-export const FieldEditor = props => {
+export const FieldToggle = props => {
     const { selected, label, onToggle, source, index } = props;
     const resource = useResourceContext();
     return (
diff --git a/packages/ra-ui-materialui/src/preferences/FieldsSelector.tsx b/packages/ra-ui-materialui/src/preferences/FieldsSelector.tsx
new file mode 100644
index 00000000000..2c450dbef99
--- /dev/null
+++ b/packages/ra-ui-materialui/src/preferences/FieldsSelector.tsx
@@ -0,0 +1,84 @@
+import * as React from 'react';
+import { usePreference, useTranslate } from 'ra-core';
+import { Box, Button } from '@mui/material';
+
+import { FieldToggle } from './FieldToggle';
+
+/**
+ * UI to select / deselect fields, and store the selection in preferences
+ */
+export const FieldsSelector = ({
+    name = 'columns',
+    availableName = 'availableColumns',
+}) => {
+    const translate = useTranslate();
+
+    const [availableFields] = usePreference<SelectableField[]>(
+        availableName,
+        []
+    );
+    const [omit] = usePreference('omit', []);
+
+    const [fields, setFields] = usePreference(
+        name,
+        availableFields
+            .filter(field => !omit?.includes(field.source))
+            .map(field => field.index)
+    );
+
+    const handleToggle = event => {
+        if (event.target.checked) {
+            // add the column at the right position
+            setFields(
+                availableFields
+                    .filter(
+                        field =>
+                            field.index === event.target.name ||
+                            fields.includes(field.index)
+                    )
+                    .map(field => field.index)
+            );
+        } else {
+            setFields(fields.filter(index => index !== event.target.name));
+        }
+    };
+
+    const handleHideAll = () => {
+        setFields([]);
+    };
+    const handleShowAll = () => {
+        setFields(availableFields.map(field => field.index));
+    };
+    return (
+        <div>
+            {availableFields.map(field => (
+                <FieldToggle
+                    key={field.index}
+                    source={field.source}
+                    label={field.label}
+                    index={field.index}
+                    selected={fields.includes(field.index)}
+                    onToggle={handleToggle}
+                />
+            ))}
+            <Box display="flex" justifyContent="space-between" mx={-0.5} mt={1}>
+                <Button size="small" onClick={handleHideAll}>
+                    {translate('ra.inspector.hideAll', {
+                        _: 'Hide All',
+                    })}
+                </Button>
+                <Button size="small" onClick={handleShowAll}>
+                    {translate('ra.inspector.showAll', {
+                        _: 'Show All',
+                    })}
+                </Button>
+            </Box>
+        </div>
+    );
+};
+
+export interface SelectableField {
+    index: string;
+    source: string;
+    label?: string;
+}
diff --git a/packages/ra-ui-materialui/src/preferences/index.ts b/packages/ra-ui-materialui/src/preferences/index.ts
index 5e93b7c3c16..6c8c8475bf3 100644
--- a/packages/ra-ui-materialui/src/preferences/index.ts
+++ b/packages/ra-ui-materialui/src/preferences/index.ts
@@ -1,4 +1,6 @@
 export * from './Configurable';
+export * from './FieldsSelector';
+export * from './FieldToggle';
 export * from './Inspector';
 export * from './InspectorButton';
 export * from './InspectorRoot';