🐛 Bug Report
A server-rendered fluent-dropdown with a placeholder renders an empty control before the custom element upgrades. Hydration inserts the control button and placeholder text, causing a visible layout shift.
💻 Repro or Code Sample
- Server-render the dropdown using the package's SSR pipeline.
- Delay the client-side component definition.
- Compare the dropdown before and after hydration.
<fluent-dropdown placeholder="Select a fruit">
<fluent-listbox>
<fluent-option value="apple">Apple</fluent-option>
<fluent-option value="banana">Banana</fluent-option>
</fluent-listbox>
</fluent-dropdown>
<script type="module">
setTimeout(
() => import("@fluentui/web-components/dropdown/define.js"),
2000,
);
</script>
Before the definition loads, the control has no placeholder. Once the component upgrades, the placeholder and default control styling appear and its bounds change.
🤔 Expected Behavior
The server-rendered dropdown should display its placeholder with the same dimensions and styling as the hydrated control. Hydration should not produce a layout shift or visible change.
😯 Current Behavior
The SSR control slot has no fallback content. The internal button is created during component initialization, so the placeholder remains absent while the element is undefined.
Hydration inserts the button and placeholder text and applies the default outline styling. The pre-hydration and post-hydration screenshots and bounding boxes differ, and Chromium records a layout-shift event without recent user input.
💁 Possible Solution
Render an aria-hidden placeholder as fallback content in the control slot, then style it to match the generated control. Pre-definition CSS should also represent the default placeholder color and outline appearance.
I have a local fix and can submit it.
🔦 Context
This affects server-rendered applications that defer or stream client JavaScript. Users see an empty dropdown change size or appearance when hydration completes, which contributes to cumulative layout shift.
🌍 Your Environment
- OS & Device: macOS on Mac
- Browser: Chromium
- Version:
@fluentui/web-components@3.1.2
🐛 Bug Report
A server-rendered
fluent-dropdownwith aplaceholderrenders an empty control before the custom element upgrades. Hydration inserts the control button and placeholder text, causing a visible layout shift.💻 Repro or Code Sample
Before the definition loads, the control has no placeholder. Once the component upgrades, the placeholder and default control styling appear and its bounds change.
🤔 Expected Behavior
The server-rendered dropdown should display its placeholder with the same dimensions and styling as the hydrated control. Hydration should not produce a layout shift or visible change.
😯 Current Behavior
The SSR control slot has no fallback content. The internal button is created during component initialization, so the placeholder remains absent while the element is undefined.
Hydration inserts the button and placeholder text and applies the default outline styling. The pre-hydration and post-hydration screenshots and bounding boxes differ, and Chromium records a layout-shift event without recent user input.
💁 Possible Solution
Render an
aria-hiddenplaceholder as fallback content in the control slot, then style it to match the generated control. Pre-definition CSS should also represent the default placeholder color and outline appearance.I have a local fix and can submit it.
🔦 Context
This affects server-rendered applications that defer or stream client JavaScript. Users see an empty dropdown change size or appearance when hydration completes, which contributes to cumulative layout shift.
🌍 Your Environment
@fluentui/web-components@3.1.2