Skip to content

Conversation

@tsullivan
Copy link
Member

@tsullivan tsullivan commented Nov 10, 2025

New Managed Flyout System

Re-open of #9068

This represents a significant evolution in the flyout system: a hook-based manager system with automatic context detection and support for complex flyout workflows.


Summary of API changes

Change Component API Brief description and documentation
New optional prop EuiFlyout session? ('start' | 'never' | 'inherit', optional - defaults to inherit) Enable flyout session management. Read more
Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#EuiFlyout-prop-session
New optional prop EuiFlyout onActive? (() => void, optional) Callback fired when the flyout becomes active/visible.
Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#EuiFlyout-prop-onActive
New optional prop EuiFlyout resizable? (boolean, optional - defaults to false) A new way to create resizable flyouts available directly in EuiFlyout - no EuiFlyoutResizable necessary. The resizable prop is dynamic which allows toggling between resizable state without whole-component rerenders
This makes the old EuiFlyoutResizable component obsolete (but it still works exactly like before). We may officially deprecate it in near future, but that's not the goal of this project. Read more
Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#resizable-flyout
New optional prop EuiFlyout onResize? Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#EuiFlyout-prop-onResize
New optional prop EuiFlyout minWidth? (number, optional) Brings feature parity with EuiFlyoutResizable. Use with resizabe={true} to set a minimum width of the flyout.
Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#resizable-flyout
New optional prop EuiFlyout flyoutMenuProps? (EuiFlyoutMenuProps, optional) Allows configuring certain aspects of the flyout menu like the title or custom action buttons. Read more
Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#flyout-menu
New optional prop EuiFlyout hasChildBackground? Documentation: https://eui.elastic.co/pr_9202/docs/components/containers/flyout/#EuiFlyout-prop-hasChildBackground
ref prop update EuiFlyout ref? (React.Ref, optional) Breaking change
Legacy string refs are no longer supported with EuiFlyout. Use modern React refs like useRef instead. Read more
Removed old flyout session system EuiFlyoutSessionProvider
useEuiFlyoutSession
EuiFlyoutSessionApi
EuiFlyoutSessionConfig
EuiFlyoutSessionRenderContext
- The initial, experimental flyout system implementation has been removed. Read more
Removed EuiFlyoutChild

ADDED - New Public-Facing Features

1. New Manager System

A comprehensive new "manager" system for centralized management of flyout sessions (src/components/flyout/manager/):

Core Component:

  • EuiFlyout: Same flyout component that developers are accustomed to

Session Management Props:

  • session prop on EuiFlyout with three modes:
    • 'start' - Creates a new flyout session (for main flyouts)
    • 'inherit' - (default) Inherits existing session if active
    • 'never' - Opts out of session management

Required EuiFlyoutMenu:
Managed flyouts require an EuiFlyoutMenu at the top, which is automatically rendered when you provide the necessary props.

  • flyoutMenuProps.title - (Required, or provide aria-label) Prop that provides the title for the automatically rendered EuiFlyoutMenu. Additional properties in flyoutMenuProps allow further customization of the menu (back button, history items, custom actions, etc.).

Synchronizing local state with the visible flyout session:
Since flyout sessions can be programmatically activated, i.e the history navigation system can reactivate them, you may want to have a local state that is kept in sync with the active session. These callbacks can be used to update your local state variables:

  • onActive callback - Fires when flyout becomes active/visible (new prop)
  • onClose callback - Fires when flyout is closed (not a new prop)

The new manager system uses use-sync-external-store and a singleton store pattern for cross-React-root state sharing.

2. New Flyout Menu Component

  • EuiFlyoutMenu and EuiFlyoutMenuProps: A new component for rendering a top menu bar in EuiFlyout.
  • The component uses internal context to support a "Back" button and a "History popover" control, when used within a managed flyout context.

3. resizable prop.

EuiFlyoutResizable was a complex wrapper that managed all resizing and state logic internally. The logic has been moved to an internal hook, which is now integrated directly into the main flyout component.

  • EuiFlyout now supports a resizable boolean prop
  • EuiFlyoutResizable is still supported, but is just a wrapper for EuiFlyout with resizable set to true.

REMOVED - Earlier iteration of the Flyout System Feature

An earlier pre-release iteration of the Flyout System was in main but is now being removed.

1. Old Session System

The entire API of the earlier version is being removed:

  • EuiFlyoutSessionProvider
  • useEuiFlyoutSession()
  • EuiFlyoutSessionApi
  • EuiFlyoutSessionConfig
  • EuiFlyoutSessionOpenChildOptions
  • EuiFlyoutSessionOpenMainOptions
  • EuiFlyoutSessionOpenGroupOptions
  • EuiFlyoutSessionProviderComponentProps
  • EuiFlyoutSessionRenderContext

2. Old Child Flyout Implementation

  • Standalone EuiFlyoutChild component (replaced with managed version)

The key difference from the old session system is that it used render props; the new manager system uses a centralized state store with hooks. The old system required explicit provider wrapping; the new system automatically detects when to use managed behavior.

Number of component usages in Kibana that will need to be updated: 0


Screenshots #

flyout system demo

Impact to users #

BREAKING CHANGE - String Refs No Longer Supported

EuiFlyout is a forwardRef component that previously had loose typing allowing string refs. It now enforces strict typing that only accepts modern ref patterns. Specifically, this could impact you in two scenarios:

  1. If you were passing a string ref to EuiFlyout, you will need to pass an HTML element ref instead:
    const ref = useRef<HTMLDivElement>(null);
    <EuiFlyout ref={ref}>
        <div>Hello</div>
    </EuiFlyout>
  2. If you were using styled-components to wrap EuiFlyout, your component implicitly becomes a string ref component. You will need to add ref={null} to your component:
    const StyledEuiFlyout = styled(EuiFlyout)`
    padding: 16px;
    `;
    <StyledEuiFlyout ref={null}>
        <div>Hello</div>
    </StyledEuiFlyout>

Number of component usages in Kibana that will need to be updated: 1

  1. fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx

Flyout overlay mask z-index value update

Overlay masks are now siblings of flyouts, which required a change to the mask's z-index value. Now, flyout overlay masks use z-index value of levels.flyout - 2 making it more predictable. This stacking order allows child flyouts to have their own z-index layer between the main flyout and the mask.

QA

Remove or strikethrough items that do not apply to your PR.

PR Mergeability / Release readiness checklist

