Documentation versions (currently viewingVaadin 24)

Upgrading from Vaadin 7 or 8 using Classic Components

Build on the latest Vaadin version with familiar component APIs.

One of the biggest challenges in migrating from Vaadin 7 or 8 to Vaadin Flow is that Flow is based on a different set of web components. The web components of Flow differ from their Vaadin 7 counterparts both in their API and behavior.

Classic Components is an add-on in Flow that provides a set of backwards-compatible components that ease migrating from Vaadin 7 or 8 to the latest Vaadin version. The API, behavior, and even the DOM structure of the components is made to mimic the Vaadin 7/8 components as much as possible.

Classic Components includes the following: HorizontalLayout, VerticalLayout, Panel, Label, GridLayout, and FormLayout. These components either lack counterparts in Flow entirely, or have significantly different APIs, requiring a lot of modifications when migrating. Classic Components also contain many abstract components from Vaadin 8 on which these components were based including AbstractComponent, AbstractOrderedLayout, and others.

To use Classic Components, only the imports for the supported components need to be changed to be able to use most of the same component features.


Classic Components is licensed under Commercial Vaadin Developer License 4.0 (CVDLv4) and part of the Prime Subscription. You’re asked to validate your license or start a trial period when you start using it.

Using Classic Components

Classic Components is intended for migrating your project from Vaadin 7 or 8 to the latest Vaadin version. The pack should work with any Vaadin version after 14, but it’s tested against Vaadin 23. The recommendation is to migrate to Vaadin 23.

To use Classic Components, you first need to add the dependency to your project’s pom.xml or build.gradle, as follows:

  • With Maven, add the pack dependency to pom.xml:

        <!-| other dependencies -->

    After the dependency has been downloaded, you can start using the components by updating the component imports in your Java sources. For the components that are included in the pack, change the import statements to the corresponding Classic packages, for example, com.vaadin.ui.VerticalLayout to com.vaadin.classic.v8.ui.VerticalLayout.

Limitations On Use with Multiplatform Runtime (MPR)

Using Classic Components in combination with Multiplatform Runtime (MPR) isn’t recommended for this version since it can lead to styling conflicts between the two.

Configuring the Component Defaults for Vaadin 7

If you are upgrading from Vaadin 7, you can configure the Classic components to use the Vaadin 7 defaults with the system property vaadin.classic-components.vaadin7defaults. By introducing the property (no value needed), the Vaadin 7 defaults are used.

Included Components

HorizontalLayout and VerticalLayout

Classic Components comes with HorizontalLayout and VerticalLayout designed to work and behave as close as possible to the corresponding components present in the Vaadin 7 or 8 versions. A few of the key features the components support include:

  • Expand Ratio: children with "expand ratio" defined share the available space among them based on their ratios

  • Alignment: each child can be aligned within its container (for example, top-left, middle-center, bottom-right, etc.)

  • Margin: each side of the component’s margin can be enabled or disabled independently

The DOM structure of VerticalLayout and HorizontalLayout was developed to maintain the same set of elements and CSS classes (such as v-verticallayout/v-horizontallayout, v-has-width, v-expand and so on), so that a project being migrated from Vaadin 7 or 8 is likely to be able to use its current styles without changes. Class names defined in the layouts are added in the same way as before. For example, if custom-style is added to a VerticalLayout instance, both custom-style and v-verticallayout-custom-style are added to the DOM element.


The Classic AbsoluteLayout resembles the Vaadin 8 version of AbsoluteLayout. It mimics HTML absolute positioning, letting you set an absolute position to its children.

Missing features
ContextClickEvent and LayoutClickListener aren’t yet implemented in the Classic layouts.

HorizontalLayout and VerticalLayout are both Flow components, which means that they can be used together with any other component (Flow or other Classic Components).


Unlike in the previous implementation, the layouts use flexbox, since this provides a nicer, cleaner way to achieve the same behavior, especially for the "expand ratio" and alignment features. If any of the styles in the project being migrated rely on the previous implementation, they might not work as expected with the Classic layouts.

VerticalLayout and HorizontalLayout use Lumo variables to define their spacing and margin sizes.

Valo Sass variable Lumo CSS variable














The Classic FormLayout resembles, as much as possible, the Vaadin 7 or 8 version of FormLayout. It shows all components added to it in a table with two columns. The left column contains the component’s caption (except for Checkbox). The components themselves appear stacked in the right column. If a component is marked as required, an asterisk is appended to its caption. As with HorizontalLayout and VerticalLayout, FlowLayout extends from the common superclass AbstractOrderedLayout and a selection of methods in classes with the same names from the V8 inheritance hierarchy.

