@@ -146,13 +146,9 @@ export const ExperimentalBlockEditorProvider = withRegistryProvider(
146146 const isClientSideMediaEnabled =
147147 shouldEnableClientSideMediaProcessing ( ) ;
148148
149- // Preview providers must not set up their own MediaUploadProvider or
150- // replace the mediaUpload setting. Every MediaUploadProvider with
151- // useSubRegistry={false} writes to the same shared upload-media store,
152- // so a nested preview provider would overwrite the parent's original
153- // server-side mediaUpload function with the already-replaced
154- // client-side interceptor, causing uploads in the main editor to
155- // dispatch back into the upload-media store in an infinite loop.
149+ // Skip the interceptor replacement and MediaUploadProvider for
150+ // preview providers. See the comment above MediaUploadProvider
151+ // below for details.
156152 const isPreviewMode = ! ! _settings ?. isPreviewMode ;
157153
158154 const settings = useMemo ( ( ) => {
@@ -228,6 +224,20 @@ export const ExperimentalBlockEditorProvider = withRegistryProvider(
228224 </ SelectionContext . Provider >
229225 ) ;
230226
227+ // MediaUploadProvider writes the mediaUpload function from
228+ // _settings into the shared upload-media store so the store can
229+ // hand files off to the server. useMediaUploadSettings extracts
230+ // mediaUpload from the original _settings prop — *before* the
231+ // interceptor replacement above — so the store receives the
232+ // real server-side upload function.
233+ //
234+ // Only the top-level (non-preview) provider should do this.
235+ // Nested preview providers (e.g. from useBlockPreview in
236+ // core/post-template) inherit settings that already contain
237+ // the interceptor, so their MediaUploadProvider would
238+ // overwrite the store's server-side function with the
239+ // interceptor, causing uploads to loop instead of reaching
240+ // the server.
231241 if ( isClientSideMediaEnabled && ! isPreviewMode ) {
232242 return (
233243 < MediaUploadProvider
0 commit comments