Jul 15, 20203:15 AM - edited Jul 16, 202011:46 AM
Contributor | Partner
A place to gather resources, issues, and solutions to optimize website speed
Optimizing a Hubspot site to load quickly is difficult at the moment. Good solutions are scattered and the current documentation has gaps. Let's use this post to donate solutions that work and start a conversation to fix what doesn't.
This guides needs two sections:
Working solutions that everybody can do
Unique Hubspot issues
1. Working Solutions
If you're new, start with the Hubspot CMS-Boilerplate—it has all the answers. Carefully step through the docs until you're comfortable with local development (don't forget Github Actions).
Snippets Coming Not That Soon
Webpack config
Hubspot Autouploader
SASS/SCSS
Image optimization
Image deferral
Critical path CSS
Videos
Font optimization
Researching and Need Help
Deferring Hubspot analytics
Separate CSS/JS bundles at page level
2. Unique Hubspot Issues
Some features (like tracking codes) are always loaded—Hubspot without tracking analytics isn't Hubspot. Other features (like contact forms) are optional but require creative implementations to prevent nerfed PageSpeed scores.
The Worst (Only?) Offenders
1. Hubspot Forms
Embedding a Hubspot form can drop your pagespeed score by up to 20% on mobile. Why do the form and "//js.hsforms.net/forms/v2.js" script have such a huge cost?
If you are 100% sure that your form will appear "below the fold", this is a working solution:
<!-- Add somewhere in your HTML -->
<div id="form-holder"></div>
/********************
Add somewhere in your JS
******************/
function userScroll() {
var hsform = document.createElement('script');
hsform.src='//js.hsforms.net/forms/v2.js';
hsform.async = true;
document.head.appendChild(hsform);
const currentScroll = window.pageYOffset;
if (currentScroll > 0) {
if (window.hbspt) {
window.hbspt.forms.create({
portalId: "[your-portal-id]",
formId: "[your-form-id]",
target: '#form-holder'
});
window.removeEventListener('scroll', userScroll, false);
}
}
}
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'complete') {
window.addEventListener('scroll', userScroll, false);
}
});
Please reply if you have a better solution or have an idea to make this work for forms that appear "above the fold".
Jon McLaren pointed out some issues with this solution in the developer Slack channel.
Scroll is not the best trigger for performance reasons
Not a good idea to depend on scrolling for important content
This could trigger when a lot of other things are also happening
It's not clear if these files are needed but they are automatically included. As far as I know, the following files are always loaded and cannot be removed:
Should we be able to define custom cache policies to help repeat visits load faster? Is there a downside to setting these to far in the future and using a hash to load new versions?
5. Which files are required for a locally developed Hubspot site to publish? Can they be combined to make fewer HTTP requests?
6. Does it ever make sense to build a Hubspot site that doesn't include standard Hubspot analytics?
7. Which of these mystery files am I actually generating myself by accident? Can I prevent them from loading?
8. Are there any other hidden files or bloated files that we don't need 100% of the time?
9. Maybe Google PageSpeed scores aren't a good reflection of most user experiences on your site?
A place to gather resources, issues, and solutions to optimize website speed
Hello,
Are you available to consult with us and have a look at our HubSpot templates to see if there are modifications that can be made which will improve Core Web Vitals and Page Speed on mobile?
A place to gather resources, issues, and solutions to optimize website speed
Hey there @flipflopflower3, unfortunately I can't give 1:1 individual audits for every customer. We do have some tools you can use to identify some areas of improvement.
A place to gather resources, issues, and solutions to optimize website speed
Related to loading jQuery from the footer - we've released a guide which shows you how to upgrade to the latest version of jQuery. That may give you some performance benefits, along with new features.
A place to gather resources, issues, and solutions to optimize website speed
Updating my prior answer further.
In 2023, if you are still using jQuery frequently, I highly encourage reviewing what you're actually using it for. Much of jQuery's functionality has an easy to use equivalent baked directly into JavaScript now.
In all your templates you want to define the load_css variable before the call of the base.html, so for example for the blog template it will be something like this:
{% set load_css = "../../css/blog.css" %} {% extends "./layouts/base.html" %}
Repeat for each template file.
Create the {template-name}.css files that will call just the needed CSS files based on the template/page. I would also suggest to have a "general" one that will contain the across website element that you will also need such _reset, _normalize.css _typograghy.css etc. So it's easier to keep it right and simpler. Example of my blog.css file:
{% include './general.css' %} {% include './_blog.css' %}
My version is based on an outdated boilerplate version so double check your needs here*. Ideally you will get rid of the theme-overrides.css and integrate where applies. Notice the load_css is calling the new blog.css file (not _blog.css) because it not a fragmented one (it contains the calls to those fragmented files that would be used). Repeat for all you want to split, for example blog.css, lp.css, page.css and system.css.
Explanation:
The variable load_css will load a css file, for example blog.css. It will contains a similar structure to the current main.css file, but instead calling all the fragmented css files will call just based on the template you are visting and it needs. The only "issue" is that on the website side, you can't really fragment that much because you don't know if it will include a module, a form a table etc. But it definetly worth it for the blog side.
Regarding the new dnd I am very concern about how HS manages background images for the sections (as background-image CSS properties). It would be much more worth it to have them as image elements so you can lazyload them too (that is what we used to do, and I could do a CM for it, but I would like to use as much HS tools this time instead "hacking" ways that will increase dificulty for the marketers). Other option is make them CSS class dependent so will only load after you scroll until that point, but not as good as the previous suggestion -unnecesary JS handle-.
If this answer helps you to solve your questions please mark it as a solution.
A place to gather resources, issues, and solutions to optimize website speed
Hey @Gonzalo responding to your feedback about dnd_areas and background images.
I will relay this feedback internally. To be honest this lazy loading technique you're describing is effectively a "hack" to get browsers to do what you want. For HubSpot to provide that functionality it would require HubSpot injecting a JavaScript file to load that the developer has no control over.
It's also not at all unlikely that a form of native lazy loading will come for background images after developers have seen the benefits of native lazy loading. Native lazy loading is preferrable for a lot of reasons - it is better for accessibility, you're less likely to affect Cumulative Layout Shift because you are not swapping say a 1px by 1px image with a real full-res image, it also transfers the work of optimizing the performance to the web browser.
For background images - CLS and accessibility is already kind of factored out, so the main goal is just serving the image in a performant way.
There is by far more intelligent and meaningful optimization that a browser can do than developers can do using JavaScript. Any JavaScript needed to lazy load requires a blocking JS script in the head of a page. Native lazy loading removes the file/script all-together removing both a network request and script execution. It can also take advantage of it's awareness of what images the browser already has cached.
Meaning at some point possibly soon, the JavaScript would become unnecessary, and a breaking change would be needed to upgrade websites to the better native implementation.
For img elements we're currently thinking through how we could deliver the best experience for developers and marketers to be able to control native lazy loading. Would love to hear any thoughts anyone has regarding that.
I didn't explained well as I wanted to provide the "current solution" I am doing, so it was a confusing feedback.
I ment to say that ideally, you will handle the current background-image in the new DND as <img loading="lazy" ... >. This will require some extra tweaks, but as today its pretty easy to do something like:
.dnd-section{ position: relative } .dnd-section > .background{ position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: -1; object-fit: cover; /** or add extra controls here */ }
This can be included as default and will be easily ovewritable if needed.
Thank you 😉
If this answer helps you to solve your questions please mark it as a solution.
Sep 10, 202011:48 AM - edited Sep 10, 202011:57 AM
Contributor
A place to gather resources, issues, and solutions to optimize website speed
Hi Jon!
I think the option to use native lazy loading would be great! I recently created a custom image module that incorperates lazy loading, but having this loading="lazy", native browser option in hubspot would a +1.
The only thing is, this is a LOT of work to switch out our old image modules with the new lazy loading one. If there was an option to flip a switch to automatically turn all the OLD image modules to native lazy loading that would be AMAZING. Because we still haven't switched all our image modules, as you can see it's very time consuming and feel as if I will never get finished with it as there are some pages like blogs that don't have an image modules used in the design manager (and this is just one example) etc etc.. very convuluted!
A place to gather resources, issues, and solutions to optimize website speed
Nice! I'll include a warning here - I always recommend against prematurely optimising images using the lazy loading technique. Doing it this way can have some negative side effects:
1. If for any reason JS doesnt run - you're going to be missing a bunch of images.
2. It's harder for search engines to crawl your images (if you care about this)
3. If not done correctly, it can make performance worse. You need to make sure your images have a height/width set otherwise you're going to trigger a content reflow.
There are some cases in which it's good, but just adding without checking your analytics and understanding your audience better is not a good idea.
Jul 17, 20202:13 AM - edited Jul 17, 20202:15 AM
Recognized Expert | Elite Partner
A place to gather resources, issues, and solutions to optimize website speed
I have to agree! The script is triggered when you set a lazy class and pass the data-src attribute, so you have full control on what images you want to optimize, but use this wisely. We always include a no-js fallback to prevent any issues.
Learn more about HubSpot by following me on LinkedIn or YouTube
✅ Did my answer solve your issue? Help the community by marking it as the solution.
Jul 20, 202011:18 AM - edited Jul 20, 202011:19 AM
HubSpot Employee
A place to gather resources, issues, and solutions to optimize website speed
For lazy loading I recommend using native browser lazy loading. Does not require javascript and trasfers the optimization to the browser to handle. Javascript lazy loading as a fallback if native is not supported is also decent method. https://web.dev/native-lazy-loading/ @ChadP shared his native lazy loading script which is a fallback if native lazy loading is not available.