FormLayout is also in Flow
FormLayout is also a Flow component, which means it can be used together with any other component (Flow or other Classic Components).


The Classic GridLayout resembles the Vaadin 8 version of this component. It supports row and column spans, row and column expand ratios, and cell alignment.


The Classic Panel component implements the same behavior as its counterpart in Vaadin 7/8 versions. It comes with updated styles based on the Lumo theme.


While most of the API comes from the Classic API, there are a few methods that are either not implemented or have their signature changed. For example, setIcon(Resource) is deprecated, but you can use setIcon(Icon) instead. However, getIcon() can’t be used, because it originally returns a Resource instance. Instead, the Classic Panel introduces getIconAsIcon(), which returns the Icon instance set previously. You can find the full list of unimplemented methods in "Incompatible and Unsupported API and Migration Instructions".


The Classic Label component supports the same API as its counterpart in Vaadin 7/8. The only unsupported API is setIcon().

As in Vaadin 7/8, it’s possible to change how the component interprets its contents. The content mode can be ContentMode.HTML, ContentMode.PREFORMATTED, and ContentMode.TEXT. The default is ContentMode.TEXT. The caption can be interpreted as HTML by setting the mode with setCaptionAsHtml().


Unlike in the Label component in Vaadin 7/8, the wrapper element is always present, regardless of whether a caption is set or not. While this doesn’t affect the visual layout, it might break some CSS selectors. For example, it might break use of a CSS direct-child selector, such as .my-class > .v-label.

Another deviation from Vaadin 7/8 is when the content mode is set to ContentMode.HTML. Although the anchor and the image tags are still going to work, contrary to Vaadin 7/8, the script tags are completely removed from the content. The same applies when the caption is interpreted as HTML.

Also, as previously mentioned, there is no support for setIcon() at the moment.

Incompatible and Unsupported API and Migration Instructions

Any API that was already deprecated in Vaadin 8 (or 7) doesn’t exist in the Classic Components. You should thus change any code that uses the deprecated APIs before starting the migration.

Any Classic Component API that can’t work or is obsolete in Vaadin Flow is included in the Classic Components as @Deprecated and doesn’t do anything except log a warning in development mode. This is done to make it’s faster to get the project to compile and run, and enables you to see the migration results sooner without having to comment out code.

This section goes through both the incompatible and the unsupported API introduced by each Classic Component class and how you could mitigate the situation if using that API in your project.

The Component Interface

The base Component interface from Vaadin 7 and 8 is replaced by the abstract class com.vaadin.flow.component.Component in Flow. Most of the API is still the same or has changed only slightly. Classic Components introduces any missing API in the AbstractComponent class instead.

Table 1. com.vaadin.ui.Component
Method signature Mitigation

String getId()

Return type changed to Optional<String> by Flow Component

HasComponents getParent()

Return type changed to Optional<Component> by Flow Component

UI getUI()

Return type changed to Optional<UI> by Flow Component

String getCaption()

void setCaption(String caption)

Migrate. Supported only by the classic Label; for other components, you need to move the text to another component, such as Span or Div. Replaced by setLabel(String) in field components in Flow.

String getDescription()

Remove/Migrate. Not supported by Classic Components, and no direct replacement in Flow. Alternatives are available in the Directory.

Resource getIcon()

setIcon(Resource icon)

Remove/Migrate. Not supported by Classic Components. For Flow components, it depends on whether the component supports icons; for example, Button supports icons.

void readDesign(org.jsoup.nodes.Element design, DesignContext designContext)

void writeDesign(org.jsoup.nodes.Element design, DesignContext designContext)

Remove. You shouldn’t be even calling these methods as they are for Vaadin Designer integration only.

The AbstractClientConnector Class

The Classic Components version of the class is in the com.vaadin.classic.v8.server package.

Table 2. com.vaadin.server.AbstractClientConnector
Method signatures Mitigation

protected void fireEvent(EventObject event)

Migrate. Flow components' ComponentEventBus needs event object type to be ComponentEvent<T> instead. Use getEventBus().fireEvent(event) to fire the event. From outside the component, use ComponentUtil::fireEvent().

protected void addExtension(Extension extension)

Collection<Extension> getExtensions()

void removeExtension(Extension extension)

Remove/Migrate. Flow components can’t be extended with extensions. The method of migration depends on what the extension does. For pure server-side extensions, you can subclass the component. For extensions with client-side parts, you need to make a JavaScript file and call it from Java code inside the extended class.

Registration addListener(Class<?> eventType, SerializableEventListener listener, Method method)

