Blog

Adding JSON-LD and Open Graph to Vaadin apps for link previews

By  
Sami Ekblad
Sami Ekblad
·
On Dec 19, 2024 4:12:25 PM
·

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. 

 

Open Graph

Microdata

JSON-LD

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 property attributes

HTML tags using itemprop attributes

Inline JSON script using <script> tag

Embedding Location

<head> section (meta tags)

Inline with HTML content

<head> section or inline <script>tag

Example

<meta property="og:title" content="MyApp">

<meta itemprop="name" content="MyApp">

<script type="application
/ld+json"> {"name": "MyApp",} </script>

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. 🚀

Further reading:

Sami Ekblad
Sami Ekblad
Sami Ekblad is one of the original members of the Vaadin team. As a DX lead he is now working as a developer advocate, to help people the most out of Vaadin tools. You can find many add-ons and code samples to help you get started with Vaadin. Follow at – @samiekblad
Other posts by Sami Ekblad