<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eco Web Hosting</title>
    <description>The latest articles on DEV Community by Eco Web Hosting (@ecowebhostinguk).</description>
    <link>https://dev.to/ecowebhostinguk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F312927%2F4bd8b190-97c2-4ad3-b27c-7022b01e7cbd.png</url>
      <title>DEV Community: Eco Web Hosting</title>
      <link>https://dev.to/ecowebhostinguk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ecowebhostinguk"/>
    <language>en</language>
    <item>
      <title>Seven Easy Ways to Protect Your WordPress Site</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Fri, 17 Apr 2020 13:26:11 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/seven-easy-ways-to-protect-your-wordpress-site-2b1g</link>
      <guid>https://dev.to/ecowebhostinguk/seven-easy-ways-to-protect-your-wordpress-site-2b1g</guid>
      <description>&lt;p&gt;WordPress is the most popular content management systems on the Internet. If you’re looking at any website, there’s &lt;a href="https://w3techs.com/technologies/details/cm-wordpress"&gt;a 35% chance that it’s built on WordPress&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And because it’s so popular, it’s also an easy target for hackers. If there’s a way to get in, you can be sure that it’s already been exploited.&lt;/p&gt;

&lt;p&gt;So you need to make sure that your WordPress setup is as airtight as possible.  Here are seven tips to help you keep your WordPress secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update everything
&lt;/h3&gt;

&lt;p&gt;Whenever there’s an update, update. Get into the habit, no matter how small the updates are. Update your plugins. Update your themes. Update the software. Keep everything updated.&lt;/p&gt;

&lt;p&gt;These aren’t just vanity updates to make a developer feel better about themselves — most of the time they’re fixing security issues. Or they’re fixing bugs. You want a smooth-running WordPress installation? You update.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check your passwords and change them if necessary
&lt;/h3&gt;

&lt;p&gt;Are you using the same password across multiple sites? Or do you think you’re being clever and are changing the number at the end? You don’t even need to check &lt;a href="https://haveibeenpwned.com/"&gt;HaveIBeenPwned&lt;/a&gt; — you know that password’s been cracked somewhere.&lt;/p&gt;

&lt;p&gt;You need to make sure you have a unique password for your WordPress administrative account on your site. Luckily, WordPress makes it easy to generate one, but then it’s a matter of remembering it. Look into password safes like &lt;a href="https://keepass.info/"&gt;KeePass&lt;/a&gt;, &lt;a href="https://1password.com/"&gt;1Password&lt;/a&gt;, or &lt;a href="https://lastpass.com/"&gt;LastPass&lt;/a&gt; and make sure you keep that safe and secure as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review who has administrative privileges
&lt;/h3&gt;

&lt;p&gt;Did you give a developer admin rights on your WordPress install to fix something? What about people who have left the company? Do you even have an account with an “Admin” login?&lt;/p&gt;

&lt;p&gt;All of these are easy ways for people to get into your site. Go through your list of users and if there’s anyone on there who shouldn’t have rights to your site, set them to “No role for this site”.  That means that even if they log in, they can’t do anything on the site, and, if you’re running a blog with individual authors, it’ll still keep them listed as the author of articles.&lt;/p&gt;

&lt;p&gt;And if you’ve actually made an administrative account with the login of “admin”, please change it. You’ve pretty much just left your front door open there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make sure you’re using legitimate plugins and themes
&lt;/h3&gt;

&lt;p&gt;Cracked versions of plugins and themes just lead to more problems, not just because you’re pirating software from an already fragile industry, but you’re also opening up your site to anything and everything. If you can’t afford that particular plugin or theme, look at the free alternatives — often, you’ll find something that works even better than the paid version.&lt;/p&gt;

&lt;p&gt;And always make sure your plugins or themes are available on WordPress.org — if the company has vetted them, you can be sure they’re safer than the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up two-factor authentication for your site
&lt;/h3&gt;

&lt;p&gt;Two-factor authentication is where after you enter in your password, you then enter in another code provided by another system, whether it’s an authenticator app on your phone, a key fob, an email sent to your main account, or a fingerprint ID scanner.&lt;/p&gt;

&lt;p&gt;Two-factor authentication makes it more difficult for people to get in using your account. If you want to make sure your WordPress site is secure, it’s a great way to add in an extra bit of security. There are several plugins you can use, as seen in WordPress.org’s &lt;a href="https://wordpress.org/support/article/two-step-authentication/"&gt;Two Step Authentication article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Take regular backups
&lt;/h3&gt;

&lt;p&gt;No matter what you do, you still run the risk of being hacked. That’s where regular backups come in — an easy way to restore your site back to its original glory. Our Managed WordPress packages come with daily snapshot backups, or you can purchase snapshot backups for our Web Hosting packages separately. You can also manually back up and restore your site and database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep aware of what’s happening
&lt;/h3&gt;

&lt;p&gt;Keeping everything updated is a good start, but keeping informed of what’s happening in the WordPress world is also immensely helpful. Wordfence, a WordPress security plugin, has &lt;a href="https://www.wordfence.com/blog/"&gt;a detailed blog&lt;/a&gt; where they write up vulnerabilities and patches that they have found. WordPress.org’s article on &lt;a href="https://wordpress.org/support/article/hardening-wordpress/"&gt;Hardening WordPress&lt;/a&gt; is also a great read, getting into much more detail than I can in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update your site — no, really
&lt;/h3&gt;

&lt;p&gt;Honestly, I can’t repeat this enough. So many of the hacked websites we see are because someone hasn’t updated their version of WordPress. Keep it updated, keep it safe.&lt;/p&gt;

&lt;p&gt;And if you’ve updated to a recent version, there’s now a fantastic feature on the dashboard — Site Health Status. With that, you can check the status of your site, see what needs to be fixed, and help make your WordPress site even better.&lt;/p&gt;

&lt;p&gt;I hope this helps you keep your site safe and running smoothly!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 15 April 2019 by Kate Bolin, our Marketing Director, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/seven-easy-ways-to-protect-your-wordpress-site/?utm_campaign=wpsdev&amp;amp;utm_source=dt-170420-wpsdev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-wpsblogpost"&gt;the Eco Web Hosting blog&lt;/a&gt;.&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/dtcqDxllycI"&gt;Jason Briscoe on Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
    </item>
    <item>
      <title>Our Guide to Remote Working</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Fri, 20 Mar 2020 09:38:13 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/our-guide-to-remote-working-3bof</link>
      <guid>https://dev.to/ecowebhostinguk/our-guide-to-remote-working-3bof</guid>
      <description>&lt;p&gt;With the ongoing pandemic, a lot more people are remote working. This is new for many of you, but it isn’t for us.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://www.ecowebhosting.co.uk/blog/four-challenges-of-working-from-home-and-how-to-overcome-them/?utm_campaign=wfhdev&amp;amp;utm_source=dt-200320-wfhdev&amp;amp;utm_medium=social&amp;amp;utm_content=b-t-wfhblogpost"&gt;the very beginning&lt;/a&gt;, we’ve been a remote-working company. And while we might have occasionally worked in coworking spaces or gone over to coworkers’ houses, self-quarantining isn’t that much of a change for us – we’re already working at home.&lt;/p&gt;

&lt;p&gt;So, since we’re old hands at this, we thought you’d like to know some of the tricks we use to keep working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wake up at your normal time
&lt;/h3&gt;

&lt;p&gt;If you’re used to waking up early for your commute, stick to that schedule. Sleeping in might seem to be one of the perks of working at home, but if it’s only temporary, you’ll end up regretting it when you have to go back to the office.&lt;/p&gt;

&lt;p&gt;Plus, it gives you a chance to break up your day into Home Time and Work Time. Take a shower, change into new clothes, enjoy a nice breakfast, get into the office mindset, and you’ll be ready to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set aside your own place to work
&lt;/h3&gt;

&lt;p&gt;If you have a home office, that’s great – it’s a great way to separate out your work life and your home life, and you just need to make sure you’re at your desk at the start of the day. But if you don’t have the space for a home office, make sure you work in a different location than you normally sit when you’re lounging around. A different spot on the couch, the dining room table, whatever. This seat is for Serious Work, the other is for Fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  Take breaks, and set alarms if you have to
&lt;/h3&gt;

&lt;p&gt;You might get into the zone, being ultra-productive since you’re not getting interrupted by coworkers, but you still need to take breaks and you still need to stop at the end of the day. Set alarms if you have to – I personally really like &lt;a href="http://www.workrave.org/"&gt;Workrave&lt;/a&gt;, which makes sure you look away from your screen, stand up on a regular basis, and take breaks. And at the end of the day, close down your computer, and set it aside.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t worry about getting sidetracked
&lt;/h3&gt;

