Docs

Documentation versions (currently viewingVaadin 24)

Master-Detail Layout

Master-Detail Layout makes it easy to create responsive horizontally or vertically split UIs.

Master-Detail Layout is component for building UIs with a horizontally or vertically split pair consisting of a master area and a detail area that can responsively switch to an overlay.

Note
Preview Feature

This is a preview version of Master-Detail Layout. You need to enable it with the feature flag com.vaadin.experimental.masterDetailLayoutComponent. Preview versions may lack some planned features, and breaking changes may be introduced in any Vaadin version. We encourage you to try it out and provide feedback to help us improve it.

Important
Scaled down examples
The examples on this page are scaled down so that their viewport-size-dependent behavior can be demonstrated. Some examples also change their behavior based on your browser viewport size.

In the example below, clicking a row in the table reveals the detail area which is rendered next to the master area by default. Drag the splitter to change the width of the layout: the detail area will switch to an overlay when the master area’s minimum width is reached.

Open in a
new tab
link:include::../../../frontend/demo/component/master-detail-layout/react/master-detail-layout-basic.tsx[]
MasterDetailLayout layout = new MasterDetailLayout();
/* Sets the minimum size of the flexible master area to 450px,
   below which the detail area will be rendered as an overlay:*/
layout.setMasterMinSize("450px");
/* Sets the detail area size to a static size: */
layout.setDetailSize("250px");
/* The detail area is revealed when it's populated with a component,
 * and hidden when the component is removed: */
personList.getGrid().asSingleSelect().addValueChangeListener(event -> {
    Person selectedPerson = event.getValue();
    if (selectedPerson != null) {
        personDetail.setPerson(selectedPerson);
        layout.setDetail(personDetail);
    } else {
        layout.setDetail(null);
    }
});

personDetail.addCloseListener(event -> personList.getGrid().deselectAll());

Setting the Overlay Breakpoint

For each area, either a fixed size or a minimum size can be specified. The area with a minimum size takes whatever space is available next to the fixed size area. If the available space is smaller than the minimum size, the detail area is rendered as an overlay.

layout.setMasterMinSize("600px");
layout.setDetailSize("300px");

The two most common configurations are:

  • A flexible master area with a minimum size, next to a fixed-size detail area;

  • A fixed-size master area, next to a flexible detail area with a minimum size.

If both areas are configured to be flexible, whichever reaches its minimum size first determines the overlay breakpoint.

If neither a minimum or a fixed size is explicitly set, the layout tries to determine an appropriate minimum size for each area automatically based on its content.

Stack Mode

By default, the detail overlay is rendered as a drawer that opens from the right-hand side of the layout. By switching to stack mode, the two areas are rendered "stacked" on top of each other, with the detail area fully covering the master area, when rendered as an overlay.

layout.setOverlayMode(MasterDetailLayout.OverlayMode.STACK);

Forced Overlay Mode

The layout can be configured to always render the detail area as an overlay (either as drawer or stack), regardless of minimum sizes:

layout.setForceOverlay(true);

Overlay Containment Modes

The overlay can be configured to render in two different ways, called containment modes:

  • Layout: The overlay only covers the master area (default);

  • Viewport: The overlay covers the entire viewport (i.e. page).

layout.setContainment(MasterDetailLayout.Containment.VIEWPORT);
Note
Limited Modality with Viewport-Containment
Due to browser support constraints, the modality of the detail overlay in viewport containment mode is currently limited to pointer interactions. Elements behind the overlay can be focused by keyboard. This limitation is planned to be addressed in V25.

Orientation

By default, the Master-Detail Layout is split horizontally. This can be changed to a vertical split.

Open in a
new tab
import React, { useEffect } from 'react';
import { useSignal } from '@vaadin/hilla-react-signals';
import { MasterDetailLayout, SplitLayout } from '@vaadin/react-components';
import PersonDetail from 'Frontend/demo/component/master-detail-layout/react/PersonDetail';
import PersonList from 'Frontend/demo/component/master-detail-layout/react/PersonList';
import { getPeople } from 'Frontend/demo/domain/DataService';
import type Person from 'Frontend/generated/com/vaadin/demo/domain/Person';

function Example() {
  const items = useSignal<Person[]>([]);
  const selectedPerson = useSignal<Person | null>(null);
  useEffect(() => {
    getPeople().then(({ people }) => {
      items.value = people;
    });
  }, []);

  return (
    <SplitLayout orientation="vertical" style={{ height: '100%' }}>
      <MasterDetailLayout
        orientation="vertical"
        masterMinSize="150px"
        detailSize="250px"
      >
        <MasterDetailLayout.Master>
          <PersonList
            people={items.value}
            selectedPerson={selectedPerson.value}
            onSelect={(person) => {
              selectedPerson.value = person;
            }}
          />
        </MasterDetailLayout.Master>
        <MasterDetailLayout.Detail>
          {selectedPerson.value ? (
            <PersonDetail
              person={selectedPerson.value}
              onClose={() => {
                selectedPerson.value = null;
              }}
            />
          ) : null}
        </MasterDetailLayout.Detail>
      </MasterDetailLayout>
      <div style={{ flex: '0 0 auto', backgroundColor: 'var(--lumo-contrast-5pct)', textAlign: 'center' }}>
        <span style={{fontWeight: 'bold', minHeight: '1.75em'}}>Drag to resize</span>
      </div>
    </SplitLayout>
  );
}
MasterDetailLayout layout = new MasterDetailLayout();
layout.setOrientation(MasterDetailLayout.Orientation.VERTICAL);

Hiding the Detail Area

The detail area is automatically shown or hidden when content is added or removed from it. There is no explicit property to control the visibility of the detail area.

Master-Detail Layout exposes the following events that can be used to hide the detail area by removing the content from it:

  • backdrop-click: Fired when the user clicks on the backdrop of the detail overlay when it is in drawer mode.

  • detail-escape-press: Fired when the user presses Escape within the detail area.

layout.addBackdropClickListener(event -> {
    // Hide the detail area by removing the content
    layout.setDetail(null);
});

layout.addDetailEscapePressListener(event -> {
    // Hide the detail area by removing the content
    layout.setDetail(null);
});

Router Integration

Master-Detail Layout can be used as a router layout (see Flow/Hilla), so that nested views are automatically rendered in the details area of the component. This allows showing nested views without having to manage the contents of the details area manually when the route changes, while providing the same benefits such as responsive behavior that the component normally provides.

The Flow MasterDetailLayout component implements the RouterLayout interface. When using a view class that extends from MasterDetailLayout as a layout for a nested view, that view is then automatically shown in the details area of the component.

The example below shows how to set up a master and a detail view. The master view is ProductListView, which would show a list of products, and the detail view is ProductDetailView, which shows information about a specific product. The ProductListView extends from MasterDetailLayout, so that it can be used as a route layout by the detail view. It also configures a @Route so that it can be navigated to by itself. Assuming there is a main layout for the application, for example one using AppLayout, it configures that as a parent layout. The ProductDetailView configures a @Route, using the ProductListView as the route layout.

With this setup, when navigating to /products, the layout would only show the product list. When navigating to a product detail, for example /products/1, it would then also show the product details next to, or on top of, the product list.

@ParentLayout(MainLayout.class)
@Route(value = "products", layout = MainLayout.class)
public class ProductListView extends MasterDetailLayout { ... }

@Route(value = "products/:productId", layout = ProductListView.class)
public class ProductDetailView extends VerticalLayout { ... }
Component Usage Recommendation

Split Layout

A component with two content areas and a draggable resize handle between them.