Links are crucial for user experience and discoverability, even in apps behind login screens. This tutorial shows how to use JSON-LD and Open Graph metadata to create better link previews, improve branding, and boost engagement.
So, why does linking even matter?
First, we must accept that Vaadin applications typically are behind login screens, and linking to them only redirects them to the login page. However, for a better user experience, you still might want to include them. Here are some of the scenarios:
- Users: You might send email links that lead users directly to specific parts of your app after login. Linking directly to a problem ticket or report is better than saying, “Log in and go find it.”
- Integration: Tools like Slack, Microsoft Teams, or ticketing systems can include and display links to direct a user to the correct part of the app with more information.
- Branding: Polished public URLs can increase trust and credibility for your app, especially when shared via social media or embedded in blogs.
While the Vaadin session might still be valid, the information fetched by social media bots and crawlers or by users who have not yet logged in is handled as anonymous. This introduces certain limitations regarding the information you might want to present when generating metadata or content previews. Additionally, social media bots only request the page content when the link is first shared or indexed.
This typically limits you to present very generic data, like:
- Application or service title
- Description to help users understand what the link is about
- Default social media image
And this is a good start already and a fallback. Additionally, for deep linking, you might want to include some generic information about the page or document they are linking to, like the document title and summary with a note about login being needed.
Metadata in three forms
Basically, you have three options for including metadata on your web page. While JSON-LD is the recommended way for social previews, there are still Microdata and JSON-LD that search engines use.
Standard |
Open Graph Protocol (OGP) |
Schema.org Microdata |
Schema.org JSON-LD |
Used in |
Social media previews and sharing |
SEO optimization for legacy tools |
SEO optimization (modern, preferred method) |
Support |
Facebook, LinkedIn, Twitter (partially) |
Search engines (Google, Bing, etc.), older parsers |
Google, modern search engines, voice assistants |
Syntax |
HTML meta tags using |
HTML tags using |
Inline JSON script using |
Embedding Location |
|
Inline with HTML content |
|
Example |
|
|
|
Vaadin Router has you covered - mostly
As you already might know, Vaadin’s Router simplifies deep linking in single-page applications (SPAs) by mapping URLs to views. It takes care of the navigation basics, including inbound links:
- Direct URL navigation: Vaadin Router maps URLs (like example.com/dashboard) to corresponding views. If a user enters a URL directly into the browser, the Vaadin server detects the URL and serves the correct view. When using the application, client-side logic ensures smooth navigation without full-page reloads.
- Automatic handling of deep links: This feature allows users to bookmark specific views or share URLs with others. If a user requests a URL like example.com/dashboard/report/123, Vaadin Router parses the path and maps it to the associated view. It also passes relevant parameters (e.g., reportId = 123) to the view.
- In-app redirects for authentication: When the app requires authentication, Vaadin can redirect users to a login page. After login, Vaadin redirects users back to the original path.
Most of this is handled through @Route
annotation. The limitation with social previews, SEO, and external links comes when the browser requests a deep link (e.g., example.com/dashboard/report/123) for the first time: Vaadin's server must still serve the mostly empty index.html
file, which bootstraps the full app.
To include data for bots and crawlers, we need to modify the index.html
.
Metadata injection in Vaadin
The IndexHtmlRequestListener
is part of the Vaadin Flow API that allows you to intercept and modify the index.html
response on the server side:
Implementation and registration is pretty straightforward in your Spring Boot application bootstrap:
@Component
public static class ApplicationServiceInitListener
implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
event.addIndexHtmlRequestListener(event -> {
// TODO: Use path to generate different content if needed
String path = event.getVaadinRequest().getPathInfo();
// Inject metadata into the head element
Element head = response.getDocument().head();
injectSeoAndSocialTags(head,
"MyApp",
"This is my application.",
"http://public.app.url/",
"http://public.app.url/images/socialpreview.png",
LocalDate.now());
});
}
}
The injectSeoAndSocialTags
utility method generates and injects JSON-LD, Microdata, and Open Graph metadata into the <head>
element of an HTML document to optimize SEO and social media previews. Here, you can use getPathInfo
to adjust the metadata based on the requested URL, but for simple applications, static content is usually enough:
public static void injectSeoAndSocialTags(Element head,
String name,
String description,
String url,
String imageUrl, LocalDate datePublished) {
// Default to today
String formattedDate = datePublished != null
? datePublished.format(DateTimeFormatter.ISO_DATE)
: LocalDate.now().format(DateTimeFormatter.ISO_DATE);
// JSON-LD
String jsonLdContent = String.format("""
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "%s",
"description": "%s",
"url": "%s",
"datePublished": "%s",
"image": {
"@type": "ImageObject",
"contentUrl": "%s",
"caption": "%s"
}
}
""", name, description, url, formattedDate, imageUrl, name);
head.appendElement("script")
.attr("type", "application/ld+json")
.appendText(jsonLdContent);
// Microdata
head.appendElement("meta").attr("itemprop", "name")
.attr("content", name);
head.appendElement("meta").attr("itemprop", "description")
.attr("content", description);
head.appendElement("meta").attr("itemprop", "url")
.attr("content", url);
head.appendElement("meta").attr("itemprop", "datePublished")
.attr("content", formattedDate);
head.appendElement("meta").attr("itemprop", "image")
.attr("content", imageUrl);
// Open Graph
head.appendElement("meta").attr("property", "og:title")
.attr("content", name);
head.appendElement("meta").attr("property", "og:description")
.attr("content", description);
head.appendElement("meta").attr("property", "og:url")
.attr("content", url);
head.appendElement("meta").attr("property", "og:image")
.attr("content", imageUrl);
head.appendElement("meta").attr("property", "og:type")
.attr("content", "website");
head.appendElement("meta").attr("property", "og:site_name")
.attr("content", name);
}
Some additional notes and tips for the metadata content:
- Links included in metadata must be publicly accessible. Social media crawlers and search engine bots cannot access private or local addresses like http://localhost:8080
- Always provide metadata URLs using HTTPS.
- Keep the content concise and informative:
- Title: Limit to around 60 characters for optimal display.
- Description: Max 150 characters to ensure the full text is visible on most platforms.
- Remember, on social media, the crawlers typically fetch once. If you update metadata (e.g., title, image), the crawlers will not automatically re-fetch the new content.
- Ensure the image used has the correct aspect ratio, resolution, and format. This varies a bit depending on your target platform, but it is typically 2:1 with a minimum 600x300px resolution. JPG and PNG are safe choices.
Here is an example of a Slack message displaying a link to a Vaadin application with valid JSON-LD metadata:
Adding SEO and social media metadata to your Vaadin applications can make a difference. By combining these techniques, you ensure your application is discoverable, shareable, and user-friendly across platforms.
New to Vaadin? Kick off your first project at start.vaadin.com. 🚀