&lt;p&gt;It’s going to happen. It happens in the office too, and think about how much time that takes up. Do you fret about time spent at the coffee maker? That conversation you have with a coworker about who would win in a fight – astronauts versus cavemen?&lt;/p&gt;

&lt;p&gt;A lot of the time, you’re spending more time worrying about getting sidetracked than you would spend actually being sidetracked. Spend that five minutes looking up the most haunted prison in the USA, go “Oh, that is interesting,” then move back on to your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wear what you like, but wear something different
&lt;/h3&gt;

&lt;p&gt;Working from home also seems to be the perfect opportunity to spend all day in your pyjamas, being ultra-comfy and never wearing trousers again.&lt;/p&gt;

&lt;p&gt;But that just means it’s even more difficult to split between Work Time and Fun Time. So while you don’t have to put on the full office wear, putting on fresh clothes in the morning will make it clearer that you’re getting ready for work. I might be wearing leggings instead of jeans, but I’m still getting dressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make sure you eat well
&lt;/h3&gt;

&lt;p&gt;Just because you’re sitting at home and can eat nothing but chocolate biscuits doesn’t mean you &lt;em&gt;should&lt;/em&gt;. Make sure you have good healthy food with plenty of variety so you don’t get bored. Make sure there are plenty of vegetables and fruit as well. I like to make ramen because it looks fancy, but it really easy to prepare.&lt;/p&gt;

&lt;p&gt;Seriously, just put instant ramen, chicken pieces, a poached egg, and spring onion in a bowl. You spend more time waiting for the noodles to soften than you do preparing the rest.&lt;/p&gt;

&lt;p&gt;And watch out for getting food delivered – you might think it’s a great way to enjoy treats, but those delivery costs will start to rack up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t get distracted by housework
&lt;/h3&gt;

&lt;p&gt;During work hours, you are there to WORK. Getting distracted by how the carpet needs hoovering or those dishes in the sink or tidying up that coffee table will just eat up too much time that you’re supposed to be working.&lt;/p&gt;

&lt;p&gt;That doesn’t mean you can’t do any chores – I regularly pop in a load of laundry while preparing breakfast, then hang it up to dry during my lunch break – but if you think that doing one thing is going to start you cleaning the entire house, take a breath, and let it go.&lt;/p&gt;

&lt;h3&gt;
  
  
  And don’t forget to have fun!
&lt;/h3&gt;

&lt;p&gt;Listen to that cheesy pop on high volume. Sing along. Talk to yourself. Wear a wacky hat. Do push-ups against the wall. Dye your hair. Yeah, you’re working, but you’re also at home, and who’s going to stop you?&lt;/p&gt;

&lt;p&gt;I hope these tips help, and that you’ll have a great and productive working-from-home life!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 18 March 2019 by Kate Bolin, our Marketing Director, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/our-guide-to-remote-working/?utm_campaign=wfhdev&amp;amp;utm_source=dt-200320-wfhdev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-wfhblogpost"&gt;the Eco Web Hosting blog.&lt;/a&gt;&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/GaBDdA63GcQ"&gt;Roberto Nickson on Unsplash.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>workingfromhome</category>
      <category>remoteworking</category>
      <category>work</category>
      <category>worklifebalance</category>
    </item>
    <item>
      <title>Five WordPress Tools for Developers</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Fri, 21 Feb 2020 16:01:49 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/five-wordpress-tools-for-developers-2la8</link>
      <guid>https://dev.to/ecowebhostinguk/five-wordpress-tools-for-developers-2la8</guid>
      <description>&lt;p&gt;WordPress is popular as a CMS, and not only are designers being asked to work with it, developers are as well. And while, as a content management system, WordPress does it seem like it would make it easier, the challenges of customising WordPress to meet a site’s needs can be difficult.&lt;/p&gt;

&lt;p&gt;Here are five tools we’ve found that should be able to help you with your WordPress development, whether you’re building an entirely new site, working with an existing one, or just trying to add more features on your own site.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/user-switching/"&gt;User Switching&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When you’re building a plugin or a site, you might want different features for different users, and having to constantly repeat the process of logging out and logging in under a different user can take up a lot of time.&lt;/p&gt;

&lt;p&gt;User Switching lets you instantly switch between user accounts, letting you check features and permissions for all your different user types.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/wp-reset/"&gt;WP Reset&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Need to wipe everything and start again? WP Reset resets your site’s database to the default values, removing all customisations and content and leaving you with a mostly clean and empty site.&lt;/p&gt;

&lt;p&gt;It has a built-in snapshot feature that saves your existing site before you delete everything. However, you’ll have to remember to create that snapshot because once you hit that button, it’s gone. And it won’t delete media files, site title, search engine visibility settings, or plugins. But when you’re looking at thousands of posts, it might be an easy way to clear everything out.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/aryo-activity-log/"&gt;Activity Log&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you have several people working on your site remotely, manage a team of developers, or just have a tendency to forget what you did the night before, Activity Log is a great little plugin that gives you plenty of information about what’s happening on your site.&lt;/p&gt;

&lt;p&gt;Whether it’s seeing who’s activated a plugin, double-checking when you changed that one setting, or making fun of that colleague who took 20 minutes remembering their password, Activity Log gives you the details you need when you’re working with WordPress.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/wp-toolbelt/"&gt;Toolbelt&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An all-in-one plugin that provides day-to-day functions you need on your site, packaged together with a focus on privacy and speed. Whether you want to lazy load images, add in a quick cookie banner that points to your privacy policy, or tweak your admin panel to make it a little more usable, Toolbelt has plenty of options available to make your life easier, and includes the impact on loading times as well.&lt;/p&gt;

&lt;p&gt;It’s a fairly new plugin, so we’re hoping for even more features in the future. And we’re using it on our blog, so let us know what you think!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/fakerpress/"&gt;FakerPress&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You need to populate your test sites with content, it’s a pain to keep doing it by hand, so why not use FakerPress to give you everything you need?&lt;/p&gt;

&lt;p&gt;Get posts, images, users, tags, comments, and more, and then quickly and easily get rid of the content after you finish. It’s great for when you’re checking your blocks and making sure everything’s working just as it should be.&lt;/p&gt;

&lt;p&gt;With these tools, I hope you’re able to make short work of some of the trickier problems WordPress developers face. Good luck, and have fun!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 4 December 2019 by Kate Bolin, our Marketing Director, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/five-wordpress-tools-for-developers/?utm_campaign=wpdevdev&amp;amp;utm_source=dt-210220-wpdevdev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-wpdevblogpost"&gt;the Eco Web Hosting blog&lt;/a&gt;.&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/azft6PuI3Ug"&gt;Joshua Aragon on Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>wordpress</category>
      <category>development</category>
      <category>plugins</category>
    </item>
    <item>
      <title>How We Built Our Own Prerenderer (And Why) - Part 2: How</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Wed, 12 Feb 2020 14:05:35 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/part-2-how-144p</link>
      <guid>https://dev.to/ecowebhostinguk/part-2-how-144p</guid>
      <description>&lt;h2&gt;
  
  
  How hard could it be?
&lt;/h2&gt;

&lt;p&gt;Though I generally always feel daunted when I look at other people’s code, after looking through the prerenderers I found on GitHub, the basic premise seemed fairly simple. Essentially, a local server instance would be started up, with the docroot of the site being set as such on that instance. Then, a browser client/DOM library would be loaded up, navigate to some provided routes and request them from the server instance. Finally, the markup from the responses to those requests would be saved to a file.&lt;/p&gt;

&lt;p&gt;As the build process for Vue is a Node one, remaining within the Node environment seemed sensible. Node already has well-established server capabilities, further extended by Express. So that’s our server. Many of the existing prerender libraries used Puppeteer, an API library that allows communication with a(n optionally) headless Chromium browser instance. Puppeteer has methods that allow you to navigate to a page, wait for specific elements to load before continuing the execution, and save the generated HTML. So that was the client sorted.&lt;/p&gt;

&lt;p&gt;And writing the generated HTML back to the file system could be done using Node’s existing fs library, which is file creation taken care of.&lt;/p&gt;

&lt;p&gt;Basically, the prerenderer needed to pull together the functionality of three different sets of libraries – a server, a client and a filesystem lib. The rest would just be configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our implementation: A tale of three classes
&lt;/h2&gt;

