diff --git a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx index 3f74a077e54c..25bf59bb1733 100644 --- a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx +++ b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx @@ -48,8 +48,10 @@ function Loadable(options: LoadableOptions) { ) : null - // If it's non-SSR or provided a loading component, wrap it in a suspense boundary - const hasSuspenseBoundary = !opts.ssr || !!opts.loading + // Only wrap in a Suspense boundary when a loading component is explicitly provided. + // When no loading component is given, use Fragment to allow parent Suspense + // boundaries to handle the loading state properly. + const hasSuspenseBoundary = !!opts.loading const Wrap = hasSuspenseBoundary ? Suspense : Fragment const wrapProps = hasSuspenseBoundary ? { fallback: fallbackElement } : {} const children = opts.ssr ? ( diff --git a/packages/next/src/shared/lib/loadable.shared-runtime.tsx b/packages/next/src/shared/lib/loadable.shared-runtime.tsx index d82563ed97fc..a48484ae26ac 100644 --- a/packages/next/src/shared/lib/loadable.shared-runtime.tsx +++ b/packages/next/src/shared/lib/loadable.shared-runtime.tsx @@ -138,6 +138,9 @@ function createLoadableComponent(loadFn: any, options: any) { return React.useMemo(() => { if (state.loading || state.error) { + if (!opts.loading) { + return null + } return React.createElement(opts.loading, { isLoading: state.loading, pastDelay: state.pastDelay,