{"record":{"id":"c6b4b79ce6bb02f5","repo":"marmelab/react-admin","slug":"configurable-must-be-used-inside-a-preferencesedit","errorCode":null,"errorMessage":"Configurable must be used inside a PreferencesEditorContextProvider","messagePattern":"Configurable must be used inside a PreferencesEditorContextProvider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ra-ui-materialui/src/preferences/Configurable.tsx","lineNumber":92,"sourceCode":"        editorOpenRef.current = isEditorOpen;\n    }, [isEditorOpen]);\n\n    // on unmount, if selected, remove the editor\n    useEffect(() => {\n        return () => {\n            if (!editorOpenRef.current) return;\n            setPreferenceKey && setPreferenceKey(null);\n            setEditor && setEditor(null);\n        };\n    }, [setEditor, setPreferenceKey]);\n\n    if (!hasPreferencesEditorContext) {\n        return children;\n    }\n\n    const handleOpenEditor = () => {\n        if (!setEditor) {\n            throw new Error(\n                'Configurable must be used inside a PreferencesEditorContextProvider'\n            );\n        }\n\n        // include the editorKey as key to force destroy and mount\n        // when switching between two identical editors with different editor keys\n        // otherwise the editor will see an update and its useStore will return one tick later\n        // which would forbid the usage of uncontrolled inputs in the editor\n        setEditor(\n            cloneElement(editor, {\n                preferenceKey: prefixedPreferenceKey,\n                key: prefixedPreferenceKey,\n            })\n        );\n        if (!setPreferenceKey) {\n            throw new Error(\n                'Configurable must be used inside a PreferencesEditorContextProvider'\n            );","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/marmelab/react-admin/blob/051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0/packages/ra-ui-materialui/src/preferences/Configurable.tsx#L74-L110","documentation":"Configurable opens the preferences editor via setEditor, which is only defined when a PreferencesEditorContextProvider (created by <Admin> with a UI enabling configuration, e.g. using <UIConfigurationEditor>/dark-layout config) wraps the tree. If the context exists but setEditor is missing, react-admin throws on click.","triggerScenarios":"Clicking a Configurable component's edit button when the component is rendered in an app (or route) not wrapped by the PreferencesEditorContextProvider.","commonSituations":"Using Configurable-wrapped components outside <Admin>, in tests, storybook, or custom entry points without the preferences editor setup.","solutions":["Render the app inside <Admin> so the PreferencesEditorContextProvider is present.","Remove or conditionally disable Configurable wrappers outside the admin shell.","Wrap the subtree in PreferencesEditorContextProvider manually when embedding outside Admin."],"exampleFix":"// before\nrender(<Configurable editor={<MyEditor />}>...</Configurable>);\n// after\nrender(<Admin dataProvider={dp}><Configurable editor={<MyEditor />}>...</Configurable></Admin>);","handlingStrategy":"validation","validationCode":"const { setEditor } = usePreferencesEditorContext();\nif (!setEditor) return children; // render non-configurable fallback","typeGuard":"const hasPrefsEditor = (c: { setEditor?: unknown }): c is { setEditor: Function } => typeof c.setEditor === 'function';","tryCatchPattern":"try { openEditor(); } catch (e) { if (String(e).includes('PreferencesEditorContextProvider')) return; /* no-op outside admin */ throw e; }","preventionTips":["Use Configurable only inside <Admin>.","In tests/storybook, wrap with the real provider.","Guard the edit UI behind presence of setEditor."],"tags":["react-admin","missing-context","preferences-editor"],"backgroundTag":"missing-context-provider","analyzedSha":"051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0","analyzedAt":"2026-08-30T02:28:14.926Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}