Modifying the Bootstrap Page at Runtime
Application Shell
The application shell model aims to make your website faster by loading the 'important' parts of the web page first, so improving the user experience. The key to this is to deliver the minimum HTML, CSS and JavaScript required to display the user interface on the first visit, and eventually cache it for use on subsequent revisits.
The Application Shell in Vaadin is also known as the Bootstrap Page, or index.html
.
Modifying the Application Shell
In Vaadin 15 and later, the developer has full control of the contents of index.html
.
You can modify it in various ways:
-
On the client side, by editing
frontend/index.html
when the content is static, for instance the<viewport>
tag. -
On the server side, for changes that require some dynamic server content, or when Java syntax is preferred; for example, making the application installable by enabling the
@PWA
built-in feature.-
Implement
AppShellConfigurator
for cases covered by theAppShellSettings
API, or by annotations. -
Configure an
IndexHtmlRequestListener
for advanced cases that modify the document structure.
-
Customizing the Application Shell Template
The Vaadin servlet uses the frontend/index.html
file as a template to generate the bootstrap page response.
The servlet processes the template and injects the following information:
-
<base href='./relative/to/root'>
: Vaadin calculates the relative path from the current request path to the root path of the application. That’s required for relative links in view templates to work correctly. -
Bundled script: Vaadin automatically adds the bundled and optimized script generated from the
frontend/index.ts
file. It uses a pre-configured Vite instance that’s included together withvaadin-maven-plugin
as a module bundler. Therefore, thefrontend/index.html
template doesn’t need to include theindex.ts
(orindex.js
) script explicitly.
Tip
|
You can change the
The frontend directory pathfrontend directory path can be changed by providing the property vaadin.frontend.frontend.folder when running the Maven goals prepare-frontend or build-frontend from vaadin-maven-plugin .
|
Default Bootstrap Template and Entry Point
If the index.html
or index.ts
files in the front-end folder are missing, vaadin-maven-plugin
generates a default corresponding file in the target
folder.
In that case the application uses server-side routing only.
You can take control of these files by moving them into the frontend
folder.
By default these files look similar to the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body, #outlet {
height: 100vh;
width: 100vw;
margin: 0;
}
</style>
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
<!-- vaadin-router in index.ts needs an outlet for displaying the views -->
<div id="outlet"></div>
</body>
</html>
// import Vaadin client-router to handle client-side and server-side navigation
import { Router } from '@vaadin/router';
// import Flow module to enable navigation to Vaadin server-side views
import { Flow } from '@vaadin/flow-frontend';
const {serverSideRoutes} = new Flow({
imports: () => import('../target/frontend/generated-flow-imports')
});
const routes = [
// for client-side, place routes below (more info https://hilla.dev/docs/routing)
// for server-side, the next magic line sends all unmatched routes:
...serverSideRoutes // IMPORTANT: this must be the last entry in the array
];
// Vaadin router needs an outlet in the index.html page to display views
export const router = new Router(document.querySelector('#outlet'));
router.setRoutes(routes);
This is the best place to customize the application shell, for example to put an analytics tag in the page.
...
<head>
<title>My App</title>
</head>
<body>
...
<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'my-app-bootstrap');
</script>
</body>
Customizing the Application Shell at Runtime
The AppShellConfigurator Interface
In Java code, when adding dynamic content during the bootstrap process, use the AppShellConfigurator
marker interface rather than editing the index.html
.
Note
|
There must be a single application shell for the entire Vaadin application, so that no more than one class can implement AppShellConfigurator .
|
Note
|
AppShellConfigurator replaces the obsolete PageConfigurator interface.
|
The AppShellConfigurator.configurePage() Method
Override configurePage()
to add content to the index.html
template by calling the following AppShellSettings
methods:
-
AppShellSettings.setViewport()
to set the viewport value; this replaces the viewport present in theindex.html
template. -
AppShellSettings.setPageTitle()
to set the initial page title; this replaces the template title tag. -
AppShellSettings.setBodySize()
to configure the body width and height values. -
AppShellSettings.addMetaTag()
to append meta tags to the head. -
AppShellSettings.addInlineFromFile()
to include content from resource files. -
AppShellSettings.addInlineWithContents()
to add arbitrary content. -
AppShellSettings.addLink()
to add links to the head. -
AppShellSettings.addFavIcon()
to configure the favicon. -
AppShellSettings.getLoadingIndicatorConfiguration()
to configure a loading indicator when legacy bootstrapping is used (deprecated; see the details after the code example). -
AppShellSettings.getReconnectDialogConfiguration()
to configure the reconnect dialog when legacy bootstrapping is used (deprecated; see the details after the code example). -
AppShellSettings.getPushConfiguration()
to customize the push mechanism when legacy bootstrapping is used (deprecated; see the details after the code example).
public class AppShell implements AppShellConfigurator {
@Override
public void configurePage(AppShellSettings settings) {
settings.setViewport("width=device-width, initial-scale=1");
settings.setPageTitle("A cool vaadin app");
settings.setBodySize("100vw", "100vh");
settings.addMetaTag("author", "bunny");
settings.addFavIcon("icon", "icons/icon-192.png", "192x192");
settings.addLink("shortcut icon", "icons/favicon.ico");
settings.addInlineFromFile(
TargetElement.BODY,
Position.APPEND,
"custom.html",
Wrapping.AUTOMATIC);
settings.addInlineWithContents(Position.PREPEND,
"console.log(\"foo\");", Wrapping.JAVASCRIPT);
}
}
public class ServiceListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addUIInitListener(uiInitEvent -> {
LoadingIndicatorConfiguration indicator = uiInitEvent.getUI()
.getLoadingIndicatorConfiguration();
indicator.setApplyDefaultTheme(false);
indicator.setSecondDelay(700000);
PushConfiguration push = uiInitEvent.getUI().getPushConfiguration();
push.setPushMode(PushMode.AUTOMATIC);
ReconnectDialogConfiguration dialog = uiInitEvent.getUI()
.getReconnectDialogConfiguration();
dialog.setDialogText("reconnecting...");
});
}
}
Java Annotations
Vaadin provides a set of annotations to modify the application shell. However, in Vaadin 15, unlike in previous versions, these annotations must be placed in the application shell class.
-
@Viewport
to set the viewport value. -
@PageTitle
to set the initial page title. -
@BodySize
to configure the body size. -
@Meta
to append meta tags to the head. -
@Inline
to include content from resource files in theindex.html
. -
@PWA
to define application PWA properties. -
@Push
to configure server push.
@Viewport("width=device-width, initial-scale=1")
@PageTitle("A cool vaadin app")
@BodySize(height = "100vh", width = "100vw")
@Meta(name = "author", content = "bunny")
@Inline(wrapping = Wrapping.AUTOMATIC,
position = Position.APPEND,
target = TargetElement.BODY,
value = "custom.html")
@PWA(name = "Cool Vaadin App", shortName = "my-app")
@Push(value = PushMode.MANUAL, transport = Transport.WEBSOCKET)
public class AppShell implements AppShellConfigurator {
}
Note
|
Modifications in AppShellConfigurator.configurePage() have priority over the equivalent annotations.
|
Note
|
Annotations don’t cover all the cases that can be achieved when overriding the AppShellConfigurator.configurePage() method.
|
The IndexHtmlRequestListener Interface
For advanced cases not covered in the previous section, content can be modified via an IndexHtmlRequestListener
.
An implementation of the listener should be added via a ServiceInitEvent
when a VaadinService
is initialized.
See the ServiceInitListener tutorial for details of using Vaadin ServiceInitListeners
.
The following example changes the body class dynamically:
public class MyIndexHtmlRequestListener implements
IndexHtmlRequestListener {
@Override
public void modifyIndexHtmlResponse(
IndexHtmlResponse indexHtmlResponse) {
Document document = indexHtmlResponse.getDocument();
Element body = document.body();
body.classNames(computeBodyClassNames());
}
private Set<String> computeBodyClassNames() {
// Introduce some logic to dynamically change the body class
return Collections.singleton("my-className");
}
}
This can also be achieved using a servlet container deployment property with the name useDeprecatedV14Bootstrapping
.
Note, however, that this option is only supported if webpack is used as the front-end build tool and not if the application uses Vite (which is the default).
webpack can be enabled using its associated feature flag.
Learn more about how to enable it here.
38A2B3F1-CC6B-45DF-8CB8-9DEF23BA53B0