protected Registration addListener(String eventIdentifier, Class<?> eventType, SerializableEventListener listener, Method method)

Migrate. For external usage, use distinct addXyzListener API in the component or ComponentUtil::addListener() methods. For usage inside the component, this is replaced by Flow’s ComponentEventListener added to ComponentEventBus, which is only accessible inside the component.

protected void addMethodInvocationToQueue(String interfaceName, Method method, Object[] parameters)

Remove. This method was only for internal usage; you shouldn’t be using it. It doesn’t apply for Flow.

protected SharedState createState()

protected SharedState getState()

protected SharedState getState(boolean markAsDirty)

Class<? extends SharedState> getStateType()

protected void updateDiffstate(String propertyName, JsonValue newValue)

Remove/Migrate. SharedState isn’t applicable to Flow; data is transferred through the Element API with properties and attributes instead.

JsonObject encodeState()

Remove. Internal method that doesn’t apply to Flow.

static Iterable<? extends ClientConnector> getAllChildrenIterable(ClientConnector connector)

Migrate. Doesn’t apply directly to Flow; you can get child components with Component::getChildren()

String getConnectorId()

Remove/Migrate. Doesn’t apply to Flow. Manually set IDs can be used with setId() / getId(). Internally, Flow uses StateNode::getId() to track nodes between client and server.

ErrorHandler getErrorHandler()

void setErrorHandler(ErrorHandler errorHandler)

Migrate. Flow doesn’t have a component-level error handler. Migrate to use VaadinSession::setErrorHandler() instead. Or, depending the type of error, you could use an error view instead.

Collection<?> getListeners(Class<?> eventType)

Remove/Migrate. No replacement available in Flow. Use the fireEvent() API from ComponentEventBus or ComponentUtil to notify all listeners.

protected Resource getResource(String key)

protected void setResource(String key, Resource resource)

Remove. Not applicable in Flow.

ServerRpcManager<?> getRpcManager(String rpcInterfaceName)

List<ClientMethodInvocation> retrievePendingRpcCalls()

Remove. Internal method that isn’t applicable in Flow.

protected <T extends ClientRpc> T getRpcProxy(Class<T> rpcInterface)

protected <T extends ServerRpc> void registerRpc(T implementation)

protected <T extends ServerRpc> void registerRpc(T implementation, Class<T> rpcInterfaceType)

Remove/Migrate. Not applicable in Flow. See documentation for Remote Procedure Calls (RPC) between the client and the server.

boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path)

Remove. Internal method that shouldn’t even be used.

protected boolean hasListeners(Class<?> eventType)

Migrate. The event type is different; Classic Components have both protected boolean hasListeners(Class<? extends ComponentEvent>) and hasListener(Class<? extends ComponentEvent>). Flow’s Component introduces the latter.

The AbstractComponent Class

The Classic Components version of the class is in the com.vaadin.classic.v8.ui package.

Table 3. com.vaadin.ui.AbstractComponent
Method signature Mitigation

protected void fireComponentErrorEvent()]

Remove/Migrate. Not supported by Classic Components and no direct replacement in Flow. The method of migration depends on what the error event was for.

protected void focus()

Migrate. You need to first check whether the component implements com.vaadin.flow.component.Focusable, and then call focus() on it.

protected ActionManager getActionManager()

Migrate. Not supported by Classic Components. See how to add shortcuts in Flow.

ErrorMessage getComponentError()

ErrorMessage getErrorMessage()

void setComponentError(ErrorMessage componentError)

Remove/Migrate. Not supported by Classic Components and, in Flow, error messages are component-specific.

protected Collection<String> getCustomAttributes()

Remove. You shouldn’t even be calling this, as it was for Vaadin Designer integration only.

boolean isCaptionAsHtml()

void setCaptionAsHtml(boolean captionAsHtml)

Migrate. Supported only by the classic Label component. For other components, you need to move the text to another component, such as Span or Div. Replaced by setLabel(String) in field components in Flow.

protected boolean isReadOnly()

protected void setReadOnly(boolean readOnly)

Remove/Migrate. Not supported by Classic Components. In Flow, only field components can be read-only.

protected boolean isRequiredIndicatorVisible()

protected void setRequiredIndicatorVisible(boolean visible)

Remove/Migrate. Not supported by Classic Components. In Flow, only field components can have a required indicator.

boolean isResponsive()

void setResponsive(boolean responsive)

Remove. Not supported by Classic Components or Flow components.

void setDescription(String description)

void setDescription(String description, ContentMode mode)

Remove/Migrate. Not supported by Classic Components, and no direct replacement in Flow. Alternatives are available in the Directory.