Menu
Menu Sheet Overlay
Search
Search Sheet

SEO Developer Guide

    You want your users to find your Progressive Web App in search engine results, but to do that you must optimize your application for search engine crawlers. This guide is focused on the developer features built into the PWA ecosystem: the PageMeta component and built-in structured data.

    Table of Contents

    The PageMeta Component

    To learn more about useful meta tags, please refer to our recommended SEO best practices guide.

    One very basic requirement for good SEO is that all pages within a website (including PWAs) have a title tag (<title>) as well as meta tags (<meta>) with useful and descriptive text. These tags are what search engines use to build their search results with. Consider the following example of a typical search engine result might look like:

    Search engine with multiple results, each one showing a page title and descriptive text

    You can see in the screenshot above that each result has a page title and a description. These texts come from the pages’ title and meta tags, as can be seen below:

    <html>
        <head>
            <title>Merlin's Potions</title>
            <meta name="description" content="Welcome to Merlin's Potions. Camelot's number 1 online supplier of potions, books, ingredients and more." />
        </head>
        <!-- etc. -->
    </html>
    

    However the PWA is a single page web app, and that fact introduces challenges when it comes to controlling the content of the document’s head tag. For example, the head tag does not update by itself when navigating from page to page, because page navigations in a single page app is simulated!

    So how do we empower ourselves to take control of the head tag? We use the PageMeta component for this purpose. With the react-helmet library under the hood, it gives us the ability to dynamically update the head tag.

    To use the PageMeta component, import it into your container and pass it a title and description:

    // The PageMeta component is available from the local component directory
    import PageMeta from '../../components/page-meta'
    
    const MyPage = () => {
        return (
            <div className="t-my-page">
                {/*
                  * Even though this is defined in the body of the application,
                  * it still manages to only update the site's head tag.
                  */}
                <PageMeta
                    title="My Page Title"
                    description="My Page Description"
                />
    
                {/* etc. */}
            </div>
        )
    }
    

    It is advisable that both title text and meta description be provided on all pages (all containers). But if for some reason those are omitted, the PWA will fallback on some simple defaults. Those default values are defined in the app container using the defaultTitle and siteTitleTemplate, and are passed to an instance of react-helmet. You can customize them in the following way:

    // File:
    // web/app/containers/app/container.jsx
    const defaultTitle = 'My Site Name'
    // Thus, if no title is given then the title tag will render as "My
    // Site Name". But if a title is provided, then that will render instead
    

    If there is a particular format that you want your titles to follow, you can provide a title template using siteTitleTemplate (also passed to an instance of react-helmet). The %s is replaced with what ever the current title is.

    // File:
    // web/app/containers/app/container.jsx
    const siteTitleTemplate = `%s | My Site Name`
    // Thus, if the the current title is something like "Potions", the title
    // will render as: "Potions | My Site Name". If no title is provided,
    // then the default title (see above) will render instead.
    

    Lastly, the variables described in the above examples are passed to an instance of react-helmet in the following manner:

    // File:
    // web/app/containers/app/container.jsx
    import {Helmet} from 'react-helmet'
    
    // Somewhere in the App container's markup (usually close to the top)...
    <Helmet
        defaultTitle={defaultTitle}
        titleTemplate={siteTitleTemplate}
    />
    

    Structured Data

    To learn more about structured data overall, please refer to our recommended SEO best practices guide.

    According to Google, structured data is defined in the following way:

    Structured data is a standardized format for providing information about a page and classifying the page content; for example, on a recipe page, what are the ingredients, the cooking time and temperature, the calories, and so on.
    -Introduction to Structured Data

    On a practical level, structured data gives search engines the power to display much richer search results than would otherwise be possible. Instead of just a page title and description, a search engine can also display a product’s price, reviews, and more.

    Notice that the term microdata is also used throughout this document. Microdata is an HTML5 specification for structured data. In other words, microdata is one type of structured data. There are other types of structured data, but this document focuses on microdata specifically.

    A screensheet the demonstrates rich search results, which have built-in breadcrumbs, user review and price ranges

    External Documentation

    Before discussing how exactly structured data is used in a Progressive Web App, it is important to understand how the format works. The following resources are useful for learning the basics of structured data formats, such as microdata:

    Note: Any markup written to use Microdata can be tested using Google’s Structured Data testing tool

    Product Details and List Containers

    The product details and list containers use Schema.org’s product spec.

    Because containers are fully customizable, you are free to modify the markup as you see fit and format the microdata to your meet your exact needs using data from the Redux Store. See below for an extremely simple example of what this might look like:

    // Basic Product Example
    render() {
        const {name, image, price, description} = this.props
    
        return (
            <div itemScope itemType="http://schema.org/Product">
                <h1 itemProp="name">{name}</h1>
    
                // The `itemProp` prop is required. Without it, the image will not
                // be considered part of the product's data. However if you have
                // other images that are unrelated to the product, then those
                // images can exclude `itemProp`
                <img itemProp="image" src={image.src} alt={image.alt} />
    
                <p itemProp="description">{description}</p>
    
                <div itemProp="offers" itemScope itemType="http://schema.org/Offer">
                    $<span itemProp="price">{price}</span>
                    <meta itemProp="priceCurrency" content="USD" />
                </div>
            </div>
        )
    }
    

    Google demonstrates more examples of structured data for products. Additionally, they describe many useful guidelines and some best practices for how products should be formatted.

    Breadcrumbs and Image Components

    The Breadcrumbs component uses the Schema.org’s BreadcrumbsList spec, and the Image component uses the Schema.org’s ImageObject spec.

    Important: note that it is required to include the includeMicroData prop for Breadcrumbs, and itemProp="image" for Image components. Without these props, their data will not be considered part of the structured data and will be ignored by crawlers. See below for a basic example:

    // Basic Product Example
    render() {
        const {name, image, price, description, breadcrumbItems} = this.props
    
        return (
            <div itemScope itemType="http://schema.org/Product">
                <h1 itemProp="name">{name}</h1>
    
                // Remember, the `includeMicroData` is required!
                <Breadcrumbs
                    items={breadcrumbItems}
                    includeMicroData
                />
    
                // Notice that this is the `Image` component now, rather than the
                // `img` tag. Remember that the `itemProp` prop is still required!
                <Image itemProp="image" src={image.src} alt={image.alt} />
    
                <p itemProp="description">{description}</p>
    
                <div itemProp="offers" itemScope itemType="http://schema.org/Offer">
                    $<span itemProp="price">{price}</span>
                    <meta itemProp="priceCurrency" content="USD" />
                </div>
            </div>
        )
    }
    

    Google demonstrates more examples of structured data for breadcrumbs. It includes various use cases, guidelines, and basic best practices.

    Data Beyond Products

    While the Progressive Web App comes out of the box including some basic structured data for products, you as the developer have the freedom to expand the application with as much structured data as you need. Schema.org’s documentation has a wide variety of data structures for you to choose from, including events, persons, places, reviews, and more!

    Just don’t forget to verify your structured data using tools like Google’s structured data testing tool.