Best Practices Around Page Performance

In order to create the best user experience, here are a few tips around optimizing your Answers site (built with the Hitchhiker theme) for page performance!
:racing_car:

1. Confirm Your JS and CSS are Inlined

As of Theme version 1.16, we inline the theme’s CSS and JS. In your html.hbs file, you should see the data-webpack-inline on the bundle.css and bundle.js, like this:

<link rel="stylesheet" type="text/css" href="{{relativePath}}/bundle.css" data-webpack-inline>

This impacts the html.hbs file; if you previously overrode this file, you might not see the update. Please add the data-webpack-inline to the bundle.js and bundle.css tags or refresh your html.hbs file with the latest version.

2. Remove Unused Formatters via Tree Shaking

All sites built on the Hitchhiker theme come with a set of formatters, to make it easy to spin up a page with great looking cards. However, you might not be using all of them – in which case, they can be removed (reducing the size of the bundle that loads with the page). The bulkiest formatter here is nationalizedPhoneDisplay – if you’re not using it, we definitely recommend removing it.

  1. Assemble the list of cards you’re using
  2. In each card, check the component.js file. (If you haven’t forked the card, this can be found in themes/answers-hitchhiker-theme/cards/[card name]/component.js.
    The formatters that card uses will be listed in dataForRender function’s return statement, with the prefix Formatter. For example, you can find the formatters below that are used in the location-standard card:
dataForRender(profile) {
    return {
      title: profile.name, // The header text of the card
      url: profile.website || profile.landingPageUrl, // If the card title is a clickable link, set URL here
      target: '_top', // If the title's URL should open in a new tab, etc.
      titleEventOptions: this.addDefaultEventOptions(), // The event options for title click analytics
      // subtitle: '', // The sub-header text of the card
      hours: Formatter.openStatus(profile),
      // services: [], // Used for a comma delimited list of services for the location
      address: Formatter.address(profile), // The address for the card
      phone: Formatter.nationalizedPhoneDisplay(profile) // The phone number for the card
      phoneEventOptions: this.addDefaultEventOptions(), // The analytics event options for phone clicks
      distance: Formatter.toLocalizedDistance(profile), // Distance from the user’s or inputted location
      // details: profile.description, // The description for the card, displays below the address and phone
      // altText: '', // The alt-text of the displayed image
      // image: '', // The URL of the image to display on the card
      showOrdinal: true, // If the ordinal should be displayed on the card
      CTA1: { // The primary call to action for the card
        label: 'Call', // The label of the CTA
        iconName: 'phone', // The icon to use for the CTA
        url: Formatter.phoneLink(profile), // The URL a user will be directed to when clicking
        target: '_top', // If the URL will be opened in a new tab, etc.
        eventType: 'TAP_TO_CALL', // Type of Analytics event fired when clicking the CTA
        eventOptions: this.addDefaultEventOptions(), // The analytics event options for CTA clicks
        // ariaLabel: '', // Accessible text providing a descriptive label for the CTA
      },
      CTA2: { // The secondary call to action for the card
        label: 'Get Directions',
        iconName: 'directions',
        url: Formatter.getDirectionsUrl(profile),
        target: '_top',
        eventType: 'DRIVING_DIRECTIONS',
        eventOptions: this.addDefaultEventOptions(),
        // ariaLabel: '',
      }
    };
  }
  1. Next, Shadow formatters.js.
  2. For each formatter, confirm if it’s being used in any of the cards you’re referencing.
    Comment it out in both the import and export statement in formatters.js.

As an example, say I was only using the location-standard card above. I’d shadow formatter.js and comment out all other formatters, like this:

/**
 * The Formatters are a combination of the internal formatters like
 * directions/address formatting and the custom formatters defined by the user.
 * The custom formatters always take precedence if it exists in both internal and
 * custom formatters.
 */

import {
  address,
  phoneLink,
  //phoneDisplay,
  nationalizedPhoneDisplay,
  // emailLink,
  getDirectionsUrl,
  toLocalizedDistance,
  toKilometers, //this is used by toLocalizedDistance
  toMiles, //this is used by toLocalizedDistance
  // isTodayHoliday,
  // bigDate,
  // betterTime,
  // dateRange,
  // snakeToTitle,
  // prettyPrintObject,
  // joinList,
  // image,
  // truncate,
  openStatus,
  // hoursList,
  generateCTAFieldTypeLink,
  // price,
  // highlightField
} from './formatters-internal.js';
import * as CustomFormatters from './formatters-custom.js';

/**
 * Contains some of the commonly used formatters for parsing pieces of profile
 * information. To remove a formatter from the bundle, comment the desired
 * line below.
 */
let Formatters = {
  address,
  phoneLink,
  //phoneDisplay,
  nationalizedPhoneDisplay,
  // emailLink,
  getDirectionsUrl,
  toLocalizedDistance,
  toKilometers, //this is used by toLocalizedDistance
  toMiles, //this is used by toLocalizedDistance
  // isTodayHoliday,
  // bigDate,
  // betterTime,
  // dateRange,
  // snakeToTitle,
  // prettyPrintObject,
  // joinList,
  // image,
  // truncate,
  openStatus,
  // hoursList,
  generateCTAFieldTypeLink,
  // price,
  // highlightField
};
Formatters = Object.assign(Formatters, CustomFormatters);

export { Formatters as default };

3. Make Sure You’re on the Latest Theme

We release page speed improvements with almost every theme update; our recommendation would be to upgrade to the latest version to get these for free, but are a little harder to include ad-hoc. Here are some examples:

  • 1.14 - Removed maps loading on mobile
  • 1.17 - Eliminate Parser Blocking JS in Theme
  • 1.19 - Removed additional unnecessary whitespace in our JS and html files, and optimized which cards are shipped with a build

4. Update Wait Times for Geolocation Component

When a user types a “near me” query, we automatically request their HTML5 geolocation. Once they grant access, it can take some time for that API to respond with their location. By default, we wait 1 second for that information to come back, otherwise we fall back to reverse IP lookup. We also store that location for 5 minutes. You can wait a shorter period of time for the response, or increase the cache maximum age by adding the following configuration to your SearchBar component under ComponentSettings:

"SearchBar": {
  //other config
  "geolocationOptions": {
    // Optional, the maximum amount of time (in ms) a geolocation call is allowed to take before defaulting, defaults to 1 second.
    "timeout": 1000, //reduce this for a faster, but less accurate experience (more likely to use reverse IP)
    // Optional, the maximum amount of time (in ms) to cache a geolocation call, defaults to 5 minutes.
    "maximumAge": 300000, //increase this for a faster, but less accurate experience (in case the user moves around and their location changes)
  }
}

You apply the same configuration for the LocationBias component:

"LocationBias": {
  //other config
  "geolocationOptions": {
    // Optional, the maximum amount of time (in ms) a geolocation call is allowed to take before defaulting, defaults to 1 second.
    "timeout": 1000, //reduce this for a faster, but less accurate experience (more likely to use reverse IP)
    // Optional, the maximum amount of time (in ms) to cache a geolocation call, defaults to 5 minutes.
    "maximumAge": 300000, //increase this for a faster, but less accurate experience (in case the user moves around and their location changes)
  }
}

5. Preload Custom Fonts

If you’re using custom fonts, try preloading them to avoid the font flicker when the page first loads. Steps can be found here:

Hi Rose!

I have a client whose homepage load speed is supposedly negatively affected from loading the JS in the Answers Search bar.

They said - “The mobile score is Poor (33), and the biggest issue is the presence of used JS”

Would upgrading in this case resolve the speed issues here?