Skip to content

Commit 68b0289

Browse files
JonasBacodex
andcommitted
fix(slot): Render nothing when no outlet is registered
Stop rendering slot consumer content in place when no outlet element is available. This avoids the initial flash at the wrong DOM position and prevents remounts caused by switching from inline rendering to a portal once the outlet appears. Co-Authored-By: OpenAI Codex <noreply@openai.com>
1 parent c56f2d2 commit 68b0289

File tree

2 files changed

+9
-9
lines changed

2 files changed

+9
-9
lines changed

static/app/components/core/slot/slot.spec.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('slot', () => {
1111
expect(SlotModule.Fallback).toBeDefined();
1212
});
1313

14-
it('renders children in place when no Outlet is registered', () => {
14+
it('renders nothing when no Outlet is registered', () => {
1515
const SlotModule = slot(['header'] as const);
1616

1717
render(
@@ -22,7 +22,7 @@ describe('slot', () => {
2222
</SlotModule.Provider>
2323
);
2424

25-
expect(screen.getByText('inline content')).toBeInTheDocument();
25+
expect(screen.queryByText('inline content')).not.toBeInTheDocument();
2626
});
2727

2828
it('portals children to the Outlet element', () => {
@@ -44,7 +44,7 @@ describe('slot', () => {
4444
);
4545
});
4646

47-
it('multiple slot consumers render their children independently', () => {
47+
it('multiple slot consumers render nothing independently when no Outlet is registered', () => {
4848
const SlotModule = slot(['a', 'b'] as const);
4949

5050
render(
@@ -58,8 +58,8 @@ describe('slot', () => {
5858
</SlotModule.Provider>
5959
);
6060

61-
expect(screen.getByText('slot a content')).toBeInTheDocument();
62-
expect(screen.getByText('slot b content')).toBeInTheDocument();
61+
expect(screen.queryByText('slot a content')).not.toBeInTheDocument();
62+
expect(screen.queryByText('slot b content')).not.toBeInTheDocument();
6363
});
6464

6565
it('consumer throws when rendered outside provider', () => {

static/app/components/core/slot/slot.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,6 @@ function makeSlotConsumer<T extends Slot>(
140140
return () => dispatch({type: 'decrement counter', name});
141141
}, [dispatch, name]);
142142

143-
const element = state[name]?.element;
144-
145143
// Provide outletNameContext from the consumer so that portaled children
146144
// (which don't descend through the outlet in the component tree) can still
147145
// read which slot they belong to via useSlotOutletRef.
@@ -151,10 +149,12 @@ function makeSlotConsumer<T extends Slot>(
151149
</outletNameContext.Provider>
152150
);
153151

152+
const element = state[name]?.element;
153+
154154
if (!element) {
155-
// Render in place as a fallback when no target element is registered yet
156-
return wrappedChildren;
155+
return null;
157156
}
157+
158158
return createPortal(wrappedChildren, element);
159159
}
160160

0 commit comments

Comments
 (0)