We must ensure this feature has everything it needs to be a publicly available feature in EUI before this branch is merged into main. Please ensure the following are completed before merging this PR:

  • List explicitly what has been added, removed, or changed in the public facing API.
  • For each of the above, please ensure documentation has been updated accordingly.
  • Please provide production(https://eui.elastic.co/) links to all relevant documentation. (which covers, at a minimum, the API changes listed above).
  • The Impact section must be filled out and call out explicitly how users upgrading to the EUI version containing this feature will be impacted.
    • If it is all new code and features, there will be no impact.
    • If anything existing in EUI has changed either visually or functionally, it must highlighted.
  • Figma has been updated to reflect the changes in this PR (or is planned to be updated by the time this feature is published) - please provide links.
  • @JasonStoltz has reviewed the above and approved this PR

General checklist

  • Browser QA
    • Checked in both light and dark modes
    • Checked in both MacOS and Windows high contrast modes
    • Checked in mobile
    • Checked in Chrome, Safari, Edge, and Firefox
    • Checked for accessibility including keyboard-only and screenreader modes
  • Docs site QA
  • Code quality checklist
  • Release checklist
    • A changelog entry exists and is marked appropriately.
    • If applicable, added the breaking change issue label (and filled out the breaking change checklist)
  • Designer checklist
    • If applicable, file an issue to update EUI's Figma library with any corresponding UI changes. (This is an internal repo, if you are external to Elastic, ask a maintainer to submit this request)

@tsullivan tsullivan changed the title Restore Flyout System feature in EUI (#9178) Restore Flyout System feature in EUI Nov 10, 2025
@tsullivan tsullivan added the breaking change PRs with breaking changes. (Don't delete - used for automation) label Nov 10, 2025
@github-actions
Copy link

github-actions bot commented Nov 10, 2025

This PR contains breaking changes. The opener of this pull request is asked to perform the following due diligence steps below, to assist EUI in our next Kibana upgrade:

  • If this PR contains prop/API changes:
    • Search through Kibana for <EuiComponent usages (example search)
    • In the PR description or in a PR comment, include a count or list with the number of component usages in Kibana that will need to be updated (if that amount is "none", include that information as well)
  • If this PR contains CSS changes:
    • Search through Kibana for the changed EUI selectors, e.g. .euiComponent (example search)
    • In the PR description or in a PR comment, include a count or list with the number of custom CSS overrides in Kibana that will need to be updated (if that amount is "none", include that information as well)
  • 🔍 Tip: When searching through Kibana, consider excluding **/target, **/*.snap, **/*.storyshot files to reduce noise and only look at source code usages
  • ⚠️ For extremely risky changes, the EUI team should potentially consider the following precautions:
    • Using a pre-release release candidate to test Kibana CI ahead of time
    • Using kibana-a-la-carte for manual QA, and to give other Kibana teams a staging server to quickly test against

tsullivan and others added 2 commits November 13, 2025 17:35
Co-authored-by: Clint Andrew Hall <clint.hall@elastic.co>
Co-authored-by: Weronika Olejniczak <32842468+weronikaolejniczak@users.noreply.github.com>
Co-authored-by: Tomasz Kajtoch <tomasz.kajtoch@elastic.co>
Co-authored-by: Arturo Castillo Delgado <arturo.castillo@elastic.co>
Co-authored-by: Paulina Shakirova <paulina.shakirova@elastic.co>
@tsullivan tsullivan marked this pull request as ready for review November 26, 2025 15:09
@tsullivan tsullivan requested a review from a team as a code owner November 26, 2025 15:09
@weronikaolejniczak
Copy link
Contributor

When testing in Storybook the multi-session story I noticed this issue:

Kapture.2025-11-28.at.13.34.00.mp4

It has to do with our portal and a similar issue has been reported here: #9242.

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

cc @tsullivan @tkajtoch

@tkajtoch
Copy link
Member

tkajtoch commented Dec 3, 2025

When testing in Storybook the multi-session story I noticed this issue:

This issue seems to be related to the same EuiPortal issue and isn't caused by any of the changes introduced here. I dug into it a little and have some ideas on why this might be happening, but nothing concrete yet. Let's fix it separately since changing EuiPortal would need lots of testing in EUI and Kibana.

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

cc @tsullivan @tkajtoch

Copy link
Member

@tkajtoch tkajtoch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed and tested all changes included in this PR. LGTM

@tkajtoch tkajtoch merged commit 5be1523 into main Dec 3, 2025
9 checks passed
@tkajtoch tkajtoch deleted the feat/flyout-system branch December 3, 2025 11:54
tkajtoch added a commit to elastic/kibana that referenced this pull request Dec 16, 2025
## Summary

This EUI upgrade brings the new Flyout System. While all of the changes
we made are opt-in, we did have to update the DOM nesting in `EuiFlyout`
and `EuiFlyoutResizable`. This change includes making overlay masks a
sibling of flyouts, rather than wrapping the flyouts as children, which
required internal changes to the mask's z-index value. Following
@tsullivan request, please run through your UIs and make sure the
flyouts render as expected, especially in the areas where you override
EuiFlyout styles.

## Dependency updates

- `@elastic/eui`: `v110.0.0` ⏩ `v111.0.0`
- `@elastic/eui-theme-borealis`: `v5.1.0` ⏩ `v5.2.0`

---

## Changes

- Removed `z-index` overrides from various places across Kibana. The
updated EuiFlyout logic calculates `z-index` values dynamically based on
the order of opening flyouts, making the manual overrides unnecessary.
- Updated types of refs passed to `EuiFlyout` and `EuiFlyoutResizable`

## Package updates

### `@elastic/eui` v111.0.0

- Added an opt-in EuiFlyout session management for creating flyout
compositions and journeys effortlessly. Session management handles
side-by-side flyout rendering based on parent-child grouping, simple
flyout transitions with history, state sharing, and more.
([#9202](elastic/eui#9202))
- EuiFlyout session management is an optional feature that can be
enabled by adding `session="start"` to EuiFlyout. Check out the
[documentation](https://eui.elastic.co/docs/components/containers/flyout/session-management)
to learn more.
- Added a new `hasChildBackground` boolean prop (defaults to false) to
`EuiFlyout` ([#9056](elastic/eui#9056))
- Updated `EuiFlyout` with new `onActive` callback and enable stack
managed history controls.
([#9003](elastic/eui#9003))
- Updated `EuiFlyoutMenu` with new prop `historyItems` and refactored
props for back button.
([#9003](elastic/eui#9003))
- Added a new optional `resizable` (boolean) prop to `EuiFlyout`.
Resizability can now be controlled dynamically without the need to use
`EuiFlyoutResizable`.
([#8999](elastic/eui#8999))
- Flyout system menu bar: require tile, support custom actions
([#8897](elastic/eui#8897))
- Added a new `EuiFlyoutMenu` component that provides a standardized top
menu bar for flyouts.
([#8851](elastic/eui#8851))

**Breaking changes**

- Changed the way EuiFlyout renders overlay masks to decouple the
overlay mask from the flyout itself. Now, the overlay mask is a separate
portalled element. ([#9202](elastic/eui#9202))
- This change does not modify the functionality or behavior of flyout
overlays but might affect some custom usages when your application
relies on the specific element nesting within EuiFlyout.

### `@elastic/eui-theme-borealis` v5.2.0

- Updated parameters used for `euiAnimSlightResistance` for a smoother
animation ([#9202](elastic/eui#9202))

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
tsullivan added a commit to elastic/kibana that referenced this pull request Jan 13, 2026
…3806)

~~Depends on elastic/eui#8982
~~Depends on elastic/eui#9202
~~Depends on #245515

This PR enhances the core UX offerings in Kibana with
`core.overlays.openSystemFlyout`.

### `core.overlays.openSystemFlyout`
Opens a system flyout that integrates with the EUI Flyout Manager. Using
a mount point would break the context propogation of the EUI Flyout
Manager, so this method accepts React elements directly rather than
`toMountPoint`.
```typescript
import React, { useRef } from 'react';
import { 
  EuiFlyoutBody, 
  EuiFlyoutFooter,
  EuiText,
  EuiButton,
  EuiButtonEmpty,
  EuiFlexGroup,
  EuiFlexItem 
} from '@elastic/eui';
import type { OverlayRef } from '@kbn/core-mount-utils-browser';

// Create a component or function that opens the system flyout
const openMySystemFlyout = (overlays) => {
  const flyoutRef = useRef<OverlayRef | null>(null);

  const handleClose = () => {
    if (flyoutRef.current) {
      flyoutRef.current.close();
    }
  };

  const FlyoutContent = () => (
    <>
      <EuiFlyoutBody>
        <EuiText>
          <p>This is a system flyout that integrates with EUI Flyout Manager.</p>
          <p>The header is automatically created from the title option.</p>
        </EuiText>
      </EuiFlyoutBody>
      <EuiFlyoutFooter>
        <EuiFlexGroup justifyContent="spaceBetween">
          <EuiFlexItem grow={false}>
            <EuiButtonEmpty onClick={handleClose}>
              Cancel
            </EuiButtonEmpty>
          </EuiFlexItem>
          <EuiFlexItem grow={false}>
            <EuiButton onClick={() => console.log('Save')} fill>
              Save
            </EuiButton>
          </EuiFlexItem>
        </EuiFlexGroup>
      </EuiFlyoutFooter>
    </>
  );

  flyoutRef.current = overlays.openSystemFlyout(<FlyoutContent />, {
    title: 'My System Flyout',
    type: 'overlay',
    size: 'm',
    maxWidth: 600,
    ownFocus: false,
    onClose: () => {
      console.log('System flyout closed');
      flyoutRef.current = null;
    },
    onActive: () => {
      console.log('System flyout became active');
    },
  });

  return flyoutRef.current;
};

// Open the flyout
const flyoutRef = openMySystemFlyout(overlays);

// Close the flyout programmatically from outside
flyoutRef.close();
```

The developer README for the Flyout System is
[here](https://github.com/elastic/eui/blob/feat/flyout-system/packages/eui/src/components/flyout/README.md).

### Example plugin

Along with the new service and documentation changes, a new example
plugin for developers is provided that shows different integration
patterns for the EUI flyout system.

**Screenshot of example plugin:**
<img width="1327" height="836" alt="image"
src="https://github.com/user-attachments/assets/9664f0cb-f793-4a8c-8a96-c46d069756dd"
/>

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Ania Kowalska <ania.kowalska@elastic.co>
Co-authored-by: Lene Gadewoll <lene.gadewoll@elastic.co>
Co-authored-by: Weronika Olejniczak <weronika.olejniczak@elastic.co>
Co-authored-by: Jorge Oliveira <jorge.oliveira@elastic.co>
Co-authored-by: Tomasz Kajtoch <tomasz.kajtoch@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
smith pushed a commit to smith/kibana that referenced this pull request Jan 16, 2026
…stic#233806)

~~Depends on elastic/eui#8982
~~Depends on elastic/eui#9202
~~Depends on elastic#245515

This PR enhances the core UX offerings in Kibana with
`core.overlays.openSystemFlyout`.

### `core.overlays.openSystemFlyout`
Opens a system flyout that integrates with the EUI Flyout Manager. Using
a mount point would break the context propogation of the EUI Flyout
Manager, so this method accepts React elements directly rather than
`toMountPoint`.
```typescript
import React, { useRef } from 'react';
import { 
  EuiFlyoutBody, 
  EuiFlyoutFooter,
  EuiText,
  EuiButton,
  EuiButtonEmpty,
  EuiFlexGroup,
  EuiFlexItem 
} from '@elastic/eui';
import type { OverlayRef } from '@kbn/core-mount-utils-browser';

// Create a component or function that opens the system flyout
const openMySystemFlyout = (overlays) => {
  const flyoutRef = useRef<OverlayRef | null>(null);

  const handleClose = () => {
    if (flyoutRef.current) {
      flyoutRef.current.close();
    }
  };

  const FlyoutContent = () => (
    <>
      <EuiFlyoutBody>
        <EuiText>
          <p>This is a system flyout that integrates with EUI Flyout Manager.</p>
          <p>The header is automatically created from the title option.</p>
        </EuiText>
      </EuiFlyoutBody>
      <EuiFlyoutFooter>
        <EuiFlexGroup justifyContent="spaceBetween">
          <EuiFlexItem grow={false}>
            <EuiButtonEmpty onClick={handleClose}>
              Cancel
            </EuiButtonEmpty>
          </EuiFlexItem>
          <EuiFlexItem grow={false}>
            <EuiButton onClick={() => console.log('Save')} fill>
              Save
            </EuiButton>
          </EuiFlexItem>
        </EuiFlexGroup>
      </EuiFlyoutFooter>
    </>
  );

  flyoutRef.current = overlays.openSystemFlyout(<FlyoutContent />, {
    title: 'My System Flyout',
    type: 'overlay',
    size: 'm',
    maxWidth: 600,
    ownFocus: false,
    onClose: () => {
      console.log('System flyout closed');
      flyoutRef.current = null;
    },
    onActive: () => {
      console.log('System flyout became active');
    },
  });

  return flyoutRef.current;
};

// Open the flyout
const flyoutRef = openMySystemFlyout(overlays);

// Close the flyout programmatically from outside
flyoutRef.close();
```

The developer README for the Flyout System is
[here](https://github.com/elastic/eui/blob/feat/flyout-system/packages/eui/src/components/flyout/README.md).

### Example plugin

Along with the new service and documentation changes, a new example
plugin for developers is provided that shows different integration
patterns for the EUI flyout system.

**Screenshot of example plugin:**
<img width="1327" height="836" alt="image"
src="https://github.com/user-attachments/assets/9664f0cb-f793-4a8c-8a96-c46d069756dd"
/>

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Ania Kowalska <ania.kowalska@elastic.co>
Co-authored-by: Lene Gadewoll <lene.gadewoll@elastic.co>
Co-authored-by: Weronika Olejniczak <weronika.olejniczak@elastic.co>
Co-authored-by: Jorge Oliveira <jorge.oliveira@elastic.co>
Co-authored-by: Tomasz Kajtoch <tomasz.kajtoch@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change PRs with breaking changes. (Don't delete - used for automation)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants