<?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: Rocky V</title>
    <description>The latest articles on DEV Community by Rocky V (@rocky_digitalcave).</description>
    <link>https://dev.to/rocky_digitalcave</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4202945%2Fdee5e7d8-ea19-4a56-bdc0-ce3c1d942736.png</url>
      <title>DEV Community: Rocky V</title>
      <link>https://dev.to/rocky_digitalcave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rocky_digitalcave"/>
    <language>en</language>
    <item>
      <title>Local schema for a Birmingham service business</title>
      <dc:creator>Rocky V</dc:creator>
      <pubDate>Wed, 04 Nov 2026 09:30:00 +0000</pubDate>
      <link>https://dev.to/rocky_digitalcave/local-schema-for-a-birmingham-service-business-2p4g</link>
      <guid>https://dev.to/rocky_digitalcave/local-schema-for-a-birmingham-service-business-2p4g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqpoyqwqsm7v6l46li5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqpoyqwqsm7v6l46li5q.png" alt="Infographic: a LocalBusiness schema checklist covering business type, address, opening hours, service area, sameAs and keeping to one source of markup" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most local business sites either have no structured data or three conflicting copies of it, one from the theme, one from an SEO plugin and one pasted in years ago. Done properly, it is a small amount of JSON-LD that tells search engines who you are, where you work and when you are open. This walkthrough covers what to include, how to output it from WordPress with one small snippet, and the mistakes that cause most of the trouble.&lt;/p&gt;

&lt;p&gt;One caveat first. Structured data does not guarantee a rich result, and Google has no special display for LocalBusiness markup on its own. What it does is remove guesswork. Your name, address, phone number and hours are stated in a format machines read reliably, and they can be checked against your Google Business Profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick the most specific type
&lt;/h2&gt;

&lt;p&gt;LocalBusiness is a parent type with many children. Use the closest match, such as Plumber, Electrician, AccountingService, LegalService or HVACBusiness. If nothing fits, ProfessionalService is a sensible general choice. A vague type is not wrong, but a specific one says more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The properties that matter
&lt;/h2&gt;

&lt;p&gt;For most service businesses, these are enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name, url, telephone, image&lt;/strong&gt; and &lt;strong&gt;logo&lt;/strong&gt;, matching your site and profiles exactly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;address&lt;/strong&gt; as a PostalAddress, even if you hide it on your Google profile because you travel to customers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;openingHoursSpecification&lt;/strong&gt;, using the same hours you publish everywhere else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;areaServed&lt;/strong&gt;, listing the places you cover, which matters for service area businesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sameAs&lt;/strong&gt;, an array of your real profiles (Business Profile, LinkedIn, Facebook and so on).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;&lt;/strong&gt;, a stable identifier so other markup on the page can refer to the business.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  An example for the home page
&lt;/h2&gt;

&lt;p&gt;Here is a placeholder for a heating engineer in Birmingham. Swap in your own details, and keep every value identical to what customers see on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HVACBusiness"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example-heating.co.uk/#business"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Heating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example-heating.co.uk/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"telephone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+44 121 000 0000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example-heating.co.uk/images/van.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PostalAddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"streetAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 Example Street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"addressLocality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Birmingham"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"addressRegion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"West Midlands"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postalCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"B1 1AA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"addressCountry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GB"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"openingHoursSpecification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpeningHoursSpecification"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dayOfWeek"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Monday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tuesday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wednesday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thursday"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Friday"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"opens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"08:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"closes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"17:30"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"areaServed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"City"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Birmingham"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"City"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Solihull"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sameAs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://www.linkedin.com/company/example-heating/"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output it from WordPress
&lt;/h2&gt;

&lt;p&gt;A plugin can do this, but a short snippet keeps it in one place that you control. Put it in a small must-use plugin or your child theme, and only print it on the home page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_head'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&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="nf"&gt;is_front_page&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;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;$schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'@context'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://schema.org'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'@type'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'HVACBusiness'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'@id'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;home_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/#business'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Example Heating'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'url'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;home_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'telephone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'+44 121 000 0000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ...address, hours, areaServed and sameAs as above.&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;script type="application/ld+json"&amp;gt;'&lt;/span&gt;
        &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;wp_json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;JSON_UNESCAPED_SLASHES&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;JSON_UNESCAPED_UNICODE&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/script&amp;gt;'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&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="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building the array in PHP and letting wp_json_encode handle escaping is safer than pasting a hand-written string, because an unescaped quote in a business name will break the whole block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistakes to avoid
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate markup.&lt;/strong&gt; View the page source and search for application/ld+json. If your SEO plugin, your theme and your snippet each print a LocalBusiness block, switch two of them off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Details that disagree.&lt;/strong&gt; If your opening hours or phone number differ between the schema, the page and your Business Profile, you have created the confusion the markup was meant to remove.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marking up your own reviews.&lt;/strong&gt; Google does not show review stars for a business that marks up reviews about itself, so leave aggregateRating out unless the reviews come from a third party and are shown on the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marking up things that are not visible.&lt;/strong&gt; Structured data should describe content people can see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never updating it.&lt;/strong&gt; Change your hours, number or address in one place, then check the schema as part of the same job.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test it
&lt;/h2&gt;

&lt;p&gt;Paste the page address into the Schema Markup Validator at validator.schema.org to check the syntax and property names. Google's Rich Results Test is also worth a look, but it only reports types that qualify for rich results, so an empty result for LocalBusiness is normal. After any change, fetch the page again and confirm the output has exactly one business block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it in context
&lt;/h2&gt;

&lt;p&gt;Schema is one small part of local visibility. It works best alongside a complete Business Profile, consistent details across directories and a useful page for each service and area. If you would like a second pair of eyes on the whole picture, our &lt;a href="https://digitalcave.uk/seo-company-birmingham" rel="noopener noreferrer"&gt;Birmingham SEO team&lt;/a&gt; works through all of it, starting with a free audit.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rocky V is Head of Operations at DigitalCave, a digital marketing agency in Birmingham's Jewellery Quarter.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>schema</category>
      <category>wordpress</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A practical Core Web Vitals checklist for small business WordPress sites</title>
      <dc:creator>Rocky V</dc:creator>
      <pubDate>Fri, 23 Oct 2026 15:55:29 +0000</pubDate>
      <link>https://dev.to/rocky_digitalcave/a-practical-core-web-vitals-checklist-for-small-business-wordpress-sites-2i5i</link>
      <guid>https://dev.to/rocky_digitalcave/a-practical-core-web-vitals-checklist-for-small-business-wordpress-sites-2i5i</guid>
      <description>&lt;p&gt;Most small business WordPress sites do not fail Core Web Vitals because of one big mistake. They fail because of a dozen small ones that pile up: a slider on the homepage, three font families, a page builder loading everything everywhere, and a chat widget that blocks the main thread. This is the checklist we work through when a site comes to us with poor field data. It assumes a typical setup: shared or entry level managed hosting, a commercial theme or page builder, and somewhere between 15 and 40 plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: measure the right thing
&lt;/h2&gt;

&lt;p&gt;Before touching anything, check field data, not just a Lighthouse score. PageSpeed Insights shows Chrome UX Report (CrUX) data at the top when the site has enough traffic. That is what Google uses. Lab scores are useful for debugging but they run on a simulated slow device and can swing wildly between runs.&lt;/p&gt;

&lt;p&gt;The three metrics and their "good" thresholds, measured at the 75th percentile of real visits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LCP&lt;/strong&gt; (Largest Contentful Paint): 2.5 seconds or less&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INP&lt;/strong&gt; (Interaction to Next Paint): 200 milliseconds or less&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLS&lt;/strong&gt; (Cumulative Layout Shift): 0.1 or less&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the site has no CrUX data, add the &lt;code&gt;web-vitals&lt;/code&gt; library and send the numbers to your analytics so you have something real to compare against:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&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;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://unpkg.com/web-vitals@4?module&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBeacon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;onLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nf"&gt;onINP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nf"&gt;onCLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;send&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LCP checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Find the LCP element.&lt;/strong&gt; In Chrome DevTools, open the Performance panel, record a reload and look for the LCP marker. On most small business sites it is the hero image or the main heading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Do not lazy load the hero image.&lt;/strong&gt; WordPress adds &lt;code&gt;loading="lazy"&lt;/code&gt; to images automatically and since 6.3 tries to skip the first content image, but page builders and sliders often bypass that logic. Check the rendered HTML. If your hero has &lt;code&gt;loading="lazy"&lt;/code&gt;, fix it at the source or filter it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_get_loading_optimization_attributes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tag_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$context&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="s1"&gt;'img'&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$tag_name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;is_front_page&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="k"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$GLOBALS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'dc_hero_done'&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="nv"&gt;$GLOBALS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'dc_hero_done'&lt;/span&gt;&lt;span class="p"&gt;]&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="k"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$attrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'loading'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$attrs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'fetchpriority'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'high'&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="nv"&gt;$attrs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Prefer an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; over a CSS background.&lt;/strong&gt; A background image set in a stylesheet is discovered late, after the CSS has downloaded and parsed. If the theme insists on a background, preload it in the head with &lt;code&gt;&amp;lt;link rel="preload" as="image" fetchpriority="high"&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Serve modern formats at sensible sizes.&lt;/strong&gt; WordPress has supported WebP uploads since 5.8 and AVIF since 6.5, provided the server's image library supports them. Make sure &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; are present so a phone is not downloading a 2400 pixel image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Fix server response time.&lt;/strong&gt; If TTFB is above about 800ms, no front end trick will rescue LCP. Turn on full page caching, check that logged out visitors are actually getting cached pages (look for a cache header), and add a persistent object cache such as Redis if the host offers it. WooCommerce sites need care here because cart and checkout pages must stay uncached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Cut render blocking CSS.&lt;/strong&gt; Many page builders ship one large stylesheet for every widget. Use the Coverage tab in DevTools to see how much is unused on the homepage. Where the builder offers "improved asset loading" or similar, turn it on.&lt;/p&gt;

