Preserving the State on Refresh

When a URL is entered in the browser, Vaadin’s routing subsystem resolves it into a view component by inspecting @Route class annotations. When a matching class is found, a new instance is created by default. This also happens when the user refreshes the page in the same browser tab.

Occasionally, you may want to keep the state of the view between these refreshes. For example, if the view contains many data entry components, and the user is likely to refresh the page (intentionally or unintentionally) before the data is persisted in the backend. By preserving the view, you ensure the entries are not lost and provide a better UX. Another use case is supporting browser tab-specific "sessions" as an alternative to the standard cookie-based session.

The @PreserveOnRefresh annotation instructs Vaadin to re-use the view component of a route, whenever the route is reloaded in the same browser tab. The routed component instance is then the same server-side object that was created in the first request, with all of its state (member fields, subcomponent hierarchy, and so on) preserved.

Preserving the State of a Component

To make a single-view component preserve its content on refresh, simply add the @PreserveOnRefresh annotation to the class.

Example: Adding the @PreserveOnRefresh annotation to the PreservedView class.

@Route("myview")
@PreserveOnRefresh
public class PreservedView extends VerticalLayout {

    public PreservedView() {
        add(new TextField("Content will be preserved"));
        // ...
    }
}

If the view component has a router layout (via the layout parameter of the @Route annotation), the router layout is also preserved on refresh. As an alternative, you can add the @PreserveOnRefresh annotation to a class that implements RouterLayout.

Example: Adding the @PreserveOnRefresh annotation to an implementation of RouterLayout.

@PreserveOnRefresh
public class PreservedLayout extends FlexLayout
        implements RouterLayout {

    public PreservedLayout() {
        // ...
    }
}
  • The PreservedLayout instance itself, as well as any view laid out inside it, is preserved on refresh.

Any elements that are not direct children of the view component, such as notifications and dialogs, are also preserved. This means that if your @PreserveOnRefresh annotated-view class opens a dialog, in which the user makes edits and then refreshes, the dialog remains visible in its edited state.

Preconditions and Limitations

Using the @PreserveOnRefresh annotation has the following conditions/limitations:

  • The annotation must be placed in a component class that is a route target (typically annotated with @Route) or on a component that implements RouterLayout.

  • The annotation does not support partial preserving. You cannot preserve only some components on the route chain. If the annotation is present on any component in the chain, the entire chain is preserved.

  • The component is persisted only when reloaded in the same browser tab (the window.name client-side property is used to identify the tab), and only if the URL stays the same (visiting another route or changing a URL parameter discards the component state permanently).

  • Vaadin 10 and later does not preserve the UI instance between refreshes. The view is detached from its previous UI and then attached to a fresh UI instance on refresh.

  • The AttachEvent and DetachEvent events are also generated when a preserved component is moved to a new UI. This means, for instance, that your view component should expect multiple calls to onAttach and listeners registered through addAttachListener during its lifetime.