Jul 15, 2020 3:15 AM - edited Jul 16, 2020 11:46 AM
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:
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
Researching and Need Help
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.
Other posts talking about this:
2. Hubspot Chat
It's bad but I don't have numbers ready yet.
3. Mystery Files
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:
- ...scriptloader/8020292.js (802029s.hs-sites.com)
- ...159.../8020292.js (js.hs-analytics.com)
- /8020292.js (js.hs-banner.com)
- /bundles/project.js (static.hsappstatic.net)
- /collectedforms.js (js.hscollectedforms.net)
- sometimes common.js (no longer forced)
- sometimes layout.js (no longer forced)
4. Cache Policy
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?
---------------------------------------------
Reading List
Hubspot Guides
Blog Posts
Hubspot Courses
Good To Know
Last Updated: 7/16/20 (Published but in progress...)
Mar 11, 2021 1:12 PM
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?
Thanks,
Cheryl
Mar 11, 2021 9:22 PM
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.
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Jan 18, 2023 10:27 AM
Updating my prior answer. We now within the SEO recommendations tool include some site specific recommendations for how to improve your site speed.
https://knowledge.hubspot.com/seo/view-seo-recommendations-in-hubspot
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Sep 1, 2020 6:13 PM
Some awesome resources here, I am now following!
Don't forget loading jQuery from the footer, can help a lot in certain situations as well.
Nov 13, 2020 5:17 PM - edited Nov 13, 2020 5:20 PM
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.
Also an update on lazy loading images.
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Jan 18, 2023 10:33 AM
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.
I encourage if you're still using jQuery frequently to check out:
https://youmightnotneedjquery.com/
HubSpot functionality does not rely on jQuery to be loaded, and new sites created on HubSpot have jQuery disabled by default.
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Aug 26, 2020 7:52 AM - edited Aug 28, 2020 4:43 AM
Nice one @theAndreyK !
Love the idea.
So gonna share how I started to implement fragmented CSS for a theme (based on boilerplate).
{{ require_css(get_asset_url('../../css/main.css')) }}and replace it with
{% if load_css %}
{{ require_css(get_asset_url(load_css)) }}
{% else %}
{{ require_css(get_asset_url('../../css/main.css')) }}
{% endif %}
{% set load_css = "../../css/blog.css" %}
{% extends "./layouts/base.html" %}
Repeat for each template file.
{% include './general.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.
{% include './_blog.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.
More to come!
@jmclaren
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.
Thank you,
|
Sep 7, 2020 2:20 PM
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.
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Sep 19, 2020 1:12 PM - edited Sep 19, 2020 1:13 PM
Hey @jmclaren
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.
Thank you,
|
Sep 10, 2020 11:48 AM - edited Sep 10, 2020 11:57 AM
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.
Here's a preview into the code:
{% if module.image_field.src %}
{% set sizeAttrs = 'width="{{ module.image_field.width }}" height="{{ module.image_field.height }}"' %}
{% if module.image_field.size_type == 'auto' %}
{% set sizeAttrs = 'style="max-width: 100%; height: auto;"' %}
{% elif module.image_field.size_type == 'auto_custom_max' %}
{% set sizeAttrs = 'width="100%" height="auto" style="max-width: {{ module.image_field.max_width }}px; max-height: {{ module.image_field.max_height }}px"' %}
{% endif %}
<img src="{{ module.image_field.src }}" alt="{{ module.image_field.alt }}" {{ sizeAttrs }} loading="lazy">
{% endif %}
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!
Jul 16, 2020 9:47 AM - edited Jul 16, 2020 9:49 AM
export default () => { let lazyImages = [].slice.call(document.querySelectorAll("img.lazy")) if (lazyImages.length < 1) return // If the browser does NOT support IntersectionObserver if ( !('IntersectionObserver' in window) ) { lazyImages.forEach(lazyImage => updateSrc(lazyImage) ) return } let lazyImageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { let lazyImage = entry.target updateSrc(lazyImage) lazyImageObserver.unobserve(lazyImage) } }) }, {rootMargin: '0px 0px 200px 0px'}) // load 200px in advance lazyImages.forEach(lazyImage => { lazyImageObserver.observe(lazyImage) }) } function updateSrc(lazyImage) { if (lazyImage.dataset.src) { lazyImage.src=lazyImage.dataset.src } if (lazyImage.dataset.srcset) { lazyImage.srcset = lazyImage.dataset.srcset } if (lazyImage.dataset.sizes) { lazyImage.sizes = lazyImage.dataset.sizes } lazyImage.classList.add('loaded') }
Simple snippet for adding lazyload to your images, can be translated for pictures and video really simple
Jul 16, 2020 11:21 AM
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, 2020 2:13 AM - edited Jul 17, 2020 2:15 AM
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.
Jul 20, 2020 11:18 AM - edited Jul 20, 2020 11:19 AM
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.
If my reply answered your question, please mark it as a solution, to make it easier for others to find.
Jul 16, 2020 5:07 AM
Thank you for sharing this @theAndreyK! I think is really useful!
![]() | ¿Sabías que la Comunidad está disponible en Español? ¡Participa hoy en conversaciones en el idioma de tu preferencia,cambiando el idioma en tus configuraciones! Did you know that the Community is available in other languages? Join regional conversations by changing your language settings ! |
Jul 15, 2020 6:21 PM
CTA's have been bogging things down. a few customers with 3-4 cta's were making many unnecessary calls