&lt;p&gt;For those of you familiar with the British indie music scene in 2006, let’s &lt;em&gt;Skip to the End&lt;/em&gt;. For everyone else, let’s preview the result and then look through how we got there.&lt;/p&gt;

&lt;p&gt;What was a hypothetical project became an actual thing that Eco Web Hosting now uses. Here are the lines that call it in our build script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SpaPrerenderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;spa-prerenderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prerenderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SpaPrerenderer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/../ewh-main-site/public_html/vue/dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vps&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/web-hosting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/domain-names&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/cookie-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/fair-usage-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/privacy-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/security-and-data-processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/terms-and-conditions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/agreements/third-party-processors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/reseller-hosting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/managed-wordpress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/../ewh-main-site/public_html/vue/dist/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;supressOutput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;reportPageErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;prerenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are three statements over 28 lines. Statement #1 includes the prerenderer for use.&lt;/p&gt;

&lt;p&gt;Statement #2 instantiates it with a configuration that provides an input directory, the routes to prerender, an output directory, the ID of the element the prerenderer should wait for the generation of before saving markup, whether or not to use HTTPS on the local server instance the site is served to the renderer from, whether or not output should be suppressed and whether or not errors from the input pages are reported.&lt;/p&gt;

&lt;p&gt;Statement #3 triggers the prerendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  What each class does
&lt;/h3&gt;

&lt;p&gt;Since the prerenderer was initially planned for use only by ourselves, we knew that it would be used in an up-to-date Node.js environment, meaning I could use nice, clean post-ES6 syntax. Of course, I could have used Babel with that to transpile down for compatibility with older versions, but it seemed like needless bulk when there are few circumstances this would be run in a pre-ES6 environment.&lt;/p&gt;

&lt;p&gt;One post-ES6 syntax feature I used was the JavaScript class syntax, which provides a cleaner way of writing JavaScript’s prototype-based inheritance.&lt;/p&gt;

&lt;p&gt;Here’s the difference that little bit of syntactic sugar can make to legibility:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aTQLfCg0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aTQLfCg0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-7.png" alt="Screenshot of Babel showing the JavaScript class syntax making it cleaner."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I divided the project between three classes. &lt;code&gt;Options&lt;/code&gt; (for validating prerenderer options), &lt;code&gt;Server&lt;/code&gt; (the server) and &lt;code&gt;Prerenderer&lt;/code&gt; (the client, containing the calls to Puppeteer).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Prerenderer&lt;/code&gt; would be the only class instantiated externally. &lt;code&gt;Options&lt;/code&gt; and &lt;code&gt;Server&lt;/code&gt; exist for &lt;code&gt;Prerenderer&lt;/code&gt; to work with. The instance of &lt;code&gt;Prerenderer&lt;/code&gt; would be constructed with the desired options, those options validated by a method of &lt;code&gt;Options&lt;/code&gt; and an instance of &lt;code&gt;Server&lt;/code&gt; started and stored as an instance variable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Options&lt;/code&gt; just contains a static method to validate the object passed to it and throws an &lt;code&gt;Error&lt;/code&gt; or &lt;code&gt;TypeError&lt;/code&gt; in the event of an invalid value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;supressOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;reportPageErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;staticDir&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staticDir must be explicitly set.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAbsolute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staticDir must be an absolute path.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;routes must be an array.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;useHttps&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useHttps must be a boolean value.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;supressOutput must be a boolean value.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;reportPageErrors&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reportPageErrors must be a boolean value.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Server&lt;/code&gt; would be constructed with two of the options passed in from &lt;code&gt;Prerenderer&lt;/code&gt; — &lt;code&gt;staticDir&lt;/code&gt;, which is essentially the document root for the site, and &lt;code&gt;useHttps&lt;/code&gt;, which is a boolean flag that dictates whether an http or https server is started. Both are passed in the server options object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_expressServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_nativeServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Server&lt;/code&gt; essentially wraps an Express instance and augments it with a method to dynamically create a self-signed SSL certificate for it if an https server is required. The server is then started using the &lt;code&gt;init()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_expressServer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;dotfiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;}));&lt;/span&gt;

   &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tlsParts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateSelfSignedCert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_nativeServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useHttps&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
       &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tlsParts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="na"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tlsParts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;
           &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_expressServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_nativeServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is how the server is then instantiated and initialised by the prerenderer constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="nx"&gt;staticDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="nx"&gt;outputDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;waitForElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;useHttps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;reportPageErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;supressOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;reportPageErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staticDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useHttps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;supressOutput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reportPageErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reportPageErrors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;portfinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serverOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;useHttps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;staticDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once a &lt;code&gt;Prerenderer&lt;/code&gt; object has been successfully constructed, it should have a running server as a &lt;code&gt;Server&lt;/code&gt; object instance variable. Prerendering can then begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Once the prerenderer is constructed
&lt;/h3&gt;

&lt;p&gt;I divided the prerenderer’s functionality into three further instance methods. These are all called from the asynchronous &lt;code&gt;init()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startBrowser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
       &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetBase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
       &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetBase&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/index.html`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{blue {bold Prerendering }&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getMarkup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{green {bold Prerendered} &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;} {bold.blue Saving…}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supressOutput&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{green.inverse {bold Saved} to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}));&lt;/span&gt;

   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The method first calls &lt;code&gt;startBrowser()&lt;/code&gt;, which starts a Puppeteer instance and assigns it to the instance variable browser.&lt;/p&gt;

&lt;p&gt;Next, &lt;code&gt;await Promise.all()&lt;/code&gt; is used to run an instance of &lt;code&gt;getMarkup()&lt;/code&gt; for each route in parallel, and then calls &lt;code&gt;saveFile()&lt;/code&gt; after the markup for each respective route has been retrieved. Only when each of those asynchronous fetch/save routines is successful (resolves) or fails (is rejected) will &lt;code&gt;init()&lt;/code&gt; move onto the next statements, which close the Puppeteer instance followed by the server.&lt;/p&gt;

&lt;p&gt;Here’s how the three methods that &lt;code&gt;init()&lt;/code&gt; calls work in a little more detail.&lt;/p&gt;

&lt;h4&gt;
  
  
  Starting Puppeteer with &lt;code&gt;startBrowser()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;startBrowser()&lt;/code&gt; fires up an instance of Puppeteer. Since Puppeteer will be accessing only our local Express server (which may have a self-signed certificate), the &lt;code&gt;--ignore-certificate-errors&lt;/code&gt; is included to prevent Puppeteer complaining that the certificate isn’t signed by a recognised CA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;startBrowser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;ignoreHTTPSErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--ignore-certificate-errors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Getting the routes’ markup with &lt;code&gt;getMarkup()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;getMarkup()&lt;/code&gt; opens a new tab in the Puppeteer browser instance &lt;code&gt;startBrowser()&lt;/code&gt; created for the route it is getting the markup for and then navigates to it.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;reportPageErrors()&lt;/code&gt; has been set to true, it outputs any errors from the source script if they exist.  If the &lt;code&gt;waitForElement&lt;/code&gt; option has been set, it waits up to 60 seconds for that HTML element to be generated.&lt;/p&gt;

&lt;p&gt;Assuming no errors have been thrown, it then returns the markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getMarkup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useHttps&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`http&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reportPageErrors&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pageerror&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorHint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForElement&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;
         &lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`This may prevent the element {bold &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
         &lt;span class="s2"&gt;` from rendering, causing a timeout.`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{bgRed.white {bold Error from the page being rendered:}}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{red &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chalk&lt;/span&gt;&lt;span class="s2"&gt;`{red &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;errorHint&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForElement&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Saving the routes’ markup using &lt;code&gt;saveFile()&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;saveFile()&lt;/code&gt; takes content and saves it to a file path using Node’s &lt;code&gt;fs&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;saveFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathComponents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;pathComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dirPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirPath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dirPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lstatSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isFile&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unlinkSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wonderful. In the context of the &lt;code&gt;init()&lt;/code&gt; method that calls it, the file path will be whatever the &lt;code&gt;outputDir&lt;/code&gt; value is, suffixed with ‘index.html’.&lt;/p&gt;

&lt;h3&gt;
  
  
  But will it blend?
&lt;/h3&gt;

&lt;p&gt;To make sure my initial enthusiasm hadn’t been pure folly, I thought perhaps it might be wise to write some tests.&lt;/p&gt;

&lt;p&gt;I’m a great fan of code that reads like human being language, and for this reason, I used the Mocha test suite with the Chai assertion library, which allowed me to write tests like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prerender output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should save index.html to the correct path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;httpPrerenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/index.html`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should match prerender input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/index.html`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;section id="dynamic"&amp;gt;&amp;lt;h2&amp;gt;Dynamic bit&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Actual output does not match expected output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The results were promising (though the tests were a bit slow due to the inherent overhead of DOM rendering in a headless browser via Puppeteer):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w_rPi-c0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_rPi-c0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-8.png" alt="Screenshot of the test results."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hot diggity dog. And the http:// URL in the prerendered source?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JH-7dcwa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JH-7dcwa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-9.png" alt="Screenshot of the source code for the file, showing the https in the proper place."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gone. Success!&lt;/p&gt;

&lt;h2&gt;
  
  
  Open sourcing the results
&lt;/h2&gt;

&lt;p&gt;Writing a new prerenderer for the purpose of banishing a mixed content error on our own site is all well and good, but it felt a little selfish to keep it to ourselves. For this reason, &lt;a href="https://github.com/ecowebhosting/spa-prerenderer"&gt;I’ve published it on Eco Web Hosting’s brand new GitHub profile&lt;/a&gt; and as &lt;a href="https://www.npmjs.com/package/spa-prerenderer"&gt;an npm package&lt;/a&gt; for the world to use (and scrutinise).&lt;/p&gt;

&lt;p&gt;This is my (and Eco Web Hosting’s) first foray into publishing open source packages so please, install, test, fork, adapt, report upon! Contributions and feedback are more than welcome.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Note:&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Looking back, I could have submitted a pull request in the original prerenderer plugin to add the missing https toggle feature, though before embarking on this project, the source code made little sense to me. Ironically, it’s only having completed my own version that I now see my efforts might have been better directed at contributing to the original. Hindsight is, as they say, 20/20.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 12 February 2020 by Andy Dunn, our Senior Technical Lead, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/how-we-built-our-own-prerenderer-part-2-how/?utm_campaign=pre2dev&amp;amp;utm_source=dt-120220-pre2dev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-pre2blogpost"&gt;the Eco Web Hosting blog.&lt;/a&gt;&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/Agx5_TLsIf4"&gt;Fatos Bytyqi on Unsplash.&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>vue</category>
      <category>prerenderer</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How We Built Our Own Prerenderer (And Why) - Part 1: Why</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Wed, 12 Feb 2020 14:05:28 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/part-1-why-2821</link>
      <guid>https://dev.to/ecowebhostinguk/part-1-why-2821</guid>
      <description>&lt;h2&gt;
  
  
  Context: We built a Vue site
&lt;/h2&gt;

&lt;p&gt;Recently, we built ourselves a lovely new site using Vue.js. We wanted to be able to easily build reusable components and generally provide a speedier experience for users navigating the site.&lt;/p&gt;

&lt;p&gt;As with almost any design decision, there were trade-offs. The main one was the kind we like to call &lt;em&gt;“find-a-solution-that-seems-easy-on-StackOverflow-and-then-spend-a-week-trying-to-iron-out-the-bits-that-don’t-work”&lt;/em&gt;. Namely, the site needed a prerenderer in order to be read by some search engines.&lt;/p&gt;

&lt;p&gt;When looking at the problem objectively, this was just a case of methodologically breaking any problems down and working through them, one-by-one. &lt;/p&gt;

&lt;p&gt;But really, it was more a case of sinking into a minor existential crisis, questioning my career choices and deciding whether I should, in fact, pack it all in, buy a trawler and spend the rest of my days getting lashed by briny mist in the North Sea.&lt;/p&gt;

&lt;p&gt;A cup of tea and a Hobnob later, I considered the possibility that I was being a little dramatic and got back to the drawing board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is prerendering?
&lt;/h2&gt;

&lt;p&gt;Web terminology sometimes feels deliberately ambiguous. Is prerendering something that happens before rendering, or rendering that happens before something else? What is being rendered? Markup? DOM nodes?&lt;/p&gt;

&lt;p&gt;When we talk about prerendering websites, we’re talking about generating the static page source that is served to the browser, which will build the Document Object Model (DOM), which is then painted to make the web page users see.&lt;/p&gt;

&lt;p&gt;If your website has just a few static HTML files, where no content changes when served, there is no prerendering to do. The pages are already prepared for service.&lt;/p&gt;

&lt;p&gt;So say you have an HTML file containing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Prerenderer test&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Prerenderer test&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"static"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Static bit&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Nothing dynamic here…&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A browser would render this HTML something like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zw6injyA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zw6injyA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-1.png" alt="Screenshot of the HTML rendering of the code above"&gt;&lt;/a&gt;&lt;br&gt;
Thrilling stuff.&lt;/p&gt;

&lt;p&gt;Then say you then add some JavaScript to add some elements to the page, so your file now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Prerenderer test&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Prerenderer test&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"static"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Static bit&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Nothing dynamic here…&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dynamic bit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`But here, everything is generated dynamically.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your page would render like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ENCy77Xq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ENCy77Xq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-2.png" alt="Screenshot of the HTML rendering of the code above, including the dynamically generated content"&gt;&lt;/a&gt;&lt;br&gt;
Ooo-ee. That’s the stuff I got into web development for. &lt;/p&gt;

&lt;p&gt;This is a pretty basic example. Single Page Application frameworks, like Vue.js, React.js and Angular, take dynamic rendering and do something much more useful with it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vue.js apps are dynamically rendered
&lt;/h3&gt;

&lt;p&gt;Our old website was a fairly traditional affair. You’d go to ecowebhosting.co.uk, a PHP page would be requested, assembled, and the fully-fledged markup would be returned.&lt;/p&gt;

&lt;p&gt;Our new site doesn’t do that. Instead, it serves a small HTML file that acts as a mounting point for other DOM nodes.&lt;/p&gt;

&lt;p&gt;It also contains JavaScript that has the entire remainder of the site served in that first request (save for static assets like images).&lt;/p&gt;

&lt;p&gt;When you navigate around the new site, bits of that JavaScript are being executed, updating and re-rendering the markup of the page in the browser. This is why it feels fairly speedy. The browser doesn’t need to send new requests for pages each time the URL changes, as it already holds most of the site locally.&lt;/p&gt;

&lt;p&gt;This means the source for each page looked the same. Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width,initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#577e5e"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/manifest.json"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/logo_192px.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/favicon.ico"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/0.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/1.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/10.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/11.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/12.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/13.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/14.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/15.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/16.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/17.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/18.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/19.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/2.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/20.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/21.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/3.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/4.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/5.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/6.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/7.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/8.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/9.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"prefetch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/app.js"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"script"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;
        We're sorry but the Eco Web Hosting site doesn't work 
        properly without JavaScript enabled. Please enable it to continue.
        &lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- built files will be auto injected --&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--JavaScript at end of body for optimized loading--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sideNav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.sidenav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sidenav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sideNav&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/app.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yet a browser’s Inspect tool would show the dynamically generated markup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MuYZMEvG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MuYZMEvG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/prerenderer-3.png" alt="Screenshot of a browser with the Eco Web Hosting website and the code inspector open, showing the content."&gt;&lt;/a&gt;&lt;br&gt;
All’s well that ends well, right? The browser runs the JavaScript, the JavaScript constructs the view and the user is shown that view. What’s the problem? Well…&lt;/p&gt;
&lt;h3&gt;
  
  
  Most search engines don’t run JavaScript
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://moz.com/blog/search-engines-ready-for-javascript-crawling"&gt;Moz.com did some research in 2017&lt;/a&gt; to see which search engines correctly index JavaScript, and found that just Google and Ask did. At the time of writing, this was the most recent evidence I could find. Bing does index synchronous JavaScript, but it does not wait for asynchronous JavaScript to finish loading.&lt;/p&gt;

&lt;p&gt;It’s tempting to discount users of other search engines, ‘cause everyone uses Google now anyway, right? And what kind of maniac uses Bing, anyway? Unfathomable though it is, it seems that people do actually use other search engines. &lt;a href="https://gs.statcounter.com/search-engine-market-share#monthly-201906-201912-bar"&gt;StatCounter reported&lt;/a&gt; that in the second half of last year, Google had 92.65% of the global search engine market share. 92.65% is a high number, but 100% it is not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i8KnbTS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i8KnbTS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-4.png" alt="Screenshot of StatCounter Global Stats bar chart on Search Engine Market Share"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Ask would appear to be included in “Other” in this graph, I’m going to round down the “not Google or Ask” share to an estimated 7%.&lt;/p&gt;

&lt;p&gt;That’s 7% of your potential customers who will never see your beautifully crafted new site, let alone convert into sales. So yeah. Looks like we can’t neglect other search engines. Not even Bing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two ways of indexing dynamic pages
&lt;/h2&gt;

&lt;p&gt;What’s the answer then? There are two common solutions to this problem. Both involve rendering the site before it is served from the server. One is Server-Side Rendering (SSR) and the other is prerendering.&lt;/p&gt;

&lt;p&gt;On server-side rendered sites, the HTML is rendered (you guessed it) on the server and sent back to the client. This general idea is much the same as a PHP site assembling the HTML to serve, only it’s the JavaScript that does it. But once the site has loaded to the browser once, further navigation changes are made client-side.&lt;/p&gt;

&lt;p&gt;SSR, therefore, allows a speedier first load, with search engines reading the requested content as if it were a static page. Dynamic data is readied in advance, so the site retains the reusability and snappier user experience that SPAs have after the first load completes.&lt;/p&gt;

&lt;p&gt;But it can be a bit of work to implement, and also be overkill if dynamic data doesn’t need to be prepared in advance for a particular route in a Single Page App.&lt;/p&gt;

&lt;p&gt;Prerendering, on the other hand,  generates a static HTML page for each route of an SPA when the app is initially built, rather than whenever that route is requested.&lt;/p&gt;

&lt;p&gt;This is easier to implement than SSR, and the static page is ready to be served whenever the page is requested, but it does also mean that there’s no ability to dynamically prepare markup in advance within the same route.&lt;/p&gt;

&lt;p&gt;Since we would only have varying content that didn’t need to be dynamically prepared in advance, prerendering was our answer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerendering one’s woes away
&lt;/h2&gt;

&lt;p&gt;To our collective delight, it seemed prerendering was a problem for which many solutions had already been provided. Not being fans of reinventing the wheel for the sake of it, we were happy to go along with what the Vue.js documentation recommended — &lt;a href="https://github.com/chrisvfritz/prerender-spa-plugin"&gt;the prerender-spa-plugin.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integrating it was to be a fairly simple task, in theory. It could be installed via npm and then configured via our Vue.js app’s Webpack configuration file. We just had to provide the site’s docroot directory and an array of the routes to prerender.&lt;/p&gt;

&lt;p&gt;We got the prerenderer working, and all was good until we noticed something we could not ignore.&lt;/p&gt;
&lt;h3&gt;
  
  
  Curse of the mixed content warning
&lt;/h3&gt;

&lt;p&gt;There was a blight in the browser console, and it was a mixed content warning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eOlo7ryN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOlo7ryN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-5.png" alt="Screenshot of the mixed content warning in the browser console."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yet the element in question loaded just fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JyHxRa8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JyHxRa8U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/02/prerenderer-6.png" alt="Screenshot of the Trustpilot widget that was causing the mixed content warning."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the inspector showed it loading over https, just like the rest of the site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; 
&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: relative; height: 240px; width: 100%; border-style: none; display: block; overflow: hidden;"&lt;/span&gt; &lt;span class="na"&gt;scrolling=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt; 
&lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Customer reviews powered by Trustpilot"&lt;/span&gt; 
&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://widget.trustpilot.com/trustboxes/54ad5defc6454f065c28af8b/index.html?templateId=54ad5defc6454f065c28af8b&amp;amp;amp;businessunitId=582d86750000ff000597a398#v-6df015a4=&amp;amp;amp;vD20690f8=&amp;amp;amp;tags=ewh-gc&amp;amp;amp;locale=en-GB&amp;amp;amp;styleHeight=240px&amp;amp;amp;styleWidth=100%25&amp;amp;amp;theme=light&amp;amp;amp;stars=5"&lt;/span&gt; 
&lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The source told another story though:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; 
&lt;span class="na"&gt;frameborder=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;scrolling=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Customer reviews powered by Trustpilot"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; 
&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://widget.trustpilot.com/trustboxes/54ad5defc6454f065c28af8b/index.html?templateId=54ad5defc6454f065c28af8b&amp;amp;amp;businessunitId=582d86750000ff000597a398#v-6df015a4=&amp;amp;amp;vD20690f8=&amp;amp;amp;tags=ewh-gc&amp;amp;amp;locale=en-GB&amp;amp;amp;styleHeight=240px&amp;amp;amp;styleWidth=100%25&amp;amp;amp;theme=light&amp;amp;amp;stars=5"&lt;/span&gt; 
&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"position: relative; height: 240px; width: 100%; border-style: none; display: block; overflow: hidden;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The prerendered markup’s source URL for the widget was http, but once all the scripts on the page executed, the DOM was ‘hydrated’ with the correct https:// source URL.&lt;/p&gt;

&lt;p&gt;Besides looking unprofessional to any console adventurers, as Chrome’s Lighthouse pointed out to us, it could incur an SEO penalty.&lt;/p&gt;

&lt;p&gt;It seems that TrustPilot’s Trustbox widget script itself created the iframe element with a source relative to the protocol the site was being served on and that the prerenderer served the site over https on a local server during the build process.&lt;/p&gt;

&lt;p&gt;To fix it, we had a few options, though some felt rather hacky (post-build search and replace), while others relied on the addition of an https-served prerender that there didn’t seem to be much appetite for from others on &lt;a href="https://github.com/JoshTheDerf/prerenderer/blob/34f9783ac0a3a806ce3ec52154bd950bd280afef/renderers/renderer-puppeteer/es6/renderer.js#L95"&gt;the original project.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I began to get ideas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next: Andy gets into the details of how he built our own prerenderer, and the issues he faced...&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 5 February 2020 by Andy Dunn, our Senior Technical Lead, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/how-we-built-our-own-prerenderer-part-1-why/?utm_campaign=pre1dev&amp;amp;utm_source=dt-120220-pre1dev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-pre1blogpost"&gt;the Eco Web Hosting blog.&lt;/a&gt;&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/Agx5_TLsIf4"&gt;Fatos Bytyqi on Unsplash.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>vue</category>
      <category>prerenderer</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Alt, title, and your images</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Fri, 24 Jan 2020 16:42:23 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/alt-title-and-your-images-3534</link>
      <guid>https://dev.to/ecowebhostinguk/alt-title-and-your-images-3534</guid>
      <description>&lt;p&gt;Most people like having images on their website. It helps make the site more interesting and gives the users something nice to look at. And if you’re running an e-commerce site, your images can make or break your business.&lt;/p&gt;

&lt;p&gt;And most people know that when you have an image, you need to include attributes for the image. There are enough articles out there that say that, as well as a wide range of accessibility and search engine optimisation checkers that point it out.&lt;/p&gt;

&lt;p&gt;But I’ve been noticing something recently that keeps coming up – people will include a title attribute, but not an alt attribute. Or have both saying the same thing.&lt;/p&gt;

&lt;p&gt;(Sometimes they’re called “alt tags” or “title tags” as well.)&lt;/p&gt;

&lt;p&gt;So I did some reading, dug into &lt;a href="https://html.spec.whatwg.org/"&gt;the HTML standard&lt;/a&gt;, and here are my recommendations for image attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the alt attribute?
&lt;/h3&gt;

&lt;p&gt;The alt attribute is text describing what the image is. If all your images suddenly disappear, the alt attribute can still tell your users what’s going on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="cat.jpg" alt="A small orange kitten walking through grass"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;They’re absolutely vital for screen readers and users who cannot download images. Without an alt attribute, it’s like the image isn’t there. Or, worse, it’s just the file name.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the title attribute?
&lt;/h3&gt;

&lt;p&gt;The title attribute is also text describing what something is, but it’s much more all-encompassing. You can have titles on links, abbreviations, fields, whatever.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="cat.jpg" alt="A small orange kitten walking through grass" title="Photo by AndriyKo Podilnyk on Unsplash"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;p&amp;gt;I used to write a lot of &amp;lt;abbr title="HyperText Markup Language"&amp;gt;HTML&amp;lt;/abbr&amp;gt;.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It usually shows up as text when you hover over something, like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Og_CR6NI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/kitten-title.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Og_CR6NI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.ecowebhosting.co.uk/blog/wp-content/uploads/2020/01/kitten-title.png" alt="Screenshot of an image of a kitten with the title attribute appearing as a tooltip"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does this matter?
&lt;/h3&gt;

&lt;p&gt;Titles are great to have. You can do a lot with titles and it gives a lot of further information that isn’t absolutely necessary to have in the main body of the text.&lt;/p&gt;

&lt;p&gt;Alts, however, are vital. Not just for accessibility reasons, but for search engine optimisation as well.&lt;/p&gt;

&lt;p&gt;Search engines read alt attributes, not title attributes. They count towards your keyword strategy, your content strategy, and your ranking overall.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if the image is just decorative and doesn’t mean anything?
&lt;/h3&gt;

&lt;p&gt;Decorative images are great for the look of your site, but if you put an alt attribute in all of them, you’d run of the risk of your site sounding like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drawing of a cat Cat treats&lt;br&gt;
Drawing of a sale sign Sale now on&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can use empty alt attributes to make the images essentially disappear.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="cat-icon.svg" alt=""&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I make sure I have alt attributes?
&lt;/h3&gt;

&lt;p&gt;If you’re writing your pages yourself, you just need to remember. Every image has an alt attribute. It'll be hard work at first, but you'll get into the swing of things.&lt;/p&gt;

&lt;p&gt;If you’re getting your pages from another source, you can run browser auditing tools – Firefox has &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Accessibility_inspector"&gt;the Accessibility Inspector&lt;/a&gt;, and Chrome has &lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;Lighthouse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run either against your site, and it will tell you which images are missing alt attributes. Then it’s just a matter of writing up descriptions of your images and making sure they’re put in.&lt;/p&gt;

&lt;h3&gt;
  
  
  How much detail do I need to go into?
&lt;/h3&gt;

&lt;p&gt;It’s up to you and what the image is. For a lot of them, a simple explanation will do it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="kitten.jpg" alt="A kitten sitting on a sofa"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you’re running an e-commerce site, you might want to put in a bit more information, such as brand and product details.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="123456.jpg" alt="150g pack of Kitten Treats cat treats in Tuna flavour"&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What you don’t want to do, however, is keyword stuff your alt attributes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;img src="kitten.jpg" alt="persian cat kitten ragdoll tabby main coon feline kitty" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Remember – this isn’t just for search engines, this is for screen readers as well. How does the text flow when you read aloud the alt attribute? Does it make sense?&lt;/p&gt;

&lt;p&gt;I hope this helps and I hope your alts are all perfect!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 22 January 2020 by Kate Bolin, our Marketing Director, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/alt-title-and-your-images/?utm_campaign=altdev&amp;amp;utm_source=dt-240120-altdev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-altblogpost"&gt;the Eco Web Hosting blog.&lt;/a&gt;&lt;br&gt;
Cover image by &lt;a href="https://unsplash.com/photos/o_mdGdfyLHg"&gt;贝莉儿 DANIST on Unsplash.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>images</category>
      <category>html</category>
      <category>altattribute</category>
      <category>titleattribute</category>
    </item>
    <item>
      <title>Four challenges of working from home, and how to overcome them</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Fri, 17 Jan 2020 14:53:24 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/four-challenges-of-working-from-home-and-how-to-overcome-them-4fpj</link>
      <guid>https://dev.to/ecowebhostinguk/four-challenges-of-working-from-home-and-how-to-overcome-them-4fpj</guid>
      <description>&lt;h3&gt;
  
  
  So, you’re a home-worker
&lt;/h3&gt;

&lt;p&gt;When I began working at &lt;a href="https://www.ecowebhosting.co.uk/?utm_campaign=homedev&amp;amp;utm_source=li-170120-homedev&amp;amp;utm_medium=social&amp;amp;utm_content=b-t-home"&gt;Eco Web Hosting&lt;/a&gt; in February 2015, my now &lt;a href="https://www.udemy.com/user/codestars/"&gt;Udemy-famous boss Rob&lt;/a&gt; offered the job as a work-from-home position, (with the condition that I relocate). Five days later, I found myself sat on an East Coast Main Line train on a pile of my bags, with my bike propped against my thigh as I swapped Nottingham for Cambridge.&lt;/p&gt;

&lt;p&gt;Working remotely has plenty of perks. A &lt;a href="https://www.gsb.stanford.edu/faculty-research/publications/does-working-home-work-evidence-chinese-experiment"&gt;Stanford University 2003 study by Nicholas Bloom and others&lt;/a&gt; found homeworkers were more productive than office workers (good for business), and their job satisfaction higher (good for employees). For Rob, too, he could spend more time with his family. For me, I could go and visit family and friends during the week, so long as they had a quiet space and an internet connection.&lt;/p&gt;

&lt;p&gt;Remote work, however, is not without its challenges. The long-term benefits of home-working have been supported by studies such as Nicholas Bloom’s, whereas the rest of my advice has no basis more reliable than my own meandering experience (and some online research, which doesn’t allow me to plagiarise Baz Luhrmann’s turn of Mary Schmich’s phrase so readily).&lt;/p&gt;

&lt;p&gt;I will dispense this advice now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Know yourself: Are you an integrator or a segmentor?
&lt;/h3&gt;

&lt;p&gt;Home-workers add work into an environment that is otherwise reserved for relaxing, resting and socialising. When I first started home-working, I would wake up late, knowing that there was some flexibility for me to make the time up later, and then work doubly as late into the evening to compensate.&lt;/p&gt;

&lt;p&gt;Suffice to say this was not healthy, and my working days ended up spanning from 10:00 am – 11:00 pm, and sometimes beyond. I was exhausted, depressed, and after some time, unproductive.&lt;/p&gt;

&lt;p&gt;I had bought into an idea that work and leisure would mingle effortlessly with one another purely by virtue of working at home, only to discover that I had allowed work to completely take over. The idea I had bought into is one that works for many, the integrators. I however am a segmentor.&lt;/p&gt;

&lt;p&gt;Integrators like to manage the demands of work and home life by blending them together. They may need to do the shopping first, then write a marketing e-mail. They’ll make dinner, then write a bug-fix and answer some e-mails.&lt;/p&gt;

&lt;p&gt;Segmentors on the other hand would rather compartmentalise work and leisure into distinct parts of the day. For them, leisure or other responsibilities during work time is disruptive, and work during leisure time is stressful and draining.&lt;/p&gt;

&lt;p&gt;As my first year at Eco Web Hosting went on (also helped by the addition of new staff to ease the workload), I was able to develop better self-discipline during the day, and be more frank with Rob and the team about expectations outside of working hours.&lt;/p&gt;

&lt;p&gt;The result? I became less-stressed out, and more productive. It’s not necessarily a silver-bullet, but knowing what to expect from your day lets you concentrate on making the best of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #1:&lt;/strong&gt; Understand your working preferences and talk with your team to agree on boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accommodate yourself: Find flow
&lt;/h3&gt;

&lt;p&gt;Flow is that illusive state where you lose focus on everything but what you’re doing. Whether it’s running a marathon, watching a film or writing a WordPress theme, you’re completely immersed in it. The psychologist Mihaly Csikszentmihalyi has a fantastic TED talk in which he claims it is the secret to happiness. If nothing else, it feels like the key to fulfilling work.&lt;/p&gt;

&lt;p&gt;When I first moved to Cambridge, I lived in a house share where a slightly eccentric landlady would appear at random hours with old sofas, bike wheels, a man who lived in the shed for two weeks, and on one occasion, four chickens, to live in the back garden.&lt;/p&gt;

&lt;p&gt;My workspace was at a desk in my room at the front of the house. One morning, I was sat at my laptop, engrossed in writing a CRM to help us offer support, when I saw one of the chickens, freely clucking around the driveway. An escapee!&lt;/p&gt;

&lt;p&gt;I spent 20 minutes chasing it around the front of the house (I am from suburban Wolverhampton, and have never had to chase a chicken before), as the Benny Hill theme in my mind’s ear soundtracked events.&lt;/p&gt;

&lt;p&gt;Though I lost 20 minutes chasing the chicken, it took me a further 40 before I was as engrossed in my work as I was before. It was on this day, I decided my house was not a good working environment.&lt;/p&gt;

&lt;p&gt;There is a romanticised image of the home-worker perpetrated cruelly by stock photo vendors, which is that they get to spend the day working in hip coffee shops on their MacBooks. After the chicken incident, I tried this. It turns out though, that coffee shops (especially in Cambridge) are expensive, have rubbish Wi-Fi, and are filled with noisy people.&lt;/p&gt;

&lt;p&gt;Interruptions are costly. The 40 minutes I lost after completing the poultry pursuit and the frequent Wi-Fi outages of Nero, Costa and company resulted in me losing flow.&lt;/p&gt;

&lt;p&gt;I later moved into a house with the same housemates, now friends, and no chickens. I also found a co-working space with coffee and other home-workers, eager to be social, but also to pursue flow. The co-working space has sadly now closed, but I now work from a dedicated spot at home, my housemate’s office, or my girlfriend’s house, and I haven’t looked back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #2:&lt;/strong&gt; Work somewhere you can find flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organise yourself: Give ideas form
&lt;/h3&gt;

&lt;p&gt;Working from home requires self-organisation. No one wants to be harangued by a busybody supervisor peering over your shoulder to check up on you, and at home, no such busybody supervisor exists (hopefully).&lt;/p&gt;

&lt;p&gt;This places you in a position of trust. Most people I know want to do their best work off their own back, but the best of intentions can be derailed by poor planning.&lt;/p&gt;

&lt;p&gt;By 2017, after a few more hires, and having learnt PHP, my role had become a developer role. My own organisational challenges at Eco Web Hosting generally came in the form of interruptions to my intended focus.&lt;/p&gt;

&lt;p&gt;I would talk about a project, not note it anywhere but a skeletal to-do list on my laptop, and set out in earnest to complete it. My relative inexperience at that time meant that I would run into unexpected obstacles, and those would lead me down proverbial rabbit holes of PHP quirks, plus of course, as a junior developer, there would always be bugs to squash.&lt;/p&gt;

&lt;p&gt;By the time I had got past an obstacle, I would find I’d lost sight of the initial idea, and my skeletal plan had morphed into something same, same, but different.&lt;/p&gt;

&lt;p&gt;Poor planning can result in two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Context switching (changing between different tasks)&lt;/li&gt;
&lt;li&gt;Scope creep (your task mutating into more than originally planned)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Context switching is costly. It takes time to find the illusive state of flow. Context switching interrupts that, eating up the time it takes to regain that flow again from your day. It’s also really annoying, feeling something akin to someone knocking down a house of cards you’ve built as you proudly place the last Jack of Clubs at the top.&lt;/p&gt;

&lt;p&gt;Scope creep can occur when a project’s details are not planned clearly from the start, or if it is, but that plan is ignored. This can be a little more insidious and difficult to detect if there is no clear plan to start with.&lt;/p&gt;

&lt;p&gt;Both context switching and scope creep are nightmares of anyone tasked with managing their own time effectively. Here’s how to mitigate it.&lt;/p&gt;

&lt;p&gt;On a basic level, it can be as simple as making sure you have a workspace that it clear and ready for you at the start of each day (as per finding flow).&lt;/p&gt;

&lt;p&gt;When dealing with ideas, which don’t have intrinsic form, giving them a (virtual) physical form is a good place to begin.&lt;/p&gt;

&lt;p&gt;The most effective way I’ve found to deal with my organisational challenges is to type out plans, what they involve, and set target dates for each step using Trello (project management software with a generous free tier).&lt;/p&gt;

&lt;p&gt;Other project management software, such as Asana, is of course available, or if you’re old school, good old pen and paper. I opt for Trello because it’s less wasteful on paper, and keeps the team up-to-date with what I’m up to as I go.&lt;/p&gt;

&lt;p&gt;The important thing is to make things you implicitly think about a project explicit, because the human mind tends to subtly change things if left to its own devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #3:&lt;/strong&gt; Use tools like Trello to organise your ideas, plans and projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  It doesn’t have to be just yourself: Make time to talk
&lt;/h3&gt;

&lt;p&gt;It’s now 2019, and having acquired a Tak, a Mike, a Chris, a Leon and a Ben, there are seven of us at Eco Web Hosting.&lt;/p&gt;

&lt;p&gt;With all of us working remotely, as we developed more distinct roles, we’ve needed to find ways to coordinate ourselves.&lt;/p&gt;

&lt;p&gt;We began chatting on Google Hangouts every day, and would have periodic face-to-face meet-ups. We later moved to the soon-to-be-defunct HipChat, and more recently, to the surprisingly versatile Discord (great for video calls, though their 2,000 character message limit is a pain).&lt;/p&gt;

&lt;p&gt;From a practical perspective, having a means of constant communication was particularly convenient, as quick questions could unobtrusively be answered.&lt;/p&gt;

&lt;p&gt;Face-to-face meetups allowed us to better and more effectively able to communicate bigger ideas about our respective budding sections of the company, and where we wanted it to go. Also, happily, I rather like my workmates, so it was good to hang out.&lt;/p&gt;

&lt;p&gt;From a morale perspective, though, it feels good to have contact with other people during the day. Working remotely can be a lonely experience. Feeling isolated is no way to live, or to work, but Buffer’s State of Remote Work 2018 report found that isolation was the joint-biggest struggle in working remotely.&lt;/p&gt;

&lt;p&gt;With loneliness being a behind a myriad of health problems, from depression to an increased predisposition to heart disease, maintaining quality interactions should be high on the list of priorities of remote workers and companies alike.&lt;/p&gt;

&lt;p&gt;We now touch base every morning with a standup call. The call is a 30-minute catch-up where each of us has a turn to talk about what we did the previous day, what we’re up to the day ahead, and share our wit and wisdom with one another.&lt;/p&gt;

&lt;p&gt;No one enjoys sprawling meetings that lack focus, so our standup is brief. This alone isn’t the same as spending the day in company, so I try to make sure I work around my girlfriend, friends or family at least a few days a week.&lt;/p&gt;

&lt;p&gt;Mediated chat still misses the nuances of in-person interaction, so of course, we at Eco Web Hosting do occasionally find time for to get together in person too.&lt;/p&gt;

&lt;p&gt;Co-working spaces are on the rise and rise, so do some research into your area to see what’s available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson #4:&lt;/strong&gt; Quality human interaction is absolutely essential to live and work well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Take-home lessons
&lt;/h3&gt;

&lt;p&gt;The freedom that comes with working from home removes some of the restrictions of office-based work, freeing you to structure your day in a way that works for you. For me, that meant some trial and error to figure out what exactly that required.&lt;/p&gt;

&lt;p&gt;There is a balance to each aspect.&lt;/p&gt;

&lt;p&gt;Being able to work any time shouldn’t mean working all the time. Being able to work anywhere, doesn’t necessarily mean you should work anywhere. Freedom from micromanagement comes with a need to self-manage effectively. Finally, working remotely can be a little… well… remote, unless you add meaningful interactions into your day.&lt;/p&gt;

&lt;p&gt;These are just my experiences. Working from home is as popular as it’s ever been, and is ever-more possible thanks to faster, more widespread broadband and 4G coverage.&lt;/p&gt;

&lt;p&gt;Are you a remote worker? What are your experiences?&lt;/p&gt;

&lt;h4&gt;
  
  
  Further reading
&lt;/h4&gt;

&lt;p&gt;Bloom, N., Liang, J., Roberts, J. and Ying, Z. (2014). Does Working from Home Work? Evidence from a Chinese Experiment. &lt;em&gt;The Quarterly Journal of Economics&lt;/em&gt;, [online] 130(1), pp.165-218. Available at: &lt;a href="https://www.gsb.stanford.edu/faculty-research/publications/does-working-home-work-evidence-chinese-experiment"&gt;https://www.gsb.stanford.edu/faculty-research/publications/does-working-home-work-evidence-chinese-experiment&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;br&gt;
Campaign to End Loneliness. (2019). &lt;em&gt;The facts on loneliness - Campaign to End Loneliness.&lt;/em&gt; [online] Available at: &lt;a href="https://www.campaigntoendloneliness.org/the-facts-on-loneliness/"&gt;https://www.campaigntoendloneliness.org/the-facts-on-loneliness/&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;br&gt;
Griffis, H (2018), State of Remote Work 2018 Report: What It’s Like to be a Remote Worker in 2018 [online] &lt;em&gt;Buffer.&lt;/em&gt; Available at: &lt;a href="https://open.buffer.com/state-remote-work-2018/"&gt;https://open.buffer.com/state-remote-work-2018/&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;br&gt;
Grippo, A. (2017), Should You Set Clear Work-Home Boundaries? [online] &lt;em&gt;Psychology Today.&lt;/em&gt; Available at: &lt;a href="https://www.psychologytoday.com/gb/blog/the-wide-wide-world-psychology/201705/should-you-set-clear-work-home-boundaries"&gt;https://www.psychologytoday.com/gb/blog/the-wide-wide-world-psychology/201705/should-you-set-clear-work-home-boundaries&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;br&gt;
Park, Y., Fritz, C. and Jex, S. (2011). Relationships between work-home segmentation and psychological detachment from work: The role of communication technology use at home. &lt;em&gt;Journal of Occupational Health Psychology&lt;/em&gt;, [online] 16(4), pp.457-467. Available at: &lt;a href="https://psycnet.apa.org/doiLanding?doi=10.1037%2Fa0023594"&gt;https://psycnet.apa.org/doiLanding?doi=10.1037%2Fa0023594&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;br&gt;
Pozin, I. (2015), There's No Such Thing as Multitasking [online] &lt;em&gt;Forbes.&lt;/em&gt; Available at: &lt;a href="https://www.forbes.com/sites/ilyapozin/2015/01/07/theres-no-such-thing-as-multitasking/#59f2b96b2225"&gt;https://www.forbes.com/sites/ilyapozin/2015/01/07/theres-no-such-thing-as-multitasking/#59f2b96b2225&lt;/a&gt; [Accessed 30 Jan. 2019].&lt;/p&gt;

&lt;h3&gt;
  
  
  Author's Note
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 31st January 2019 by Andy Dunn, our Senior Technical Lead, originally on the &lt;a href="https://www.ecowebhosting.co.uk/blog/four-challenges-of-working-from-home-and-how-to-overcome-them/?utm_campaign=homedev&amp;amp;utm_source=li-170120-homedev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-workingfromhome"&gt;Eco Web Hosting Blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>remoteworking</category>
      <category>homeworking</category>
      <category>worklifebalance</category>
      <category>work</category>
    </item>
    <item>
      <title>Creating Automatic Emails That Work</title>
      <dc:creator>Eco Web Hosting</dc:creator>
      <pubDate>Thu, 09 Jan 2020 15:50:22 +0000</pubDate>
      <link>https://dev.to/ecowebhostinguk/creating-automatic-emails-that-work-pe0</link>
      <guid>https://dev.to/ecowebhostinguk/creating-automatic-emails-that-work-pe0</guid>
      <description>&lt;p&gt;We see them every single day – emails that are sent automatically when something happens. Placing an order. Resetting your password. Updating an expired card. You do something on a website, you get an email in response.&lt;/p&gt;

&lt;p&gt;Those emails are a vital part of every online business, and you need to make sure they're getting the right message across. A well-written and designed automatic email ensures customer trust, can produce repeat visits, calms customer frustration, and cuts down on the amount of time your support team is stuck answering questions that should've really been included in the email.&lt;/p&gt;

&lt;p&gt;When I first joined up with Eco Web Hosting, one of my first tasks was to go through the existing automatic emails and completely rewrite them. And I have a few tips to help you make your emails work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make sure the right information is being passed through
&lt;/h2&gt;

&lt;p&gt;When a customer sets up a hosting package, they get an email that gives them all the details – the login, the control panel link, what to do if they want to transfer a domain, etc. And most of that is passed straight from the database into the email.  I don’t get to see what the customer sees, I just see something like &lt;code&gt;{{domainname}}&lt;/code&gt; or &lt;code&gt;{{controlpanel}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So when you're editing the email, run a few test versions so you can see exactly what's being passed through. See if there are other fields that should be passed through and work with your development team to get those in. Ensure that the information has the right text around it that explains what they're looking at.&lt;/p&gt;

&lt;p&gt;You might not have control over the data, but you do have control over how the data's presented, so make sure it's as clear as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always include a way to contact you
&lt;/h2&gt;

&lt;p&gt;I always think that a good automatic email should cut down on the number of support tickets that are raised. It should have all the details a customer needs so they don't need to ask any questions.&lt;/p&gt;

&lt;p&gt;However, just because it should doesn't mean it will. And that's why you always have a link for customers to get in touch with you.&lt;/p&gt;

&lt;p&gt;It just doesn't cut down on the frustration, it also means you can funnel them into the right place for what they need. You don't want your social media team answering technical questions, your technical support team answering billing questions, or your sales team fending off competition winners. Ensure in each email, there's a way for the reader to get in touch with the right person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give clear instructions
&lt;/h2&gt;

&lt;p&gt;This can be tricky because clarity is a subjective term. What you think makes perfect sense for you might not work for another person.  And even if it does make perfect sense, it might end up being a mountain of text that your customers refuse to read.&lt;/p&gt;

&lt;p&gt;Start by writing down everything that the customer needs to do – no matter how small. Use bullet points or ordered lists. Then review what you've written. &lt;/p&gt;

&lt;p&gt;Are there any side tasks that could be taken out? Maybe link to an article instead of including it in the email?  Could it be broken up with screenshots if it's really long? Or, if it turns out the action is really short (like "Click on the link below and then click 'Purchase'"), could you cut it down even further (like "Purchase On Our Site")?&lt;/p&gt;

&lt;p&gt;Don't be afraid to check with your support teams as well, especially after a few weeks of the emails going out.  They'll be able to tell you what's working and what isn't, and you can revise as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upsell only when appropriate
&lt;/h2&gt;

&lt;p&gt;It's really tempting to add in discounts, special offers, and product mentions into emails. It feels like you have the perfect captive audience, people who are already purchasing things, who are interested in your company, who will obviously buy more products if you just put it in front of them.&lt;/p&gt;

&lt;p&gt;But it only takes one sales pitch to turn a customer off. And if it's in an email that's inappropriate for upselling, you're going to lose a lot of customers really quickly.&lt;/p&gt;

&lt;p&gt;Before you add in that product, think about what the email is saying. Think about when the customer would receive it.  If they're in the middle of an argument in a support ticket, are they really going to want to be told about other products they can buy?&lt;/p&gt;

&lt;p&gt;Upselling works best in two types of customer journeys – when they're being brought in and when they might be leaving. You see this in physical stores as well, with the special offers right when you enter and the fancier treats right by the checkout. Just make sure you're offering the right kind of products in the right locations, such as related items and different tiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add tracking
&lt;/h2&gt;

&lt;p&gt;This is vital. How else are you going to know exactly how well your emails are working?&lt;/p&gt;

&lt;p&gt;Google has a lot of information on &lt;a href="https://support.google.com/analytics/answer/1033863"&gt;how to best set up UTMs&lt;/a&gt;, including &lt;a href="https://ga-dev-tools.appspot.com/campaign-url-builder/"&gt;a generator&lt;/a&gt; that'll build the link for you.  But before you go throwing UTMs around left, right, and centre, have a deep think about how you want them to look.&lt;/p&gt;

&lt;p&gt;How will you identify marketing emails? Service messages? Automatic emails? Are you going to indicate exactly where in the email the link is (head, body, footer)? What type of link it is (image, button, text)? How much detail do you want in the campaign name?&lt;/p&gt;

&lt;p&gt;Work out exactly how you want your UTMs to look and keep that key on hand before you start populating your emails. Not only will it help you remember exactly what and where the link is from, but it'll also make it easier to pull data from Analytics.&lt;/p&gt;

&lt;p&gt;For example, with Eco Web Hosting's emails, I have automatically generated emails (named &lt;code&gt;g-&lt;/code&gt;), service emails (named &lt;code&gt;s-&lt;/code&gt;), and promotional emails (named &lt;code&gt;p-&lt;/code&gt;). Since I put those prefixes into &lt;code&gt;utm_source&lt;/code&gt;, I can then go into Analytics's Source/Medium tab, set it to only include sources from that name, and I instantly have a report telling me exactly how many customers come in from a set of emails.&lt;/p&gt;

&lt;h2&gt;
  
  
  And don't just abandon them!
&lt;/h2&gt;

&lt;p&gt;Combining the five tips above will give you better emails, but don't think you can just create the emails once and then abandon them. Work closely with your support team, your customers, and your data, and make plans for revisions on a regular basis.  I'm already planning out the next set of emails I want to add in, and you better believe there’ll be some changes to the existing ones.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

&lt;h3&gt;
  
  
  Author's Note
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Written 16 October 2019 by Kate Bolin, our Marketing Director, originally on &lt;a href="https://www.ecowebhosting.co.uk/blog/creating-automatic-emails-that-work/?utm_campaign=emailsdev&amp;amp;utm_source=dt-090120-emailsdev&amp;amp;utm_medium=social&amp;amp;utm_content=f-t-emailsblogpost"&gt;the Eco Web Hosting Blog&lt;/a&gt;.&lt;br&gt;
Cover image &lt;a href="https://unsplash.com/photos/IAKIkREkRzY"&gt;Gemma Evans on Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>email</category>
      <category>writing</category>
      <category>business</category>
    </item>
  </channel>
</rss>