&lt;h2&gt;
  
  
  INP checklist
&lt;/h2&gt;

&lt;p&gt;INP replaced First Input Delay in March 2024 and it is the metric most small business sites now fail. It measures how long the page takes to respond visually after a tap, click or key press, across the whole visit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Audit third party scripts.&lt;/strong&gt; Chat widgets, cookie banners, review carousels, heatmap tools and multiple tracking pixels are the usual suspects. Each one adds JavaScript that competes for the main thread. Ask whether each script earns its place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Defer what you can.&lt;/strong&gt; Since WordPress 6.3 you can register scripts with a loading strategy instead of hacking &lt;code&gt;script_loader_tag&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'site-ui'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;get_stylesheet_directory_uri&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/js/site-ui.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s1"&gt;'1.4.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'strategy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'defer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in_footer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'site-ui'&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;strong&gt;9. Load plugin assets only where they are used.&lt;/strong&gt; Contact Form 7 is a classic example: by default its script and stylesheet load on every page. Dequeue them everywhere except the pages with forms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&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="nf"&gt;is_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'contact'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'request-a-quote'&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="nf"&gt;wp_dequeue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'contact-form-7'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;wp_dequeue_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'contact-form-7'&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="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful with this on sites that use a global popup form. Test every form after the change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Delay non essential tags until interaction.&lt;/strong&gt; A chat widget does not need to load before the visitor has scrolled or moved the mouse. A small loader that injects the script on the first &lt;code&gt;pointerdown&lt;/code&gt;, &lt;code&gt;keydown&lt;/code&gt; or &lt;code&gt;scroll&lt;/code&gt; event removes it from the critical window entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLS checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;11. Give every image and embed dimensions.&lt;/strong&gt; WordPress outputs &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; on images from the media library, but images added through custom HTML blocks, sliders and iframes often lack them. Use CSS &lt;code&gt;aspect-ratio&lt;/code&gt; for embeds such as YouTube or maps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. Stabilise web fonts.&lt;/strong&gt; Self host fonts where possible, preload the one or two files used above the fold, and use &lt;code&gt;font-display: swap&lt;/code&gt; along with a fallback font that has similar metrics. The &lt;code&gt;size-adjust&lt;/code&gt; descriptor in &lt;code&gt;@font-face&lt;/code&gt; can reduce the jump when the web font arrives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. Reserve space for banners.&lt;/strong&gt; Cookie banners and announcement bars that push content down are a common cause of layout shift. Overlay them at the bottom of the viewport or reserve their height in the initial layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it fixed
&lt;/h2&gt;

&lt;p&gt;Performance regresses quietly. A new plugin, a marketing tag added through Tag Manager, or a theme update can undo weeks of work. A few habits help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a short list of approved plugins and review it every quarter.&lt;/li&gt;
&lt;li&gt;Re check field data in PageSpeed Insights or Search Console's Core Web Vitals report a month after any significant change, because CrUX uses a rolling 28 day window.&lt;/li&gt;
&lt;li&gt;Run Query Monitor on staging before deploying, and look at slow database queries as well as front end assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a second pair of eyes on a WordPress build, our &lt;a href="https://digitalcave.uk/wordpress-development-company-birmingham" rel="noopener noreferrer"&gt;WordPress development team in Birmingham&lt;/a&gt; works through exactly this list on client sites. But most of it you can do yourself in an afternoon with DevTools open and a staging copy of the site.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Rocky V, Head of Operations at DigitalCave&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webperf</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
