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!
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.
- Assemble the list of cards you’re using
- 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 indataForRender
function’s return statement, with the prefixFormatter
. For example, you can find the formatters below that are used in thelocation-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: '',
}
};
}
- Next, Shadow formatters.js.
- 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: