diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js
index a71eb543cfe5d..28a8c733d105a 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js
@@ -3278,6 +3278,9 @@ function commitRootImpl(
// We don't schedule a separate task for flushing passive effects.
// Instead, we just rely on ensureRootIsScheduled below to schedule
// a callback for us to flush the passive effects.
+ // We do need to clear this callback.
+ // Otherwise, we'll still think the commit is suspended.
+ root.cancelPendingCommit = null;
} else {
// So we can clear these now to allow a new callback to be scheduled.
root.callbackNode = null;
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js
index 4dbba1bca21f0..d946a473d67a0 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js
@@ -491,4 +491,44 @@ describe('ReactSuspenseyCommitPhase', () => {
>,
);
});
+
+ it('runs passive effects after suspended commit resolves', async () => {
+ function Effect() {
+ React.useEffect(() => {
+ Scheduler.log('flush effect');
+ });
+ return ;
+ }
+
+ const root = ReactNoop.createRoot();
+
+ await act(() => {
+ root.render(
+ }>
+
+
+ ,
+ );
+ });
+
+ assertLog([
+ 'render effect',
+ 'Image requested [A]',
+ 'Loading...',
+ 'render effect',
+ ]);
+ expect(root).toMatchRenderedOutput('Loading...');
+
+ await act(() => {
+ resolveSuspenseyThing('A');
+ });
+
+ assertLog(['flush effect']);
+ expect(root).toMatchRenderedOutput(
+ <>
+ {'render effect'}
+
+ >,
+ );
+ });
});