<?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: WPLake</title>
    <description>The latest articles on DEV Community by WPLake (@wplake).</description>
    <link>https://dev.to/wplake</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%2F997170%2Face983dc-0150-4c41-9246-8e8b3f851029.jpg</url>
      <title>DEV Community: WPLake</title>
      <link>https://dev.to/wplake</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wplake"/>
    <language>en</language>
    <item>
      <title>Month in WordPress: June 2024</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Tue, 02 Jul 2024 07:04:46 +0000</pubDate>
      <link>https://dev.to/wplake/month-in-wordpress-june-2024-1l5d</link>
      <guid>https://dev.to/wplake/month-in-wordpress-june-2024-1l5d</guid>
      <description>&lt;p&gt;A supply chain attack hits plugins, WordPress 6.5.5 and 6.6 RC 1 are released, plugin install limit tops 10M, and ACF launches its 2024 survey. &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Supply chain attack on WordPress.org plugins
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;WP Team: We identified that some plugin authors were reusing passwords exposed in data breaches elsewhere. The compromised accounts were not the result of an exploit on WordPress.org. Instead, the attackers used recycled passwords to add malicious code to a few plugins on the WordPress.org Plugin Directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that some plugin authors used either weak passwords or the same passwords as for other accounts, and these passwords were leaked. Hackers used these weak passwords to brute-force the wp.org plugin author accounts.&lt;/p&gt;

&lt;p&gt;Breakdown of the attack:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;June 24th: WP Plugin Review Team notices threat&lt;/p&gt;

&lt;p&gt;The WordPress.org &lt;a href="https://wordpress.org/support/topic/a-security-message-from-the-plugin-review-team/"&gt;Plugin Review Team was notified&lt;/a&gt; that a malicious actor had taken over one of the plugins. The Plugin Review Team disabled it and released a “clean” updated version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;June 24th: Wordfence Threat Intelligence finds more infected plugins&lt;/p&gt;

&lt;p&gt;The Wordfence Threat Intelligence team conducted additional research based on the WP Plugin Review Team's message and &lt;a href="https://www.wordfence.com/blog/2024/06/supply-chain-attack-on-wordpress-org-plugins-leads-to-5-maliciously-compromised-wordpress-plugins/"&gt;found four more plugins&lt;/a&gt; infected with the same malicious code. The Wordfence team notified the WP Plugin Review Team.&lt;/p&gt;

&lt;p&gt;In all the cases, the injected malware attempts to create a new administrative user account and then sends those details back to an attacker-controlled server. Additionally, it appears the threat actor injected malicious JavaScript into the footer of websites, adding SEO spam throughout the site.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;June 28th: Attack escalation&lt;/p&gt;

&lt;p&gt;Another &lt;a href="https://www.wordfence.com/blog/2024/06/3-more-plugins-infected-in-wordpress-org-supply-chain-attack-due-to-compromised-developer-passwords/"&gt;bunch of four more plugins&lt;/a&gt; were infected, while three malicious updates were stopped by the team, including the Pods plugin with more than 100,000 active installations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;June 29th: The WordPress team takes Major preventive actions&lt;/p&gt;

&lt;p&gt;On June 29th, plugin authors received a notification from the WP Plugins Team requiring a password reset for all plugin authors. Below you can find a full message.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello {username},&lt;br&gt;
    As a follow-up on the Andrew Wilder (NerdPress) and Chloe Chamberland (WordFence) reports that uncovered a limited number of compromised plugins, the Plugin Review team would like to provide more details about the case.&lt;br&gt;
    We identified that some plugin authors were reusing passwords exposed in data breaches elsewhere. The compromised accounts were not the result of an exploit on WordPress.org. Instead, the attackers used recycled passwords to add malicious code to a few plugins on the WordPress.org Plugin Directory.&lt;br&gt;
    First, out of an abundance of caution, additional plugin releases have been paused, and all new plugin commits temporarily need approval by the team. This way, we have the opportunity to confirm that the attackers cannot add malicious code to more plugins.&lt;br&gt;
    We have begun to force reset passwords for all plugin authors and some other users whose information was found by security researchers in data breaches. This will affect some users' ability to interact with WordPress.org or perform commits until their password is reset.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This action ensures that further infections are impossible, and no new infection reports have been made since. If you are an author of any plugin on WP.org, you should check your mailbox and follow the instructions for resetting your password. Additionally, it is recommended to enable 2FA authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. WordPress 6.5.5 Security Release and 6.6 RC 1 are available
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/news/2024/06/wordpress-6-5-5/"&gt;WordPress 6.5.5&lt;/a&gt;, a security release, was made available on June 24th. It contains a series of security fixes, and it is recommended that you update your WordPress installation.&lt;/p&gt;

&lt;p&gt;Meanwhile, the first release candidate (RC1) for WordPress 6.6 &lt;a href="https://wordpress.org/news/2024/06/wordpress-6-6-rc1/"&gt;is also available&lt;/a&gt;, offering developers and enthusiasts a preview of the upcoming changes in the &lt;a href="https://make.wordpress.org/core/2024/06/25/wordpress-6-6-field-guide/"&gt;WordPress 6.6&lt;/a&gt; release, which is scheduled for July 16th.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. WordPress plugin directory raised the "Active Install" limit to 10+ Million
&lt;/h2&gt;

&lt;p&gt;The WordPress Plugin Directory &lt;a href="https://wp-content.co/plugin-directory-raised-active-install-limit/"&gt;has increased the “Active Install” limit&lt;/a&gt;, allowing plugins hosted on WordPress.org to display active installation counts exceeding 10 million.&lt;/p&gt;

&lt;p&gt;We've updated our &lt;a href="https://wplake.org/blog/most-popular-wordpress-plugins/"&gt;most popular WP plugins by active installations&lt;/a&gt; article, so you can check which plugins have surpassed this milestone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. ACF launched its annual survey for 2024
&lt;/h2&gt;

&lt;p&gt;One of the &lt;a href="https://wplake.org/blog/acf-metabox-and-pods-review/"&gt;most popular meta field plugins&lt;/a&gt;, &lt;a href="https://wplake.org/blog/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt;, &lt;a href="https://www.advancedcustomfields.com/blog/acfs-annual-survey-2024-your-voice-matters/"&gt;has launched&lt;/a&gt; its second publicly available annual survey. &lt;a href="https://www.advancedcustomfields.com/annual-survey/"&gt;The survey&lt;/a&gt; consists of around 30 questions, most of which are multiple-choice, and includes questions about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How you’re using ACF’s fields and features&lt;/li&gt;
&lt;li&gt;Your experiences with building WordPress sites&lt;/li&gt;
&lt;li&gt;What improvements or additions you’d like to see in ACF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can participate in the survey, which is open until July 31.&lt;/p&gt;

&lt;p&gt;By publishing the results publicly (and anonymously), ACF makes this survey useful not only for themselves but for the entire WordPress community.&lt;/p&gt;

&lt;p&gt;The survey contains not only ACF-specific questions but also general WordPress questions, helping to understand developer preferences. You can find the results of the 2023 ACF annual survey &lt;a href="//A%20supply%20chain%20attack%20hits%20plugins,%20WordPress%206.5.5%20and%206.6%20RC%201%20are%20released,%20plugin%20install%20limit%20tops%2010M,%20and%20ACF%20launches%20its%202024%20survey."&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. New to the web platform in June
&lt;/h2&gt;

&lt;p&gt;This month, &lt;a href="https://web.dev/blog/web-platform-06-2024?hl=en"&gt;new features&lt;/a&gt; have landed in stable and beta web browsers during June 2024, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://web.dev/blog/set-methods?hl=en"&gt;JavaScript Set Methods&lt;/a&gt;:
intersection, union, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/async-clipboard"&gt;Async Clipboard API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Color Interpolation in CSS Gradients&lt;/li&gt;
&lt;li&gt;Cross-Document view transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. This WordPress month in numbers
&lt;/h2&gt;

&lt;p&gt;In this ongoing section, we utilize &lt;a href="https://wplake.org/blog/wordpress-org-api/"&gt;WordPress.org plugin and theme APIs&lt;/a&gt; to feature newly published items from this month. It's an excellent opportunity to discover new tools and improve your workflow.&lt;/p&gt;

&lt;p&gt;168 new plugins and 111 new themes.&lt;br&gt;
(Note, the list is too long, see the &lt;a href="https://wplake.org/blog/month-in-wordpress-june-2024/#6-this-wordpress-month-in-numbers_ys1l"&gt;original interactive element&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Thank you for reading! &lt;a href="https://wplake.org/blog/category/wordpress-news/"&gt;Subscribe&lt;/a&gt; to our monthly newsletter to stay updated on the latest WordPress news and useful tips.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>cms</category>
      <category>news</category>
      <category>webdev</category>
    </item>
    <item>
      <title>WordPress Interactivity API: Detailed Explanation</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Tue, 28 May 2024 14:54:41 +0000</pubDate>
      <link>https://dev.to/wplake/wordpress-interactivity-api-detailed-explanation-19d1</link>
      <guid>https://dev.to/wplake/wordpress-interactivity-api-detailed-explanation-19d1</guid>
      <description>&lt;p&gt;The WordPress Interactivity is a relatively new API that allows the creation of declarative templates natively. It harnesses Preact and offers SSR out-of-the-box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GhYOrAg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AHsI3Art5eDn9hG_BdE8b4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GhYOrAg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AHsI3Art5eDn9hG_BdE8b4A.png" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. When and why did the WP Interactivity API appear?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/news/2024/04/regina/"&gt;WordPress 6.5&lt;/a&gt;, released in April 2024, brings a significant shift with its own front-end reactivity system. In recent years, &lt;a href="https://www.gatsbyjs.com/docs/glossary/headless-wordpress/"&gt;decoupled (headless) WordPress&lt;/a&gt; installations have become more popular. They allow you to leverage modern front-end frameworks like &lt;a href="https://react.dev/"&gt;React&lt;/a&gt; or &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, this approach is time-consuming and has a clear drawback: you lose the WP ecosystem features on the front end, such as plugins. Tools like &lt;a href="https://wordpress.org/plugins/wordpress-seo/"&gt;Yoast&lt;/a&gt; must be forgotten and switched to manual implementation.&lt;/p&gt;

&lt;p&gt;WordPress 6.5 &lt;a href="https://make.wordpress.org/core/2024/03/04/interactivity-api-dev-note/"&gt;introduces&lt;/a&gt; a game-changing feature by adding reactivity tools to its core, allowing you to master modern front-end natively without turning to the decoupled approach. This feature has been developed by the WP Core team for a while but was delivered only in April 2024. Therefore, many developers aren’t familiar with it yet.&lt;/p&gt;

&lt;p&gt;While there is a &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/"&gt;section in the official documentation&lt;/a&gt; that explains the key aspects of the Interactivity API, it isn’t as comprehensive as you might want. In addition, in some places it mentions parts related to the Gutenberg blocks only (though it can be used fully independently), so if you’re new to both, it might make understanding more difficult.&lt;/p&gt;

&lt;p&gt;That’s why we made this comprehensive and in-depth explanation, in which we’ll share our knowledge with you.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. WordPress Interactivity API definition
&lt;/h3&gt;

&lt;p&gt;Now let’s figure out what exactly is behind the WordPress Interactivity API name.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Interactivity API is a standard system of directives, based on declarative code, for adding front-end interactivity to blocks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, it’s a way to create the front end of a WP website using a declarative approach. If the official definition seems unclear to you, don’t worry, we’ll tackle it piece by piece.&lt;/p&gt;

&lt;h4&gt;
  
  
  Declarative VS Imperative approach
&lt;/h4&gt;

&lt;p&gt;Declarative, um, what’s that? If you aren’t familiar with React or Vue, this word might be totally unfamiliar to you. But since the WordPress Interactivity API is based on it, we need to understand it. There are two different approaches to mastering the front end: &lt;strong&gt;imperative&lt;/strong&gt; and &lt;strong&gt;declarative&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Since the early web days and for decades, we all have been using the &lt;strong&gt;imperative&lt;/strong&gt; approach. The &lt;strong&gt;imperative&lt;/strong&gt; way is when we have a static initial markup and then add interactivity by manually making changes to the markup using JS code. So, we manually query the right nodes and make changes for every action.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;declarative&lt;/strong&gt; approach is a modern alternative that suggests building a dynamic layout by &lt;em&gt;declaring&lt;/em&gt; the elements and their behavior rules all at once. When a user interacts with it, the layout is updated automatically according to the behavior rules we specified, so we don’t need to manually query nodes and make changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Drawbacks of the Imperative approach
&lt;/h4&gt;

&lt;p&gt;As an example, let’s review an advanced accordion, which shows its state above. While you’re rarely going to meet similar accordions in real life, the idea is to showcase the weak sides of the &lt;strong&gt;imperative&lt;/strong&gt; way when it comes to handling multiple states.&lt;/p&gt;

&lt;p&gt;Besides the classic items toggle feature, we simply need to display the current accordion state (opened or closed) with the item name (so opened item name and last closed item name). The markup can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion"&amp;gt;
  &amp;lt;div class='accordion__panel'&amp;gt;
    &amp;lt;div class='accordion__heading-opened' hidden&amp;gt;
      Current open item is &amp;lt;span class='accordion__open-item-name'&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class='accordion__heading-closed'&amp;gt;
      Items are closed.
      &amp;lt;p class='accordion__closed-item' hidden&amp;gt;
        Last opened item is &amp;lt;span class='accordion__closed-item-name'&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class='accordion__item'&amp;gt;
    &amp;lt;p class='accordion__item-title'&amp;gt;Title&amp;lt;/p&amp;gt;
    &amp;lt;div class='accordion__item-content'&amp;gt;Content&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;!--other items--&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For hiding elements, we’ll use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden"&gt;“hidden” HTML attribute&lt;/a&gt;, and for the initial state, we hide the ‘__heading-open’ and ‘__closed-item’ elements. So, what would a classic JS implementation look like? Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener('DOMContentLoaded', () =&amp;gt; {
    document.body.querySelectorAll('.accordion__item-title').forEach((title) =&amp;gt; {
        title.addEventListener('click', () =&amp;gt; {
            let item = title.closest('.accordion__item');
            let isToOpen = !item.classList.contains('accordion__item--open');
            let accordion = item.closest('.accordion');
            let prevItem = accordion.querySelector('.accordion__item--open');

            // Handle closing the previous item
            if (prevItem) {
                prevItem.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item').removeAttribute('hidden');
                accordion.querySelector('.accordion__closed-item-name').innerText = prevItem.querySelector('.accordion__item-title').innerText;
            }

            // Toggle the current item
            if (isToOpen) {
                accordion.querySelector('.accordion__heading-closed').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-opened').removeAttribute('hidden');
                item.classList.add('accordion__item--open');
                accordion.querySelector('.accordion__open-item-name').innerText = title.innerText;
            } else {
                accordion.querySelector('.accordion__heading-opened').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-closed').removeAttribute('hidden');
                item.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item-name').innerText = title.innerText;
            }
        });
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see, the task that looked straightforward and sounded quite simple when described in text turned into a series of conditional checks and DOM queries.&lt;/p&gt;

&lt;p&gt;If you’re an experienced developer, you know how the JS code looks for real complex logic scenarios in real life. With the &lt;strong&gt;imperative&lt;/strong&gt; approach, we must write an update query chain for every possible action manually.&lt;/p&gt;

&lt;p&gt;The more actions we support, the more complex the code becomes. Gaining experience, you indeed can find shorter and better solutions, but you can’t write less than the necessary minimum, which is still tough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Benefits of the Declarative approach
&lt;/h4&gt;

&lt;p&gt;As we saw above, the main drawback of the &lt;strong&gt;imperative&lt;/strong&gt; approach is the necessity of manually handling conditionals and queries. The &lt;strong&gt;declarative&lt;/strong&gt; approach offers a solution by &lt;em&gt;declaring&lt;/em&gt; the elements and their behavior rules all at once.&lt;/p&gt;

&lt;p&gt;Let’s consider this with an advanced accordion example, without getting into the implementation details for now. Starting from the top, we have the panel with two different headings: “__heading-open” and “__heading-closed”.&lt;/p&gt;

&lt;p&gt;Only one of them should be shown at a time. So, we conclude that we need an &lt;strong&gt;isOpen&lt;/strong&gt; state and bind this state to the visibility of the items. When &lt;strong&gt;isOpen&lt;/strong&gt; is true, “__heading-open” is visible while “__heading-closed” is hidden, and vice versa.&lt;/p&gt;

&lt;p&gt;Introducing this state and moving conditional checks to the markup would allow us to simplify the JS code and change the UI as simply as changing the boolean value of our state. Let’s see how it should look in pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class='accordion__heading-opened' {if !isOpen then add 'hidden' attribute}&amp;gt;
  Current open item is &amp;lt;span class='accordion__open-item-name'&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class='accordion__heading-closed' {if isOpen then add 'hidden' attribute}&amp;gt;
  Items are closed.
  &amp;lt;p class='accordion__closed-item' hidden&amp;gt;
    Last opened item is &amp;lt;span class='accordion__closed-item-name'&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in JS, we can just do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;isOpen = true || isOpen = false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the layout will change its state without manual node queries.&lt;/p&gt;

&lt;p&gt;We hope that now you get the idea of the declarative approach. Don’t worry about the implementation details or other accordion elements, as the example above is only for demonstrating the idea. Below in the article, we’ll implement the advanced accordion example completely using the WP Interactivity API.&lt;/p&gt;

&lt;p&gt;For now, you should know that nowadays there are multiple JS frameworks based on the declarative approach, including &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;, &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt;, &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;, and others. The WordPress Interactivity API is another way to achieve this, built on top of the &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt; framework.&lt;/p&gt;

&lt;h4&gt;
  
  
  Interactivity VS Reactivity
&lt;/h4&gt;

&lt;p&gt;If you’re familiar with React or Vue, you know that both frameworks use the term &lt;strong&gt;reactivity&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For example,&lt;/em&gt; &lt;a href="https://vuejs.org/guide/extras/reactivity-in-depth.html"&gt;&lt;em&gt;Vue’s documentation&lt;/em&gt;&lt;/a&gt; &lt;em&gt;states: “One of Vue’s most distinctive features is the unobtrusive reactivity system. Component state consists of reactive JavaScript objects. When you modify them, the view updates.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This term is widely used and describes a key characteristic of the &lt;em&gt;declarative&lt;/em&gt; approach, allowing the layout to change as soon as the variables used in it change.&lt;/p&gt;

&lt;p&gt;What about &lt;strong&gt;interactivity&lt;/strong&gt;? Let’s review its definition again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Interactivity API is a standard system of directives, based on declarative code, for adding front-end interactivity to blocks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From this description, you might think that &lt;strong&gt;reactivity&lt;/strong&gt; and &lt;strong&gt;interactivity&lt;/strong&gt; are very similar, or even the same.&lt;/p&gt;

&lt;p&gt;In fact, &lt;strong&gt;Interactivity&lt;/strong&gt; is just the name that WordPress has chosen for this API. The Interactivity API’s front end is built on top of &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;, which is a &lt;strong&gt;reactive&lt;/strong&gt; framework. So, when you’re using the WP Interactivity API, you can say that it’s a &lt;strong&gt;reactive&lt;/strong&gt; tool as well.&lt;/p&gt;

&lt;p&gt;Additionally, keep in mind that &lt;strong&gt;reactivity&lt;/strong&gt; is a feature, even if it’s a key feature of the Interactivity API. Besides it, the API includes more, such as Server Side Rendering, which has its own implementation.&lt;/p&gt;

&lt;p&gt;We’ll delve into SSR later, but for now, you should know that the Interactivity API encompasses all the features related to it, while &lt;strong&gt;reactivity&lt;/strong&gt; is an important part of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Key aspects of the WP Interactivity API
&lt;/h3&gt;

&lt;p&gt;The WordPress Interactivity API consists of two main features: &lt;strong&gt;directives&lt;/strong&gt; and &lt;strong&gt;store&lt;/strong&gt; , which are used in blocks. The &lt;strong&gt;store&lt;/strong&gt; is a common term that describes &lt;strong&gt;state&lt;/strong&gt; and &lt;strong&gt;context&lt;/strong&gt; storages.&lt;/p&gt;

&lt;p&gt;Let’s figure out the new terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Block&lt;/em&gt;&lt;/strong&gt; &lt;em&gt; — it’s an independent page element with its own&lt;/em&gt; &lt;strong&gt;&lt;em&gt;store&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;and template that contains&lt;/em&gt; &lt;strong&gt;&lt;em&gt;directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;. One block can include other blocks as children and can also “talk” to each other.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Storage&lt;/em&gt;&lt;/strong&gt; &lt;em&gt; — it’s a set of variables based on which we write&lt;/em&gt; &lt;strong&gt;&lt;em&gt;directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;and add any logic. It’s the way to ‘expose’ some variable to the declarative template.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Directive&lt;/em&gt;&lt;/strong&gt; &lt;em&gt; — it’s a declaration rule added to the markup that controls an element’s behavior based on the element’s state. In pseudocode, it looks like this:&lt;/em&gt; &lt;strong&gt;&lt;em&gt;{if isOpen then add ‘hidden’ attribute}&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While &lt;strong&gt;state&lt;/strong&gt; and &lt;strong&gt;context&lt;/strong&gt; have differences that we’ll review below, both of them act as a scope of variables for specific elements.&lt;/p&gt;

&lt;p&gt;If you’re familiar with React or Vue, you’ll likely understand the concepts more easily. However, the WP Interactivity API has its own implementation, and you can’t directly use things from the React world as is.&lt;/p&gt;

&lt;p&gt;If you are encountering this for the first time, don’t worry if you haven’t grasped all the terms yet. In this chapter, we’ll review each in detail and apply them to the accordion example mentioned above.&lt;/p&gt;

&lt;h4&gt;
  
  
  Usage note
&lt;/h4&gt;

&lt;p&gt;Keep in mind that though the WP Interactivity API is built into the WP core, it isn’t applied to all the HTML by default. By default, it works only within your custom Gutenberg blocks that have the related option enabled. It’s also supported by the &lt;a href="https://wplake.org/advanced-views-lite/"&gt;Advanced Views Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We recommend reading this chapter without practical reproducing. After getting familiar with the key concepts, you can try implementing them on your own. The ‘Where you can use’ chapter below will share when and how you can harness it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1) Block
&lt;/h3&gt;

&lt;p&gt;Let’s start with this basic term.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A&lt;/em&gt; &lt;strong&gt;&lt;em&gt;block&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is an independent page element with its own store and template that contains&lt;/em&gt; &lt;strong&gt;&lt;em&gt;directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;. One block can include other blocks as children and can also “talk” to each other.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can turn any HTML tag into a block as simply as adding a data attribute. So, in our case, the top accordion element is going to be a block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" data-wp-interactive="my-accordion"&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;data-wp-interactive&lt;/strong&gt; attribute is necessary to ‘mark’ a specific element as a block. Everything inside it will be considered as block parts. As mentioned above, we can have a block within a block, so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" data-wp-interactive="my-accordion"&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
  &amp;lt;div class='popup' data-wp-interactive="my-popup"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a valid example. As the attribute value, we can pass any string, but it must be unique within the page. We recommend always giving clear and human-readable names because to ‘talk’ with some block on the page from another block, we’ll use exactly the name defined in this attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2) State
&lt;/h3&gt;

&lt;p&gt;So, we’ve defined an interactive block. But before we use any ‘magic’ &lt;strong&gt;directives&lt;/strong&gt; , we need to define some data that can be used in the &lt;strong&gt;directives&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;State&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is a set of variables based on which we can write&lt;/em&gt; &lt;strong&gt;&lt;em&gt;directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;and add any logic. It’s one of the ways to ‘expose’ some variable to the&lt;/em&gt; declarative &lt;em&gt;template. The main characteristics of the Block state are that it is global and public, i.e., it’s saved in the page scope under the block name and available to others.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; &lt;strong&gt;&lt;em&gt;State&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is an optional feature, so we can have an interactive block without the _ **_state&lt;/em&gt;** &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, any variables defined in the &lt;strong&gt;state&lt;/strong&gt;  are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Global across all the blocks with the same type (within the current page)
This means that even if you have multiple blocks of the same type on the same page, they’ll all share the same state. That’s the primary difference from the &lt;strong&gt;context&lt;/strong&gt; , which allows defining ‘current block-only’ variables.&lt;/li&gt;
&lt;li&gt;Public
This means other blocks can ‘request’ their values based on the variable name and use the block name as a ‘namespace’.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;State&lt;/strong&gt; variable can be passed from the backend, or defined on the front. Since WordPress is a PHP framework, the state can be passed from the backend by calling a PHP function.&lt;/p&gt;

&lt;p&gt;If we need to define a &lt;strong&gt;state&lt;/strong&gt; variable on the backend, we must call a specific function above the block definition. The function is called &lt;strong&gt;wp_interactivity_state&lt;/strong&gt;. So let’s introduce the &lt;strong&gt;isOpen&lt;/strong&gt; state and add it to our accordion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php echo wp_interactivity_state( 'my-accordion', [
 'isOpen' =&amp;gt; false,
] ); ?&amp;gt;
&amp;lt;div class="accordion" data-wp-interactive="my-accordion"&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;wp_interactivity_state&lt;/strong&gt; is a WordPress function that accepts two arguments: the block name, and an array of state variables.&lt;/p&gt;

&lt;p&gt;If we have nothing to pass from the backend, we can define the state on the front end in the JS code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { state } = store("my-accordion", {
  state: {
    isOpen: false
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;FYI: The ‘mixed’ way is also supported, so you can pass some state variables from the backend while defining others on the front.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.3) Context
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt; is another way to define variables that can be used in &lt;strong&gt;directives&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Context&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is a set of variables based on which we can write&lt;/em&gt; &lt;strong&gt;&lt;em&gt;directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;and add any logic. It’s one of the ways to ‘expose’ some variable to the&lt;/em&gt; declarative &lt;em&gt;template. The main characteristics of the Block context are that it is local and private, i.e., it’s saved within the current block and not available to others.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Context&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;is an optional feature, so we can have an interactive block without the _ **_context&lt;/em&gt;** &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, any variables defined in the context are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current block only
This means that even if you have multiple blocks of the same type on the same page, each of them will have its own &lt;strong&gt;context&lt;/strong&gt;. That’s the primary difference from the &lt;strong&gt;state&lt;/strong&gt; , which allows sharing variables across block instances with the same type.&lt;/li&gt;
&lt;li&gt;Private
This means other blocks can’t ‘request’ their values directly.&lt;/li&gt;
&lt;li&gt;Inheritable
Context variables are available to the current node and all its inner elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unlike state variables, which can be defined both on the back-end and front-end side, &lt;strong&gt;context&lt;/strong&gt; can only be defined on the back-end. Context variables must be passed as JSON using the &lt;strong&gt;data-wp-context&lt;/strong&gt; attribute. Let’s add the &lt;strong&gt;isOpen&lt;/strong&gt; context variable to our accordion block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" data-wp-interactive="my-accordion" 
data-wp-context='{"isOpen": false}'&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, that’s how it looks like in the markup. But in real life, you’ll likely want to pass PHP variables. It can be done like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" data-wp-interactive="my-accordion" 
data-wp-context='&amp;lt;?php echo json_encode(["isOpen" =&amp;gt; false]); ?&amp;gt;'&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can define the &lt;strong&gt;data-wp-context&lt;/strong&gt; attribute manually or call &lt;strong&gt;wp_interactivity_data_wp_context&lt;/strong&gt; , a special WP function, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" 
data-wp-interactive="my-accordion" 
&amp;lt;?php echo wp_interactivity_data_wp_context(["isOpen" =&amp;gt; false]); ?&amp;gt;'&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we mentioned, the &lt;strong&gt;context&lt;/strong&gt; is private and inheritable, so it’s available only for the current node and all its children. So you can have multiple contexts inside your block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion" data-wp-interactive="my-accordion"&amp;gt;
  &amp;lt;div data-wp-context='&amp;lt;?php echo json_encode(["someVar" =&amp;gt; "some value"]); ?&amp;gt;'&amp;gt;
    &amp;lt;!-- inner HTML elements here --&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div data-wp-context='&amp;lt;?php echo json_encode(["anotherVar" =&amp;gt; true]); ?&amp;gt;'&amp;gt;
    &amp;lt;!-- inner HTML elements here --&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4) Directives
&lt;/h3&gt;

&lt;p&gt;Now, let’s review &lt;strong&gt;directives&lt;/strong&gt; , the key feature of the Interactivity API, which allows us to create reactive layouts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Directives&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;are custom attributes added to the markup of your block to define behavior of the DOM elements.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first glance, they appear to be plain &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes"&gt;HTML data attributes&lt;/a&gt;, familiar to everyone. They follow the &lt;strong&gt;data-wp&lt;/strong&gt; format, appearing as &lt;strong&gt;data-wp-{x}=”y”&lt;/strong&gt; , where &lt;strong&gt;x&lt;/strong&gt; is the directive name, and &lt;strong&gt;y&lt;/strong&gt; is the value.&lt;/p&gt;

&lt;p&gt;To illustrate with our accordion example, let’s recall the heading part in pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class='accordion__heading-opened' {if !isOpen then add 'hidden' attribute}&amp;gt;
  Current open item is &amp;lt;span class='accordion__open-item-name'&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class='accordion__heading-closed' {if isOpen then add 'hidden' attribute}&amp;gt;
  Items are closed.
  &amp;lt;p class='accordion__closed-item' hidden&amp;gt;
    Last opened item is &amp;lt;span class='accordion__closed-item-name'&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s convert it into real WP Interactive code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'&amp;gt;
    &amp;lt;div class='accordion__panel'&amp;gt;
        &amp;lt;div class='accordion__heading-opened' data-wp-bind--hidden="!context.isOpen"&amp;gt;
            Current open item is &amp;lt;span class='accordion__open-item-name'&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class='accordion__heading-closed' data-wp-bind--hidden="context.isOpen"&amp;gt;
            Items are closed.
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!--other items--&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We defined the ‘my-accordion’ block.&lt;/li&gt;
&lt;li&gt;Defined isOpen variable in the block context.&lt;/li&gt;
&lt;li&gt;Added ‘data-wp-bind — hidden’ directives to the target elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Directive explanation
&lt;/h4&gt;

&lt;p&gt;Let’s examine the first directive: &lt;strong&gt;data-wp-bind — hidden=”!context.isOpen”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the data attribute name, we used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;data-wp-&lt;/strong&gt; as a common prefix, necessary for any directive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bind&lt;/strong&gt; , which is a directive name.
Bind is one of the WP directives that allows controlling attributes based on boolean variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;— hidden&lt;/strong&gt; , representing the name of the attribute we want to control.
Here, we can place any valid HTML attribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let’s review the value:  &lt;strong&gt;!context.isOpen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;reactive&lt;/em&gt; code, creating a bind that persists until the page is closed. Even if you change &lt;strong&gt;isOpen&lt;/strong&gt; later, after some action or timeout, it will execute our rule and keep the attribute in sync.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why did Interactivity API choose directives instead of JSX or others?
&lt;/h4&gt;

&lt;p&gt;If you’re familiar with Vue or React, you can draw an analogy with their approaches. For example, in Vue, we also use &lt;a href="https://vuejs.org/api/built-in-directives.html#v-bind"&gt;built-in directives&lt;/a&gt;, like &lt;strong&gt;v-bind:src=”srcVariable”&lt;/strong&gt; , and in React, we use &lt;strong&gt;className={className}&lt;/strong&gt;. In WordPress, we use &lt;strong&gt;data-wp-class — classname=”y”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While the longer directive format may initially frustrate you, you’ll likely agree that their names are very clear. Keep in mind that WordPress is built on a classic base, and the WP Interactivity API is designed to work with any plain HTML code. &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/iapi-faq/#what-approaches-have-been-considered-instead-of-using-directives"&gt;Here&lt;/a&gt; the official documentation explains all the reasons directives were chosen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;So, even though JSX style is shorter, WP uses the classic data-attribute approach. From our experience, after creating several blocks, you’ll get used to it and won’t notice it at all. If you’re still considering that the WP Interactivity API isn’t as great as you envisioned, it has something to make you happier:&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unlike React and Vue, it offers SSR (Server-Side Rendering) out-of-the-box! This means that initially the &lt;strong&gt;directives&lt;/strong&gt; will be processed on the server side by WordPress, and the browser will receive the already correct markup, with all classes and attributes set.&lt;/p&gt;

&lt;p&gt;Also, WordPress will take care of hydration/rehydration and will sync the data and markup, so in JavaScript, you’ll be able to access and change &lt;strong&gt;state&lt;/strong&gt; , &lt;strong&gt;context&lt;/strong&gt; , and more.&lt;/p&gt;

&lt;p&gt;We have taken a step away from the &lt;strong&gt;directives&lt;/strong&gt; to show the power of the Interactivity API. We’ll review SSR in detail a little later. For now, let’s return to &lt;strong&gt;directives&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  List of the available directives
&lt;/h4&gt;

&lt;p&gt;The Interactivity API brings a row of &lt;strong&gt;directives&lt;/strong&gt; that cover all our needs. You can find the full list on &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#list-of-directives"&gt;this page&lt;/a&gt; of the official documentation. Let’s review commonly used:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;wp-bind
As you saw, this &lt;strong&gt;directive&lt;/strong&gt; allows you to control any HTML attribute, like &lt;strong&gt;data-wp-bind — hidden=”context.isOpen”&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;wp-text
Allows you to control the &lt;strong&gt;innerText&lt;/strong&gt; of the node. Inside the value, you should pass a string variable, e.g., &lt;strong&gt;data-wp-text=”state.submitLabel”&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;wp-class
Allows you to control the class appearance based on a boolean value. E.g., &lt;strong&gt;data-wp-class — active=”context.isActive”&lt;/strong&gt;. The class name can contain any valid characters, so don’t worry, things like &lt;strong&gt;button — enabled&lt;/strong&gt; won’t break the directive.&lt;/li&gt;
&lt;li&gt;wp-style
Allows you to control inline styles, e.g., &lt;strong&gt;data-wp-style — color=”context.color”&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;wp-on
Allows you to assign listeners. E.g., &lt;strong&gt;data-wp-on — click=”actions.submit”&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.5) JS code
&lt;/h3&gt;

&lt;p&gt;Now let’s review the final piece: the JavaScript code itself, which allows us to attach listeners and add external actions, like Ajax. On the frontend, the WordPress Interactivity API is available as a separate file — a tiny library (35KB) built on top of Preact.&lt;/p&gt;

&lt;p&gt;We import this library in the JavaScript code of any block. Not all blocks require JavaScript code, so it’s optional. If we don’t need to add listeners to our block, we don’t include JavaScript code, and consequently, the library won’t be imported.&lt;/p&gt;

&lt;p&gt;As we mentioned earlier, the Interactivity API provides Server-Side Rendering out-of-the-box, so directives will be executed on the server side. When we need to set state variables on the frontend or define any actions, we have to add the following line to our JS code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { store } from '@wordpress/interactivity';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic &lt;a href="https://javascript.info/modules-intro"&gt;JavaScript module import&lt;/a&gt;, and &lt;strong&gt;@wordpress/interactivity&lt;/strong&gt; is an alias to &lt;strong&gt;/wp-includes/js/dist/interactivity.min.js&lt;/strong&gt; , added by WordPress using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap"&gt;ImportMap feature&lt;/a&gt;. The following parts are available to import: &lt;strong&gt;store&lt;/strong&gt; , &lt;strong&gt;getContext&lt;/strong&gt; , and &lt;strong&gt;getElement&lt;/strong&gt;. Let’s review all of them.&lt;/p&gt;

&lt;p&gt;For now, let’s focus on the &lt;strong&gt;store&lt;/strong&gt; , the primary one. To define the block in JavaScript, we must call the &lt;strong&gt;store&lt;/strong&gt; function imported from the Interactivity library.&lt;/p&gt;

&lt;p&gt;The first argument must be the name of our block from the &lt;strong&gt;data-interactive&lt;/strong&gt; attribute, and the second is an object with the settings. Let’s add the &lt;strong&gt;isClosed&lt;/strong&gt; state to our accordion block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { store } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
  state: {
    isClosed: false,
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The object supports the following keys: &lt;strong&gt;state&lt;/strong&gt; , &lt;strong&gt;actions&lt;/strong&gt; , &lt;strong&gt;callbacks&lt;/strong&gt;. Items defined inside the &lt;strong&gt;state&lt;/strong&gt; key are used as state variables. Items inside &lt;strong&gt;actions&lt;/strong&gt; are used in action directives, such as &lt;strong&gt;wp-on — click&lt;/strong&gt;. &lt;strong&gt;Callbacks&lt;/strong&gt; are for internal events, like &lt;strong&gt;init&lt;/strong&gt; , which is called when the node is created.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: As we mentioned earlier, you can define a&lt;/em&gt; &lt;strong&gt;&lt;em&gt;state&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;both on the backend and frontend. This means if we’ve defined the&lt;/em&gt; &lt;strong&gt;&lt;em&gt;isOpen&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;state on the backend using the&lt;/em&gt; &lt;strong&gt;&lt;em&gt;wp_interactivity_state&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;function, we can access this property in JavaScript code too, even without defining it again.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we can write &lt;strong&gt;let isOpen = state.isOpen&lt;/strong&gt; in our JS code and it’ll return the value of the state variable that we defined on the backend, as WordPress will pass them to the front as JSON automatically. However, keep in mind that state variables defined in JavaScript will only be available on the client side.&lt;/p&gt;

&lt;p&gt;This means if you haven’t defined a state variable on the backend but only on the frontend, you can still use this ‘front-only’ state in &lt;strong&gt;directives&lt;/strong&gt;. However, such &lt;strong&gt;directives&lt;/strong&gt; will be skipped during Server-Side Rendering and executed only on the frontend.&lt;/p&gt;

&lt;p&gt;Consequently, if you use them for UI control, the client may see the element you want to hide until JavaScript is loaded and executed. Therefore, if you use state variables in &lt;strong&gt;directives&lt;/strong&gt; that affect the initial UI, we recommend defining them on the backend.&lt;/p&gt;

&lt;h4&gt;
  
  
  getContext function
&lt;/h4&gt;

&lt;p&gt;Besides &lt;strong&gt;state&lt;/strong&gt; , we can also get access to the block &lt;strong&gt;context&lt;/strong&gt;. For this, we need to import the &lt;strong&gt;getContext&lt;/strong&gt; function from the Interactivity library. After that, we can call &lt;strong&gt;getContext()&lt;/strong&gt; in any action and get any context variable as a property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {store, getContext} from '@wordpress/interactivity';

store("my-accordion", {
    actions: {
        toggleItem: () =&amp;gt; {
            let context = getContext();
            context.isItemClosed = !context.isItemClosed;
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to store variables, the context variables are also writable, so you can change them when needed, and it will update all the directives where the context variable is used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: The&lt;/em&gt; &lt;strong&gt;&lt;em&gt;getContext()&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;call will automatically get the closest context to the node on which the event is fired. This means if you’ve added&lt;/em&gt; &lt;strong&gt;&lt;em&gt;data-wp-context&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;to a block and its inner child, and then added a click listener to that child, in the action method, the&lt;/em&gt; &lt;strong&gt;&lt;em&gt;getContext()&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;call will return the context of this child.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Store variables in Actions
&lt;/h4&gt;

&lt;p&gt;Let’s combine &lt;strong&gt;store&lt;/strong&gt; and &lt;strong&gt;actions&lt;/strong&gt; to add a click action to our accordion example to see how it all looks together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { store } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
  state: {
    isClosed: true,
  },
  actions: {
    toggle: (event) =&amp;gt; {
      state.isClosed = !state.isClosed;
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we assign this listener to our accordion item using the &lt;strong&gt;wp-on&lt;/strong&gt; directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class='accordion__item'&amp;gt;
  &amp;lt;p class='accordion__item-title' data-wp-on--click="actions.toggle"&amp;gt;Title&amp;lt;/p&amp;gt;
  &amp;lt;div class='accordion__item-content' data-wp-bind--hidden="!state.isOpen"&amp;gt;Content&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Thanks to the reactivity, when we change the state variable, it will execute &lt;strong&gt;directives&lt;/strong&gt; in which that variable was used and will add or remove the &lt;strong&gt;hidden&lt;/strong&gt; attribute. We can use the same variable in multiple &lt;strong&gt;directives&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  getElement function
&lt;/h4&gt;

&lt;p&gt;Using the &lt;strong&gt;getElement&lt;/strong&gt; function, we can directly access the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement"&gt;HTMLElement&lt;/a&gt; of the current block. In most cases, you won’t need it, but there are rare instances when it’s useful. For example, if you need to access the browser’s API to scroll the content inside or get the element’s width.&lt;/p&gt;

&lt;p&gt;To get the HTMLElement, we need to get the &lt;strong&gt;ref&lt;/strong&gt; part of the function response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { ref } = getElement();
// ref is an ordinary HTMLElement instance, so we can do anything with it, like:
console.log(ref.getBoundingClientRect());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Callbacks
&lt;/h4&gt;

&lt;p&gt;Returning to the &lt;strong&gt;store&lt;/strong&gt; object keys: &lt;strong&gt;state&lt;/strong&gt; , &lt;strong&gt;actions&lt;/strong&gt; , and &lt;strong&gt;callbacks&lt;/strong&gt;  — we have seen the first two in the action. The third key, &lt;strong&gt;callbacks&lt;/strong&gt; , is used to define general block callbacks that are called by the library, such as &lt;strong&gt;init&lt;/strong&gt; or  &lt;strong&gt;run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;init&lt;/strong&gt; is called only once when the node is created, while &lt;strong&gt;run&lt;/strong&gt; is called on every node rendering. To add them to the block, besides defining them in JS, we need to set them in directives too, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {store, getElement, getContext} from '@wordpress/interactivity';

store("my-accordion", {
    callbacks: {
        init: () =&amp;gt; {
            let {ref} = getElement();

            console.log('Accordion node is parsed', {
                HTMLElement: ref,
                isOpen: getContext().isOpen,
            });
        }
    }
});

&amp;lt;div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'
     data-wp-init="callbacks.init"&amp;gt;
    &amp;lt;!--inner items--&amp;gt;
&amp;lt;/div&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.6) Summary
&lt;/h3&gt;

&lt;p&gt;Breathe out, because at this step, we can congratulate you — the most complex parts are behind you, and you’ve learned the key aspects of the Interactivity API.&lt;/p&gt;

&lt;p&gt;Let’s depict them all at once so you can get a complete picture in your mind.&lt;/p&gt;

&lt;p&gt;The WordPress Interactivity API is based on plain HTML and provides &lt;strong&gt;directives&lt;/strong&gt; , &lt;strong&gt;store&lt;/strong&gt; , and &lt;strong&gt;context&lt;/strong&gt; , which allow for &lt;em&gt;declarative&lt;/em&gt; templates. Let’s look at a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php wp_interactivity_state('my-accordion', ['isClosed' =&amp;gt; true,]) ?&amp;gt;
&amp;lt;div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'
     data-wp-init="callbacks.init"&amp;gt;
    &amp;lt;div class='accordion__panel'&amp;gt;
        &amp;lt;div class='accordion__heading-opened' data-wp-bind--hidden="state.isClosed"&amp;gt;
            Current open item is &amp;lt;span class='accordion__open-item-name'&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class='accordion__heading-closed' data-wp-bind--hidden="context.isOpen"&amp;gt;
            Items are closed.
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!--other items--&amp;gt;
&amp;lt;/div&amp;gt;

import { store, getElement, getContext } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
    callbacks: {
        init: () =&amp;gt; {
            let { ref } = getElement();

            console.log('Accordion node is parsed', {
                HTMLElement: ref,
                isOpen: getContext().isOpen,
                isClosed: state.isClosed,
            });
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what’s happening here?&lt;/p&gt;

&lt;h4&gt;
  
  
  1. In the PHP backend, the templates are rendered and directives are processed
&lt;/h4&gt;

&lt;p&gt;Before passing to the browser, WordPress processes the directives and updates the markup accordingly. In addition, WP converts all the state variables into JSON and passes them to the browser along with the markup.&lt;/p&gt;

&lt;p&gt;In our case, the &lt;strong&gt;__heading-opened&lt;/strong&gt; element will have the &lt;strong&gt;hidden&lt;/strong&gt; attribute, according to the &lt;strong&gt;false&lt;/strong&gt; value of the &lt;strong&gt;isOpen&lt;/strong&gt; context variable. Meanwhile, the &lt;strong&gt;__heading-closed&lt;/strong&gt; element won’t have this attribute, because &lt;strong&gt;isClosed&lt;/strong&gt; state variable is  &lt;strong&gt;true&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. On the frontend, our JS code loads the interactivity API JS library
&lt;/h4&gt;

&lt;p&gt;That library parses the JSON (in React it’s called rehydration) and calls our block definition. The WP Interactivity.js library will put the store variables defined on the backend into the &lt;strong&gt;state&lt;/strong&gt; variable in our JS code ( &lt;strong&gt;isClosed&lt;/strong&gt; in our case).&lt;/p&gt;

&lt;p&gt;It will also call the &lt;strong&gt;init&lt;/strong&gt; callback to which we added a listener using the &lt;strong&gt;wp-init&lt;/strong&gt; directive. In this callback, we print the HTML element of the block, along with the &lt;strong&gt;isOpen&lt;/strong&gt; variable from the &lt;strong&gt;context&lt;/strong&gt; and the &lt;strong&gt;isClosed&lt;/strong&gt; variable from the  &lt;strong&gt;state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From a logical point of view, it’s pointless to have both variables at once, but we included them to showcase how you can use both &lt;strong&gt;store&lt;/strong&gt; and &lt;strong&gt;context&lt;/strong&gt; simultaneously. That’s the way the WordPress Interactivity API works, so make sure you get the whole picture.&lt;/p&gt;

&lt;p&gt;If something is unclear, we recommend re-read the related explanation above before continuing with the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Server Side Rendering in the Interactivity API
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: This information is useful for understanding how the SSR works in the Interactivity API behind the scenes, but it is not necessary for the basic API usage. You can safely skip this chapter.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  About the SSR overall
&lt;/h4&gt;

&lt;p&gt;The main drawback of any reactive frameworks in JS is client-side rendering. While classical applications send ready HTML to the client, reactive frameworks like React or Vue create HTML on the fly, based on the defined components and their data.&lt;/p&gt;

&lt;p&gt;In practice, this means clients will see an empty page, or at least sections, for some time until JS processes everything. This not only hurts the UX but also SEO, as search engines can’t immediately parse the page content.&lt;/p&gt;

&lt;p&gt;Many search engines don’t support it, and while Google claims to support it, SEO experts &lt;a href="https://www.techmagic.co/blog/react-seo/"&gt;don’t recommend using client-side rendering&lt;/a&gt; for pages sensitive to SEO scores. If you’re familiar with React/Vue, you know that full-stack frameworks like &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; (React) and &lt;a href="https://nuxt.com/"&gt;Nuxt.js&lt;/a&gt; (Vue) offer SSR.&lt;/p&gt;

&lt;p&gt;They can ‘preload’ HTML by executing JavaScript on the server side (Node.js) and passing already prepared HTML along with the necessary states, so the client can ‘hydrate’ this data and restore its state. They seamlessly handle all the nuances, but behind the scenes, it requires quite a significant effort.&lt;/p&gt;

&lt;p&gt;One of the difficulties here is that frameworks use Node.js on the backend and plain JavaScript on the client side in the browser. This means that the same piece of code can be executed in either of these environments, which are quite different.&lt;/p&gt;

&lt;h4&gt;
  
  
  SSR implementation in the WP Interactivity API
&lt;/h4&gt;

&lt;p&gt;Now, back to WordPress. As we mentioned, the Interactivity.js on the front end is based on Preact, so it doesn’t offer any SSR. Fortunately, WordPress comes with its own SSR solution, and while it may sound a little crude, it’s a nice solution that supports any plain HTML.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;SSR in WordPress Interactivity API is based on its own HTML API (&lt;/em&gt; &lt;a href="https://developer.wordpress.org/reference/classes/wp_html_tag_processor/"&gt;&lt;em&gt;WP_HTML_Tag_Processor&lt;/em&gt;&lt;/a&gt;&lt;em&gt;), which WordPress&lt;/em&gt; &lt;a href="https://make.wordpress.org/core/2023/03/07/introducing-the-html-api-in-wordpress-6-2/"&gt;&lt;em&gt;introduced in version 6.2&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea is to parse HTML pieces with the interactive directives on the backend (PHP) and modify the markup based on them, to deliver the built markup ready to the client. In addition, it includes ‘hydration’ on the client, to transfer all the states to the client side.&lt;/p&gt;

&lt;p&gt;In this way, all state variables that we add to the blocks using the &lt;strong&gt;wp_interactivity_state&lt;/strong&gt; function call in PHP, and context variables from the &lt;strong&gt;data-wp-context&lt;/strong&gt; attribute, will be used during directives execution in PHP SSR.&lt;/p&gt;

&lt;p&gt;Afterward, the state variables will be added to the current page as JSON, and then will be parsed on the client and attached to the JS block states, as we showed in the JS code explanation. In this way, the client and SEO engines get the ready HTML from the beginning, while developers can access all the data from the backend.&lt;/p&gt;

&lt;p&gt;Though it required directive support in PHP from WordPress, it integrates well with the WP ecosystem, making developers’ lives easier and the user experience much better. If you think this is a rough solution that may be bad for performance, you shouldn’t worry about it.&lt;/p&gt;

&lt;p&gt;It’s implemented smartly, so WordPress doesn’t parse all the page HTML, but only the pieces where the Interactivity API can be used, additionally limiting parsing to nodes with data-wp attributes. In this way, it doesn’t add significant overhead to the whole process.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Where you can use WordPress Interactivity API
&lt;/h3&gt;

&lt;p&gt;Since we’ve learned the basics, let’s now see where we can apply this knowledge. As we mentioned earlier, the Interactivity API SSR isn’t applied to all the page content, so by default, you can’t start using it in any template. Below we provide the ways you can apply it:&lt;/p&gt;

&lt;h4&gt;
  
  
  5.1) In custom Gutenberg blocks (created manually)
&lt;/h4&gt;

&lt;p&gt;By default, the WordPress Interactivity API is available in &lt;a href="https://developer.wordpress.org/block-editor/"&gt;Gutenberg blocks&lt;/a&gt;. To enable its support for a specific block, you need to define &lt;strong&gt;“interactivity”: true&lt;/strong&gt; in the &lt;strong&gt;block.json&lt;/strong&gt; data and can use all the WP Interactivity features in &lt;strong&gt;render.php&lt;/strong&gt; and &lt;strong&gt;view.js&lt;/strong&gt;  files.&lt;/p&gt;

&lt;p&gt;This is good news if you’re already familiar with and experienced with the custom Gutenberg block creation process. Otherwise, we wouldn’t recommend this method, as creating custom Gutenberg blocks from scratch is time-consuming and requires React knowledge.&lt;/p&gt;

&lt;p&gt;In most cases, you’ll need to write the markup for the same block twice: first in React for the Gutenberg editor, then in PHP for the front end.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.2) In custom Gutenberg blocks (created using a third-party vendor)
&lt;/h4&gt;

&lt;p&gt;Overall, building WordPress pages from custom Gutenberg blocks is still a good idea because they’re modular, efficient on the front end, and provide a good UX for editors. In our agency, we harness the &lt;a href="https://wplake.org/blog/acf-blocks/"&gt;ACF Blocks&lt;/a&gt; feature of the &lt;a href="https://wplake.org/blog/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt; plugin.&lt;/p&gt;

&lt;p&gt;This feature allows the creation of custom Gutenberg blocks without hassle. You can also use &lt;a href="https://wplake.org/blog/meta-box-plugin/#metabox-blocks_oep8"&gt;MB Blocks&lt;/a&gt; or &lt;a href="https://wplake.org/blog/pods-plugin/#3-pods-blocks_s6bz"&gt;Pods blocks&lt;/a&gt; features. Check our &lt;a href="https://wplake.org/blog/acf-metabox-and-pods-review/"&gt;best custom field plugins review&lt;/a&gt; to compare and learn how to use them.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.3) In templates of the Advanced Views Framework
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://wplake.org/advanced-views-lite/"&gt;Advanced Views&lt;/a&gt; Framework &lt;a href="https://wplake.org/blog/advanced-views-plugin-review/"&gt;introduces&lt;/a&gt; smart templates for the WordPress front-end, simplifying post queries and template creation. These templates harness the Twig engine and &lt;a href="https://docs.acfviews.com/templates/wordpress-interactivity-api"&gt;support the Interactivity API&lt;/a&gt;out-of-the-box, so you can use it in any template without extra actions.&lt;/p&gt;

&lt;p&gt;These templates &lt;a href="https://docs.acfviews.com/templates/file-system-storage"&gt;can be stored inside your theme&lt;/a&gt;, making them Git and IDE-friendly. Additionally, you can employ TypeScript/Sass and &lt;a href="https://docs.acfviews.com/templates/file-system-storage#tailwind-usage"&gt;Tailwind&lt;/a&gt; for them.&lt;/p&gt;

&lt;p&gt;Another benefit is that it also &lt;a href="https://docs.acfviews.com/display-content/custom-gutenberg-blocks-pro"&gt;supports Gutenberg block creation&lt;/a&gt; (via third-party vendors mentioned above), so you can turn any template into a custom Gutenberg block, providing a nice experience for editors while enjoying the modular approach and Interactivity API features.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.4) In plain PHP templates
&lt;/h4&gt;

&lt;p&gt;Though the Interactivity API may seem like a Gutenberg-oriented tool, it isn’t so by the fact. It’s a core and public API that can be used anywhere.&lt;/p&gt;

&lt;p&gt;In your theme or plugin, you can call the &lt;strong&gt;wp_interactivity_process_directives&lt;/strong&gt; function and pass a string with the HTML code that has directives. These directives will be executed, and the function will return the updated markup.&lt;/p&gt;

&lt;p&gt;So it may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

echo wp_interactivity_state('my-accordion', [
    'isOpen' =&amp;gt; false,
]);

ob_start();
?&amp;gt;

&amp;lt;div class="accordion" data-wp-interactive="my-accordion"&amp;gt;
  &amp;lt;!-- inner HTML elements here --&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;?php

$html = (string) ob_get_clean();
echo wp_interactivity_process_directives($html);


import { store, getElement, getContext } from '/wp-includes/js/dist/interactivity.min.js';

store('my-accordion', {
    actions: {
        // ...
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While it’s possible to use the Interactivity API in this manner, we recommend building websites modularly, using independent blocks with their own assets, as in the case of custom Gutenberg blocks and smart templates of the Advanced Views Framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Example of the interactive block
&lt;/h3&gt;

&lt;p&gt;Now, you have all the knowledge, and we recommend you to make several examples with your own hands to practice and store the knowledge better in your head. Below, as promised, we provide an advanced accordion example mentioned above, turned into the WP Interactive block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php 

wp_interactivity_state( 'my-accordion', [
 'isOpen' =&amp;gt; false,
 'isLastItemSet' =&amp;gt; false,
 'lastOpenedItemName' =&amp;gt; "",
] ); ?&amp;gt;

&amp;lt;div class="accordion"
     data-wp-interactive="my-accordion"&amp;gt;
    &amp;lt;div class='accordion__panel'&amp;gt;
        &amp;lt;div class='accordion__heading-opened' data-wp-bind--hidden="!state.isOpen"&amp;gt;
            Current open item is &amp;lt;span class='accordion__open-item-name' data-wp-text="state.lastOpenedItemName"&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class='accordion__heading-closed' data-wp-bind--hidden="state.isOpen"&amp;gt;
            Items are closed.
            &amp;lt;p class='accordion__closed-item' data-wp-bind--hidden="!state.isLastItemSet"&amp;gt;
                Last opened item is &amp;lt;span class='accordion__closed-item-name'
                                          data-wp-text="state.lastOpenedItemName"&amp;gt;&amp;lt;/span&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class='accordion__item' data-wp-context='{"isItemClosed":true,"itemName":"First"}'&amp;gt;
        &amp;lt;p class='accordion__item-title' data-wp-on--click="actions.toggleItem" data-wp-text="context.itemName"&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;div class='accordion__item-content' data-wp-bind--hidden="context.isItemClosed"&amp;gt;Content of the first item&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class='accordion__item' data-wp-context='{"isItemClosed":true, "itemName":"Second"}'&amp;gt;
        &amp;lt;p class='accordion__item-title' data-wp-on--click="actions.toggleItem" data-wp-text="context.itemName"&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;div class='accordion__item-content' data-wp-bind--hidden="context.isItemClosed"&amp;gt;Content of the second item
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt; 

import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    state: {
        openedItemTitle: null,
        get isLastItemSet() {
            return '' !== state.lastOpenedItemName;
        },
        get isOpen() {
            return null !== state.openedItemTitle;
        }
    },
    actions: {
        toggleItem: (event) =&amp;gt; {
            let titleElement = event.target;
            let context = getContext();

            // Handle closing the previous item
            if (null !== state.openedItemTitle &amp;amp;&amp;amp;
                titleElement !== state.openedItemTitle) {
                state.openedItemTitle.click();
            }

            // Toggle the current item
            context.isItemClosed = !context.isItemClosed;

            // update the top state
            state.lastOpenedItemName = context.itemName;
            state.openedItemTitle = false === context.isItemClosed ?
                titleElement :
                null;
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Getters: In this implementation, we used the built-in JS&lt;/em&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get"&gt;&lt;em&gt;getter feature&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to add dynamic state variables. This feature can be added to any object in JS and is particularly useful in our case, as&lt;/em&gt; &lt;strong&gt;&lt;em&gt;wp-bind&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;boolean properties support only boolean primitives, so we couldn’t add conditions there.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, this is a declarative implementation of the accordion block. Let’s now recall how the JS code looked in the imperative approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener('DOMContentLoaded', () =&amp;gt; {
    document.body.querySelectorAll('.accordion__item-title').forEach((title) =&amp;gt; {
        title.addEventListener('click', () =&amp;gt; {
            let item = title.closest('.accordion__item');
            let isToOpen = !item.classList.contains('accordion__item--open');
            let accordion = item.closest('.accordion');
            let prevItem = accordion.querySelector('.accordion__item--open');

            // Handle closing the previous item
            if (prevItem) {
                prevItem.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item').removeAttribute('hidden');
                accordion.querySelector('.accordion__closed-item-name').innerText = prevItem.querySelector('.accordion__item-title').innerText;
            }

            // Toggle the current item
            if (isToOpen) {
                accordion.querySelector('.accordion__heading-closed').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-opened').removeAttribute('hidden');
                item.classList.add('accordion__item--open');
                accordion.querySelector('.accordion__open-item-name').innerText = title.innerText;
            } else {
                accordion.querySelector('.accordion__heading-opened').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-closed').removeAttribute('hidden');
                item.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item-name').innerText = title.innerText;
            }
        });
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, how much shorter the event handler is now, and what a nice thing to update variables instead of querying nodes directly and updating attributes manually!&lt;/p&gt;

&lt;p&gt;You can already see the huge benefit and shift in the approach even in this small example. Recall all your JS code from real life, and envision now how much better it’s going to be when it’s written using the Interactivity API.&lt;/p&gt;

&lt;p&gt;Now it’s a great time to experiment! Try to change something in that to get practical experience with the WordPress Interactivity API!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Demonstration note: If you’re an exprienced web developer, you can see that our implementations are quite simple and can be improved. In real life, it would use the&lt;/em&gt; &lt;strong&gt;&lt;em&gt;wp-each&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;directive, or at least a PHP loop by the data array. But don’t judge strictly, we made it as simple as possible for demonstration purposes.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7. Pro Tips on the Interactivity API
&lt;/h3&gt;

&lt;p&gt;There are several useful things you should know to harness the full potential of the API. Some of them we already briefly mentioned, and now let’s review them in detail:&lt;/p&gt;

&lt;h4&gt;
  
  
  7.1) Getters in State
&lt;/h4&gt;

&lt;p&gt;As you saw in the declarative advanced accordion implementation above, we can define any getters in the state object. This is a very useful feature, and you’re going to use it often due to the fact that most directives support only boolean variables.&lt;/p&gt;

&lt;p&gt;It means you can’t write conditional statements, like in plain JS code, &lt;strong&gt;data-wp-bind — hidden=”state.isFirst &amp;amp;&amp;amp; !state.isSecond”&lt;/strong&gt; or &lt;strong&gt;“state.name == ‘’”&lt;/strong&gt;. These won’t work. So when you need such conditions, you can put them into a getter and define that getter inside the attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    state: {
        get isLastItemSet() {
            return '' !== state.lastOpenedItemName;
        },
    },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside that getter, we can also harness context variables by calling &lt;strong&gt;getContext()&lt;/strong&gt;. Also, pay attention that getters defined in the JS code aren’t available on the backend, so during SSR, &lt;strong&gt;directives&lt;/strong&gt; that include them will be skipped.&lt;/p&gt;

&lt;p&gt;In these cases when you want them to participate in SSR, you can define them using the &lt;strong&gt;wp_interactivity_state&lt;/strong&gt; function as primitive booleans, and then just override them in the JS by assigning a function to the same name.&lt;/p&gt;

&lt;h4&gt;
  
  
  7.2) Event object in Actions
&lt;/h4&gt;

&lt;p&gt;When we assign our callback to some element in plain JavaScript, like &lt;strong&gt;.addEventListener(‘click’)&lt;/strong&gt;, this callback will receive the first argument with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event"&gt;Event&lt;/a&gt; type. The object will vary depending on the action itself, but in all cases, it implements the general Event interface.&lt;/p&gt;

&lt;p&gt;Actions in the WP Interactivity API also receive this object, so you can use it as you need, e.g., to get the current element, like &lt;strong&gt;event.target&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {store} from '@wordpress/interactivity';

store("my-accordion", {
    actions: {
        toggle: (event) =&amp;gt; {
            let clickedHTMLElement = event.target;
            // clickedHTMLElement.innerHTML = 'x';
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  7.3) Loop Directive: wp-each
&lt;/h4&gt;

&lt;p&gt;It’s an advanced directive, which you’ll need only in cases where the interface requires dynamic item creation and removal. An example is the classic ‘todo’ list, where besides editing, items can be added and removed.&lt;/p&gt;

&lt;p&gt;This directive allows defining a template that will be applied to all items of the list. The idea is that when you change the related variable in JS, it’ll keep the items in sync automatically.&lt;/p&gt;

&lt;p&gt;Let’s review this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

$context = [
 'list' =&amp;gt; [
  [
   "id" =&amp;gt; "en",
   "value" =&amp;gt; "hello"
  ],
  [
   "id" =&amp;gt; "es",
   "value" =&amp;gt; "hola"
  ],
  [
   "id" =&amp;gt; "pt",
   "value" =&amp;gt; "olá"
  ]
 ]
];

?&amp;gt;

&amp;lt;div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='&amp;lt;?php
     echo json_encode( $context ); ?&amp;gt;'&amp;gt;
    &amp;lt;ul&amp;gt;
        &amp;lt;template
                data-wp-each--item="context.list"
                data-wp-each-key="context.item.id"&amp;gt;
            &amp;lt;li data-wp-text="context.item.value"&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/template&amp;gt;
    &amp;lt;/ul&amp;gt;
    &amp;lt;button data-wp-on--click="actions.removeLastItem"&amp;gt;Remove last item&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt; 

import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    actions: {
        removeLastItem: (event) =&amp;gt; {
            let context = getContext();

            context.list.pop();
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have a list of items, where each has an id and value. Using the special &lt;strong&gt;Template&lt;/strong&gt; tag and &lt;strong&gt;data-wp-each&lt;/strong&gt; directive, we define the loop by the list. In the directive name, we define the item name, in our case &lt;strong&gt;item&lt;/strong&gt; (data-wp-each — item=), and as value, we define the list itself, which can be either a context or state variable.&lt;/p&gt;

&lt;p&gt;Pay attention that we use the &lt;strong&gt;wp-each-key&lt;/strong&gt; directive to point to the unique item ID, thanks to which the Interactivity API will keep items in sync.&lt;/p&gt;

&lt;p&gt;This loop will be processed during SSR and replaced with the markup built based on the items. But unlike a plain PHP loop, it’ll keep the list context variable defined in the &lt;strong&gt;wp-each&lt;/strong&gt; directive in sync with the markup, so when we remove the last item of the array in the action, it’ll remove the target item in the markup automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  7.4) Cross Block Communication
&lt;/h4&gt;

&lt;p&gt;As we mentioned earlier, all the block state variables are public, so they can be accessible from other blocks. But how can we do it?&lt;/p&gt;

&lt;p&gt;To get the state of another block, we should call the &lt;strong&gt;store&lt;/strong&gt; function with the name of the target block, but without passing the second argument, as we did before.&lt;/p&gt;

&lt;p&gt;Below we show how to get the &lt;strong&gt;someData&lt;/strong&gt; state variable from the &lt;strong&gt;my-another-block&lt;/strong&gt;. This code can be placed anywhere, e.g., inside any action of our accordion block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {store} from '@wordpress/interactivity';

// ....

console.log(store("my-another-block").state.someData);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Conclusions
&lt;/h3&gt;

&lt;p&gt;The appearance of the WordPress Interactivity API is a significant shift in the WordPress ecosystem. Although it was released recently and hasn’t gained widespread recognition yet, it is definitely going to play a crucial role in WordPress development in the near future.&lt;/p&gt;

&lt;p&gt;This built-in integration will help developers build interactive frontends easily. Thanks to the unified approach, plugin and theme vendors can develop their own interactive blocks, which will be able to interact with each other, regardless of the vendor.&lt;/p&gt;

&lt;p&gt;We hope this article was useful and that you’ve understood all the key aspects of the Interactivity API. Happy developing!&lt;/p&gt;

</description>
      <category>gutenberg</category>
      <category>wordpress</category>
      <category>preact</category>
      <category>reactive</category>
    </item>
    <item>
      <title>Display Custom Post Types and ACF fields in WordPress — WPLake</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Wed, 07 Jun 2023 09:35:56 +0000</pubDate>
      <link>https://dev.to/wplake/display-custom-post-types-and-acf-fields-in-wordpress-wplake-23b2</link>
      <guid>https://dev.to/wplake/display-custom-post-types-and-acf-fields-in-wordpress-wplake-23b2</guid>
      <description>&lt;h3&gt;
  
  
  Display Custom Post Types and ACF fields in WordPress — WPLake
&lt;/h3&gt;

&lt;p&gt;Custom Post Types lets you add more functionality to your website. It’s also helpful when you’re already using the standard post for something else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CTUnKr6x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A57n3LR3DS36yjxSJIXuXoQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CTUnKr6x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A57n3LR3DS36yjxSJIXuXoQ.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Custom Post Types are a way of organizing your content, like jars in your kitchen.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  About Custom Post Types
&lt;/h3&gt;

&lt;p&gt;WordPress already has a built-in post type called ‘Post’. Which of course, is what many bloggers use to write their articles. As you may already know, this is one of the things that has made WordPress so popular. It’s a blogging tool on steroids, extending it with embedded content and so many other extras. With the birth of the Gutenberg editor, you now have something called blocks. Which has really been a game changer in so many ways.&lt;/p&gt;

&lt;p&gt;Okay, so what are Custom Post Types, what can it do and why, would you even want to create more complexity in an already “feature rich” WordPress post. Well you’d be surprised.&lt;/p&gt;

&lt;p&gt;Let’s dig in.&lt;/p&gt;
&lt;h4&gt;
  
  
  Plugins mentioned in this article;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://wordpress.org/plugins/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt; (Free)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wordpress.org/plugins/acf-views/"&gt;ACF Views&lt;/a&gt; (Free)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Reasons to use Custom Post Types
&lt;/h3&gt;

&lt;p&gt;When you’ve got posts, it’s great, but what if you’re creating a business directory. Is each post then a business or an article, what if you’ve got a news section too?&lt;/p&gt;

&lt;p&gt;Of course you could use taxonomy to define some content categories, or even tags if you’re desperate. But ultimately the “posts” will still be posts at the end of it, saved in the same database tables. And still be listed under “Posts” in the WordPress back-end.&lt;/p&gt;

&lt;p&gt;This isn’t actually the correct way to define different types of content. If you’ve got some blog posts and they’re in different categories, like news and notices, then it isn’t such a big deal.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example of CPT usage
&lt;/h4&gt;

&lt;p&gt;You’ve decided that you’re going to just use the built-in posts and that’s that. Then consider this, what if some content needs an extra sub headline field, or still on the business directory example. What if the “company” post already uses the ‘Featured image’ for the profile cover. Now you need an extra field for the logo, but again this logo isn’t applicable for use on other content.&lt;/p&gt;

&lt;p&gt;It therefore quickly becomes obvious, that yes you need a way to define different types of content, and with that to assign some extra fields that’s specific to that content type.&lt;/p&gt;

&lt;p&gt;There are ways to create a Custom Post Types (CPT) using code or other plugins, but when you’re a veteran WordPress web developer or designer. Then you probably know that every single plugin you install affects your site. So always try to reduce the number of plugins, and use only well supported plugins, which is an important aspect of &lt;a href="https://wplake.org/blog/9-tips-for-wordpress-speed-optimization/"&gt;WordPress speed optimization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So with that in mind, we always try to use as few plugins as possible. In this case and most other cases we usually choose &lt;a href="http://wordpress.org/plugins/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt; (ACF) for creating extra fields and since ACF v 6.1 you now have the added benefit of creating CPT’s.&lt;/p&gt;
&lt;h3&gt;
  
  
  Behind the scenes for Custom Post Types
&lt;/h3&gt;

&lt;p&gt;There is Post Meta and Custom Post Type. Each of these play a role when it comes to custom fields.&lt;/p&gt;

&lt;p&gt;In WordPress there are certain types of data like Posts, Images, Pages and even Users that you can create yourself. All the information is stored in a single database table that’s called ‘wp_posts’.&lt;/p&gt;

&lt;p&gt;Therefore, when we create a Post, a Page or a User or even a &lt;a href="https://wplake.org/blog/display-acf-fields-on-the-woocommerce-shop-page/"&gt;WooCommerce&lt;/a&gt; Product. The information will be stored inside the ‘wp_posts’ table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ex5Y1Paa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/890/1%2A7tTQnsHWyLocrn8cpEl0QQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ex5Y1Paa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/890/1%2A7tTQnsHWyLocrn8cpEl0QQ.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot you’ll see that there a few post types, attachment, pages and acf-field. Now inside each post it has the same structure, an ID, Post Author, Date, Content, Title, Excerpt and Post Type. These attributes are all necessary and required.&lt;/p&gt;
&lt;h4&gt;
  
  
  Database structure
&lt;/h4&gt;

&lt;p&gt;So, every entry has the same exact structure, which is defined by the structure of this table. And therefore we cannot add another column and name it what we like. That just isn’t possible, and this is where Post Meta comes in. The Post Meta is saved within a separate database called ‘wp_postmeta’. See the table below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BVUZg9_T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/696/1%2Aqs7pGw3kT46KsTNjCl_wDg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BVUZg9_T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/696/1%2Aqs7pGw3kT46KsTNjCl_wDg.jpeg" width="696" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this table we have ‘meta_id’, ‘post_id’, a ‘meta_key’ and a ‘meta_value’. Each entry in this table has a key and a value, and we can associate this pair to any Post ID. So in these rows of data are associations to Post ID’s, and they’re considered characteristics of the Post, or Page etc.&lt;/p&gt;

&lt;p&gt;When you need to add a characteristic to a Post or Page or another Post Type. Then you’d need to add it in the ‘wp_postmeta’ database, and not in the ‘wp_posts’ table. That’s all you need to know about Post Meta for now.&lt;/p&gt;

&lt;p&gt;Now let’s take a look at what Custom Post Types are.&lt;/p&gt;

&lt;p&gt;When you look at the ‘wp_posts’ table, you’ll notice that every entry has a ‘post_type’. So we can have a Post, a Page, Attachments, Company and Images etc, they’re all considered Post Types. Where the “Company” is actually a Post Type we’ve registered in our theme.&lt;/p&gt;

&lt;p&gt;This PHP snippet below is placed within the functions.php, file inside your theme directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// register a custom post type "company"
&amp;lt;?php
function add_cpt()
{
    $args = array(
    'labels' =&amp;gt; array(
    'name' =&amp;gt; 'Companies',
    'singular_name' =&amp;gt; 'Company',
    ),
    'hierarchical' =&amp;gt; true,
    'public' =&amp;gt; true,
    'has_archive' =&amp;gt; true,
    'menu-icon' =&amp;gt; 'dashicons-building',
    'show_in_rest' =&amp;gt; true,
    'supports' =&amp;gt; array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments',),
    'taxonomies' =&amp;gt; array('category',),
    );
register_post_type('company', $args);

}

add_action('init','add_cpt');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Custom Post Type is registered correctly, you will find the “Companies” in the admin menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnnGyDdh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A1W85kZAffZGdLBLygNyZ4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnnGyDdh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A1W85kZAffZGdLBLygNyZ4A.png" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch the video below for more info.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=fFz6OTeoiMI"&gt;https://www.youtube.com/watch?v=fFz6OTeoiMI&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Register a Custom Post Type in ACF
&lt;/h3&gt;

&lt;p&gt;If you’d like to follow along, then make sure you’ve got the &lt;a href="http://wordpress.org/plugins/advanced-custom-fields/"&gt;ACF plugin&lt;/a&gt; v6.1 or later installed and active.&lt;/p&gt;

&lt;p&gt;Continue by visiting ACF -&amp;gt; Post Types in your Admin Back-end, here you’ll see a screen with the basic settings for a new Custom Post Type (CPT). Fill the Plural Label, Singular Label and Post Type Key. This is also the bare minimum you’d need to create a post type.&lt;/p&gt;

&lt;p&gt;For my example, I’ll call my Plural Label as “Companies”, Singular label as “Company”, and my post key as “company”. You should feel free to create whatever you’d like here, the steps are pretty much the same.&lt;/p&gt;

&lt;p&gt;Let’s set some advanced configuration for your new Content Type. On the same creation screen, switch on the “Advanced Configuration” toggle to show the settings. Under the ‘Supports’ heading check for various content editor features, and tick or un-tick to enable and disable them. For my case I’ve unchecked “Editor”, as I don’t plan on using it.&lt;/p&gt;

&lt;p&gt;Switch to the Visibility tab and in the Menu icon field, add in the Dashicons class name. This isn’t critical but I recommend having some unique icon in the back-end, and if you’re building the site for a client then it looks more professional with the small extra touches. See the ‘Permissions’ tab for and turn on “Delete With User”. As in my case if a user no longer has a profile. Then I won’t allow them to have a business listed. Lastly, click “Save changes” and that’s it. You’ve just created a new Custom Post Type.&lt;/p&gt;

&lt;p&gt;You can read more about CPT’s in the &lt;a href="https://www.advancedcustomfields.com/resources/registering-a-custom-post-type/"&gt;official ACF article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding ACF fields to my CPT
&lt;/h3&gt;

&lt;p&gt;Alright, there you have a Custom Post Type, now what?&lt;/p&gt;

&lt;p&gt;We need some extra fields, as you know by now, we’re not just creating CPT’s for the fun of it, we’re doing it with a purpose. As a reminder the purpose is to have custom content types, for specific use cases and to keep content organized in the back-end.&lt;/p&gt;

&lt;p&gt;Let’s create some Advanced Custom Fields (ACF) that we’ll assign to our ‘Company’ CPT.&lt;/p&gt;

&lt;p&gt;Head over to the ACF -&amp;gt; Field Groups, click “Add New” and fill in the Field Group name (mine is called ‘Details’). Add a field with the “Add Field” button, select your Field Type, in my case I’m selecting “ &lt;a href="https://wplake.org/blog/display-acf-image-field-with-shortcode/"&gt;Image&lt;/a&gt; “ with the field label “Logo”, with Return Format as “Image ID”.&lt;/p&gt;

&lt;p&gt;Each “Company” really needs a Map too, so I’ll add a Google Map field, along with the User field. The intention is for a ‘company’ to get more exposure, and so that someone can find them with the map. The User field will be for associating the ‘company’ to a profile, and then also add a URL field for the company website. Lastly, in the Settings under “Location Rules” select ‘Post Type’, ‘is equal to ‘Company’ (Note: Select your CPT, if you’ve got another).&lt;/p&gt;

&lt;p&gt;Important note: &lt;a href="https://wplake.org/blog/acf-google-map-field/"&gt;Google Maps API should be registered&lt;/a&gt; or the ACF Google Map field won’t work or display.&lt;/p&gt;

&lt;p&gt;As a base set up for a ‘Company’, I’d say this is pretty solid. Any more ideas can be added later as the site grows, so let’s not add too many fields just yet. Remember to Save changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJFofSgT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AREvZLOdt1qmSoBhQfXLnfQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJFofSgT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AREvZLOdt1qmSoBhQfXLnfQ.png" width="800" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;ACF fields with location rules for Custom Post Type.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Display a set of Posts with PHP code
&lt;/h3&gt;

&lt;p&gt;Now that we’ve got all the groundwork ready, we need to display the CPT items.&lt;/p&gt;

&lt;p&gt;Each Post Type has a template to display the associated information of a single item, but in this article we’re going to focus on displaying multiple items with their fields in a grid.&lt;/p&gt;

&lt;p&gt;Now, you may know it’s quite a complex thing to display Posts with code, so I won’t blame you for skipping to the next section on how to display Posts with a shortcode.&lt;/p&gt;

&lt;p&gt;For the PHP code to work, you’ll need to follow closely, and pay close attention to the field names you’ve defined when you added your fields to your Custom Post Type.&lt;/p&gt;

&lt;p&gt;Here’s an example of a Custom Post Type for ‘Companies’, with a filter to show posts if the “Since date” year is bigger than 2010.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

$queryArgs = [
    // todo use your post type here
    'post_type' =&amp;gt; 'company',
    'post_status' =&amp;gt; 'publish',
    // todo maximum amount of posts, use -1 to set unlimited
    'posts_per_page' =&amp;gt; 5,
    // todo type of order
    'order' =&amp;gt; 'DESC',
    // todo order field
    'orderby' =&amp;gt; 'date',
    // todo use your fields
    'meta_query' =&amp;gt; [
        [
            'key' =&amp;gt; 'since',
            'value' =&amp;gt; 2010,
            'compare' =&amp;gt; '&amp;gt;',
            'type' =&amp;gt; 'numeric',
        ],
    ],
];

// SQL query will be executed during this line
$query = new WP_Query($queryArgs);

// @var WP_Posts[]
$posts = $query-&amp;gt;get_posts();

echo "&amp;lt;div class='companies'&amp;gt;";
foreach ($posts as $post) {
    $postId = $post-&amp;gt;ID;
    $title = get_the_title($postId);
    $description = get_the_excerpt($postId);
    $since = get_field('since', $postId);
    ?&amp;gt;
    &amp;lt;div class="companies__company company"&amp;gt;
        &amp;lt;h2 class="company__title"&amp;gt;&amp;lt;?php
            echo $title ?&amp;gt;&amp;lt;/h2&amp;gt;
        &amp;lt;div class="company__description"&amp;gt;&amp;lt;?php
            echo $description ?&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div class="company__about"&amp;gt;
            &amp;lt;p class="company__field-label"&amp;gt;Since:&amp;lt;/p&amp;gt;
            &amp;lt;p class="company__field-value"&amp;gt;
                &amp;lt;?php
                echo $since ?&amp;gt;
            &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;?php
}
echo "&amp;lt;/div&amp;gt;";
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, time for a summary.&lt;/p&gt;

&lt;p&gt;When you want to display a custom set of posts you will need to a) write a &lt;a href="https://developer.wordpress.org/reference/classes/wp_query"&gt;query&lt;/a&gt;, then b) get the field data, and c) write the necessary HTML markup to display the posts. It’s all pretty flexible, and you can do great things with this method, but it also requires a lot of time and effort. And of course some PHP knowledge, so let’s rather focus on an easier way without the drawbacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Display a set of Posts with a shortcode
&lt;/h3&gt;

&lt;p&gt;At this point you’ll need to install and activate the &lt;a href="https://wordpress.org/plugins/acf-views/"&gt;ACF Views plugin&lt;/a&gt;, because it’s really the power house of features in displaying Posts with their ACF fields without having to write the code.&lt;/p&gt;

&lt;p&gt;Visit the ACF Views link in your admin backend, and click “Add New” to create an ACF View. We’ll use this to assign fields from our Company CPT. I’ve named my ACF View “company stub”.&lt;/p&gt;

&lt;p&gt;Next, in the Fields section, assign the fields by selecting them from the drop down list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kFaHxSeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AoCXyrVJg701FpIOFezV1Og.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kFaHxSeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AoCXyrVJg701FpIOFezV1Og.png" width="800" height="491"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Assigning the ACF fields to your ACF View.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Save your ACF View by clicking ‘Publish’.&lt;/p&gt;

&lt;p&gt;We’re done with assigning the fields at this stage, you can edit your ACF View at any time, to add or remove fields. (Note: we’re not copying the ACF Views shortcode, we’re assigning it to our ACF Card)&lt;/p&gt;
&lt;h4&gt;
  
  
  Creating an ACF Card
&lt;/h4&gt;

&lt;p&gt;Visit the ACF Cards and click “Add New”, name your card, in my case I’ve named it “Company list”, scroll down and assign your ACF View created earlier. This View will be used to display your Post items. Now switch to the ‘Filters’ tab and for Post Type, choose ‘Company’ or your CPT name, next, in the ‘Sort’ tab, select Title and keep “Ascending” selected for Sort order.&lt;/p&gt;

&lt;p&gt;Click “Publish” to save and publish your ACF Card. Copy the shortcode to clipboard, we’ll paste it on our page in a minute.&lt;/p&gt;

&lt;p&gt;Finally it’s time to create a page where you’ll display the list of posts for Company CPT.&lt;/p&gt;

&lt;p&gt;Visit the “Pages” list and “Add New” page, call it whatever you’d like. But remember to make it clear and user friendly. I’ve kept mine simple for clarity, so just “Companies”.&lt;/p&gt;

&lt;p&gt;If you’re using the Gutenberg editor, then add a ‘shortcode’ block, paste your ACF Card shortcode from earlier, otherwise paste your shortcode anywhere in the content. Click “Publish” or “Save draft”. We’ll come back to this page in a bit.&lt;/p&gt;
&lt;h4&gt;
  
  
  Fill the fields
&lt;/h4&gt;

&lt;p&gt;Did we forget something?&lt;/p&gt;

&lt;p&gt;Yes, yes we did, we have to create some ‘company’ posts, at least some dummy ‘company’ items to test if everything is working as expected.&lt;/p&gt;

&lt;p&gt;Head over to “Companies” in the admin list and add posts, the same way you’d usually do when creating Blog posts.&lt;/p&gt;

&lt;p&gt;When you add your first ‘Company’ post, the first thing you would notice is that it looks like a normal post edit screen, but with the extra ACF fields that were added.&lt;/p&gt;

&lt;p&gt;Once you’ve created some ‘Company’ posts, go back to the Pages list, find your page (the page where you pasted the ACF Cards shortcode) and view it to see the results. You should have a list of posts with the fields you’ve filled displayed.&lt;/p&gt;

&lt;p&gt;All you have to do now is add in some styles with CSS. So, go back to your ACF Views item, switch to the ‘Advanced tab” and add your styles in the CSS code field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1aHnAILH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AbeJbHfqGrehm5gCRxMvDiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1aHnAILH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AbeJbHfqGrehm5gCRxMvDiw.png" width="800" height="286"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A set of ‘company’ custom post types displayed in a grid.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Create a list or grid of posts
&lt;/h4&gt;

&lt;p&gt;To create a grid of custom post type items, like above, you could use the following CSS, replacing the tags with your field names (if needed);&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* style the cpt items, paste in ACF Views item, in CSS code field */
#view {
 width: 30%;
 border-radius: 24px;
 border: 1px solid #f4f4f4;
 padding: 20px;
 margin-bottom: 30px;
 margin-right: 20px;
}

.acf-view__logo img {
 max-height: 40px;
 position: absolute;
 margin-top: 10px;
}

.acf-view__post_title_link {
  font-size: 24px;
  padding-top: 10px;
}

.acf-view__thumbnail_id img {
 width: 100%;
}

/* show card items in one row, paste in ACF Cards item, in CSS code field */
#card__items {
 display: flex;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you could opt for a easier, more responsive route, especially if you’re still learning CSS. Visit your ACF Card item, switch to the ‘Layout’ tab.&lt;/p&gt;

&lt;p&gt;This tab enables you to add rules, for mobile and other desktop screen sizes. Then it adds the CSS into the CSS code field in the ‘Advanced’ tab. It has options for different types of layouts, like “Row”, “Column” or “Grid”, with easy to define gap size and amount of columns for grid. In other words, you could have 3 per row for desktop, 5 in a row for larger screens, and then only 2 in a row for mobile, making sure it always looks neat and uncluttered on the screen.&lt;/p&gt;

&lt;p&gt;Furthermore, if you’d like to show “Company” items, and there are some special behavior that you need to include, like having “Featured companies” which show at the top of the list, or ‘companies’ with different tiers, then you could filter by those field meta. Just include an extra section on your page (or Gutenberg block) and so forth, following a similar approach as below, where you filter by meta or taxonomy depending on your requirements.&lt;/p&gt;

&lt;p&gt;Let’s see where you could add these filters.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sort and Filter by Taxonomy or ACF field meta
&lt;/h4&gt;

&lt;p&gt;When using &lt;a href="https://wplake.org/acf-views-pro/"&gt;ACF Views Pro&lt;/a&gt;, you get access to the Taxonomy Filters and Meta Filters, where you can add rules to filter by a taxonomy query, and also to add rules for filtering by a fields’ meta. In other words, if you have ‘company’ CPT items, and they have “Industry” categories, you could have a page to display only companies for a specific industry category.&lt;/p&gt;

&lt;p&gt;Powerful meta filters allow you to do a bit more. Let’s take an example, you have ‘company’ CPT items and you’d like to show all items that have the same owner (User), then you could use $posts$.user rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;In this article we’ve shown you how to register new Content Post Types, and how to add extra ACF fields and finally, how to display a list or grid of posts, with their ACF fields on a page.&lt;/p&gt;

&lt;p&gt;We hope this article is useful to you and that you’ll be creating more post grids and blocks of content with different CPT items for different purposes.&lt;/p&gt;

&lt;p&gt;…and remember the main rule is to have fun, and to use as few plugins as possible.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://wplake.org/blog/custom-post-types/"&gt;&lt;em&gt;https://wplake.org&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on June 7, 2023.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>customposttypes</category>
      <category>webdesign</category>
      <category>wordpresswebdevelopm</category>
      <category>wordpressplugins</category>
    </item>
    <item>
      <title>How to use and display the ACF Google Map field</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Mon, 01 May 2023 11:32:00 +0000</pubDate>
      <link>https://dev.to/wplake/how-to-use-and-display-the-acf-google-map-field-wplake-48ee</link>
      <guid>https://dev.to/wplake/how-to-use-and-display-the-acf-google-map-field-wplake-48ee</guid>
      <description>&lt;h3&gt;
  
  
  How to use and display the ACF Google Map field — WPLake
&lt;/h3&gt;

&lt;p&gt;Learn about the ACF Google Map field, the field’s options, and two ways to display it, one without any coding. Read about what happens behind the scenes and how information is stored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7j6xaGMl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALTarezdd8oJ9ZJmHU1iBQQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7j6xaGMl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALTarezdd8oJ9ZJmHU1iBQQ.jpeg" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ACF Google Map field allows you to display… You guessed it, a Google Map.&lt;/p&gt;

&lt;h3&gt;
  
  
  About the Google Map field
&lt;/h3&gt;

&lt;p&gt;The ACF Google Map field stores an address, that is then turned into a map on the frontend.&lt;/p&gt;

&lt;p&gt;The Google Map field is one of many ACF field types, there are more than 35 field types already and still somehow that number is still growing.&lt;/p&gt;

&lt;p&gt;ACF Google Map field is like an embed field, where you don’t have direct control over the content, but still have some control on how it displays and in what size visitors see the map and the all important location marker.&lt;/p&gt;

&lt;p&gt;There are many reasons why you’d want a Google Map on your post or page. You can show the location of a business, e.g. a Business directory. Perhaps you have an online store and one of your delivery options is “Free pickup”, buyers may need an actual map to find their way.&lt;/p&gt;

&lt;h4&gt;
  
  
  Embed the map the old way
&lt;/h4&gt;

&lt;p&gt;You could embed the map directly. As you may know, in the past and still today it was quite easy to just go to Google Maps or Visit maps.google.com, perhaps even with Google Earth it was possible, but let’s not lose focus here. Google Maps is simply one of those technological things we can’t live without, enough said.&lt;/p&gt;

&lt;p&gt;To embed a Google Map directly, visit a location on Google Maps, select ‘share’ and then switch to ‘Embed the map’, select a size and then ‘Copy HTML’ and paste it in place.&lt;/p&gt;

&lt;p&gt;Example of HTML embed code for Somerset West, City Center in South Africa;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!--a map that's directly embedded--&amp;gt;
&amp;lt;iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d105767.5598163991!2d18.746078233330014!3d-34.063457740807046!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x1dcdb565fbc4f737%3A0x42f889f307a16bb9!2sSomerset%20West%2C%20Cape%20Town!5e0!3m2!1sen!2sza!4v1682081617435!5m2!1sen!2sza" width="600" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Embedding maps directly has of course many drawbacks, the main points are; it doesn’t provide any settings or UI to editors, and if you need to add a map to for example a CPT, different locations can’t be used on different pages in this way. At least not without a lot of manual work and effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Field settings
&lt;/h3&gt;

&lt;p&gt;With the Google Map field you have some settings when creating the field. You can define latitude and longitude coordinates to center the initial map around that point, you can set the zoom level and customize the height of the Google Map.&lt;/p&gt;

&lt;p&gt;These settings are useful and help on the post/page edit screen, it even saves time, and helps with consistency across the site post and pages, so the maps look similar and have the same height.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mryUAoLg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AKG4JkZ5NiGiaZcDzUT4T9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mryUAoLg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AKG4JkZ5NiGiaZcDzUT4T9g.png" width="800" height="438"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Google Map field settings, define the zoom level and height.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Behind the scenes in ACF
&lt;/h3&gt;

&lt;p&gt;Behind the scenes, the ACF Google Map field type allows editors to see and interact with a live map to define a marker. It uses the Google Maps JS API to provide address autocomplete searching and reverse geocoding lookup.&lt;/p&gt;

&lt;p&gt;Information about your choice is returned as an array, and it’s saved into the meta field as a serialized value. It contains several keys, like ‘address’, ‘lat’, ‘lng’ and ‘country’, see below for an example of the meta value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a:10:{s:7:"address";s:10:"London, UK";
s:3:"lat";d:51.5072178;s:3:"lng";d:-0.1275862;
s:4:"zoom";i:8;s:8:"place_id";s:27:"ChIJdd4hrwug2EcRmSrV3Vo6llI";
s:4:"name";s:6:"London";s:4:"city";s:6:"London";
s:5:"state";s:7:"England";
s:7:"country";s:14:"United Kingdom";
s:13:"country_short";s:2:"GB";}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this data as you see fit, from the most common usage, like displaying the address on the contact page, to using it in some API request as an argument.&lt;/p&gt;

&lt;p&gt;But don’t forget the fact that ACF doesn’t provide any frontend for the map. This means that ACF includes the Maps’ API only for editors in the backend. Therefore you’d need to load Google Maps API, and initialize the map yourself for it to appear on the frontend of your site.&lt;/p&gt;

&lt;p&gt;Doing it in this was does provide flexibility in the usage of the data, as you can display the map using an alternative vendor (like OpenStreetMap), but also usually results in more time spent on integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Register Google Map JS API key
&lt;/h3&gt;

&lt;p&gt;To use the Google Maps JS API you’d need to register an API key. The Google Map field requires the following APIs; Maps JavaScript API, Geocoding API and Places API.&lt;/p&gt;

&lt;p&gt;Follow Google’s API instructions to &lt;a href="https://developers.google.com/maps/documentation/javascript/get-api-key"&gt;Get your API key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Register your Google Map API on your site, using one of these methods;&lt;br&gt;&lt;br&gt;
Paste it in your theme’s functions.php template file, replacing ‘xxx’ with your key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Method 1: Filter.
function my_acf_google_map_api( $api ){
    $api['key'] = 'xxx';
    return $api;
}
add_filter('acf/fields/google_map/api', 'my_acf_google_map_api');

// Method 2: Setting.
function my_acf_init() {
    acf_update_setting('google_api_key', 'xxx');
}
add_action('acf/init', 'my_acf_init');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QhualpHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A22_4mSm6PcmtR0ymJSv0CA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QhualpHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A22_4mSm6PcmtR0ymJSv0CA.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;It’s often tough to navigate complex code and know where to paste it.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Display ACF Google Map with PHP code
&lt;/h3&gt;

&lt;p&gt;Be sure to Register the Google Map JS API key as mentioned above. To display a live map, we need to transfer the field data (about the selected marker) to the front JS. Then we need to query the Google Maps API, and ensure the map is uninitialized. Below we provide a universal code, that suits both cases, for when you either have a map marker or not.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Create the markup
&lt;/h3&gt;

&lt;p&gt;Add the code below into your theme template file or to your functions.php as a code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

// TODO your field name here
$mapInfo = get_field("map");

$zoom = $mapInfo['zoom'] ?? '16';
$lat = $mapInfo['lat'] ?? '';
$lng = $mapInfo['lng'] ?? '';

// zoom level - gets from every specific map (when admins zoom out and saves a page, the zoom is also saved)
printf(
    '&amp;lt;div class="my-map" style="width:100%%;height:400px;" data-zoom="%s"&amp;gt;',
    $zoom
);

printf(
    '&amp;lt;div class="my-map__marker" data-lat="%s" data-lng="%s"&amp;gt;&amp;lt;/div&amp;gt;',
    esc_attr($lat),
    esc_attr($lng)
);

echo "&amp;lt;/div&amp;gt;";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Query Google Maps JS
&lt;/h3&gt;

&lt;p&gt;Use the code below to load the Maps JS API for all pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

add_action('wp_footer', function () {
    $apiData = apply_filters('acf/fields/google_map/api', []);

    $key = $apiData['key'] ?? '';

    $key = !$key ?
        acf_get_setting('google_api_key') :
        $key;

    if (!$key) {
        return;
    }

    wp_enqueue_script(
        'google-maps',
        sprintf('https://maps.googleapis.com/maps/api/js?key=%s&amp;amp;callback=googleMapsCallback', $key),
        null,
        true
    );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet uses the same Google API key that you’ve defined for ACF for site admins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pro tip: For better performance, we recommend adding some checks, to make sure this only loads on pages where the map exists. E.g. Limit it to a single page by URL with&lt;/em&gt; &lt;a href="https://developer.wordpress.org/reference/functions/is_page/"&gt;&lt;em&gt;is_page&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, or to the CPT type with&lt;/em&gt; &lt;a href="https://developer.wordpress.org/reference/functions/is_singular/"&gt;&lt;em&gt;is_singular&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Initialize the map
&lt;/h3&gt;

&lt;p&gt;Continue by adding the following JS code to the target page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Map {
    constructor(element) {
        this.element = element
        this.map = null
        this.mapMarkers = []
    }

    readMarkers() {
        // TODO replace the selector if you've changed it in the markup
        this.element.querySelectorAll('.my-map__marker').forEach((markerElement) =&amp;gt; {
            let lat = markerElement.dataset.hasOwnProperty('lat') ?
                markerElement.dataset['lat'] :
                0
            let lng = markerElement.dataset.hasOwnProperty('lng') ?
                markerElement.dataset['lng'] :
                0

            this.mapMarkers.push({
                lat: parseFloat(lat),
                lng: parseFloat(lng),
            })

            markerElement.remove()
        })
    }

    createMap() {
        let mapArgs = {
            zoom: parseInt(this.element.dataset.hasOwnProperty('zoom') ?
                this.element.dataset['zoom'] :
                16),
            mapTypeId: window.google.maps.MapTypeId.ROADMAP,
        }
        this.map = new window.google.maps.Map(this.element, mapArgs)
    }

    createMarkers() {
        this.mapMarkers.forEach((marker) =&amp;gt; {
            new window.google.maps.Marker({
                position: marker,
                map: this.map,
            })
        })
    }

    centerMap() {
        // Create map boundaries from all map markers.
        let bounds = new window.google.maps.LatLngBounds()

        this.mapMarkers.forEach((marker) =&amp;gt; {
            bounds.extend({
                lat: marker.lat,
                lng: marker.lng,
            })
        })

        if (1 === this.mapMarkers.length) {
            this.map.setCenter(bounds.getCenter())
        } else {
            this.map.fitBounds(bounds)
        }
    }

    init() {
        if (!window.hasOwnProperty('google') ||
            !window.google.hasOwnProperty('maps') ||
            !window.google.maps.hasOwnProperty('Map') ||
            !window.google.maps.hasOwnProperty('Marker') ||
            !window.google.maps.hasOwnProperty('LatLngBounds') ||
            !window.google.maps.hasOwnProperty('MapTypeId') ||
            !window.google.maps.MapTypeId.hasOwnProperty('ROADMAP')) {
            console.log('Google maps isn\'t available')
            return
        }

        // before the map initialization, because during creation HTML is changed
        this.readMarkers()
        this.createMap()
        this.createMarkers()
        this.centerMap()
    }
}

class Maps {
    constructor() {
        this.isMapsLoaded = false
        this.mapsToInit = []

        // TODO change to yours if you've defined own callback (for https://maps.googleapis.com/maps/api...)
        window.googleMapsCallback = this.mapsLoadedCallback.bind(this)

        'loading' !== document.readyState ?
            this.setup() :
            window.addEventListener('DOMContentLoaded', this.setup.bind(this))
    }

    setup() {
        const observer = new MutationObserver((records, observer) =&amp;gt; {
            for (let record of records) {
                record.addedNodes.forEach((addedNode) =&amp;gt; {
                    this.addListeners(addedNode)
                })
            }
        })
        observer.observe(document.body, {
            childList: true,
            subtree: true,
        })

        this.addListeners(document.body)
    }

    mapsLoadedCallback() {
        this.isMapsLoaded = true

        this.mapsToInit.forEach((map) =&amp;gt; {
            map.init()
        })

        this.mapsToInit = []
    }

    addListeners(element) {
        if (Node.ELEMENT_NODE !== element.nodeType) {
            return
        }

        // TODO replace the selector if you've changed it in the markup

        element.querySelectorAll('.my-map').forEach((mapElement) =&amp;gt; {
            let map = new Map(mapElement)

            if (!this.isMapsLoaded) {
                this.mapsToInit.push(map)

                return
            }

            map.init()
        })
    }

}

new Maps()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snippet above will initialize all the maps on your page, that have the markup from the first step. If you made changes to the markup classes, don’t forget to reflect them here too (see TODO).&lt;/p&gt;

&lt;p&gt;This snippet uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"&gt;MutationObserver&lt;/a&gt; feature, so it will work even in case your maps’ info was added dynamically, e.g. after an ajax request.&lt;/p&gt;

&lt;p&gt;You can read more about the Google Map field in the &lt;a href="https://www.advancedcustomfields.com/resources/google-map/"&gt;official ACF article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7BGSQ3E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AB8NQhJtBHNeM_E4Slvgzow.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7BGSQ3E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AB8NQhJtBHNeM_E4Slvgzow.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The road to endless possibilities.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Display ACF Google Map with a shortcode
&lt;/h3&gt;

&lt;p&gt;Using the code method is quite complex and becomes a little worse when you want to do more with a Google Map, it’s worth mentioning that there are some drawbacks, of which the biggest is that you would need to write and add several different code snippets (see above PHP way).&lt;/p&gt;

&lt;p&gt;However, there is an alternative way to solve this without so much code. It will still allow you to display the Google Map and you can have markers too. It’s done with the incredible &lt;a href="https://wordpress.org/plugins/acf-views/"&gt;ACF Views plugin&lt;/a&gt;. It was built for handling complex field types, read more about &lt;a href="https://wplake.org/blog/display-acf-fields-beautifully-and-without-coding/"&gt;the shortcode approach here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ACF Views will create the HTML markup, so you won’t have to. All you do is install it, active it and start creating. You select fields from a dropdown, one by one, each field has some settings, then once you publish your ACF View you get shortcodes, each with its own purpose. Copy the shortcode in place on a post or page, that’s it.&lt;/p&gt;

&lt;p&gt;Read more on how to &lt;a href="https://docs.acfviews.com/guides/acf-views/fields/google-map"&gt;display Google Map fields without coding&lt;/a&gt; and the field options available.&lt;/p&gt;

&lt;h3&gt;
  
  
  How-to Step-by-step
&lt;/h3&gt;

&lt;p&gt;Follow along by installing and activating the &lt;a href="https://wordpress.org/plugins/acf-views/"&gt;ACF Views plugin&lt;/a&gt; on your WordPress website. You’ll also need &lt;a href="https://wordpress.org/plugins/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt; installed and active, both plugins are available free of charge from &lt;a href="https://wordpress.org/plugins/"&gt;Wordpress.org&lt;/a&gt; plugin directory.&lt;/p&gt;

&lt;p&gt;Now that you’re ready, let’s continue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1. Define Google Map API key
&lt;/h3&gt;

&lt;p&gt;Be sure to Register the Google Map JS API key as mentioned earlier.&lt;/p&gt;

&lt;p&gt;See the ACF Views &lt;a href="https://docs.acfviews.com/guides/acf-views/fields/google-map"&gt;Google Map field&lt;/a&gt; for up-to-date documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2. Create an ACF View
&lt;/h3&gt;

&lt;p&gt;When you activate the ACF Views plugin a new item appears in the admin menu, called “ACF Views”. The item has several sub-items, but in our case, we’ll be working only with the one called “ACF Views”.&lt;/p&gt;

&lt;p&gt;Click that menu item to open the ACF Views page and then click the ‘Add New’ button to create a &lt;em&gt;View&lt;/em&gt;. On the new page give your &lt;em&gt;View&lt;/em&gt; a name, it can be anything that describes the &lt;em&gt;View&lt;/em&gt;. I’ve called my &lt;em&gt;View&lt;/em&gt; “business locations”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3. Assigning fields
&lt;/h3&gt;

&lt;p&gt;It’s time to assign the Google Map field to your &lt;em&gt;View&lt;/em&gt;. Click on the ‘Add Field’ button and select your ‘Group’ from the dropdown. In my case, I’ve called the group “Map Location”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ubq-6upm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AL5nfiKj7eOT0JJS4TJqz7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ubq-6upm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AL5nfiKj7eOT0JJS4TJqz7g.png" width="800" height="242"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Assign the ACF Google Map field in your ACF View.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then continue to select the target field from the list. I’ve selected “Map” which is what my Google Map field is called. See Field Options tab for more settings and to hide map or change address format (only in &lt;a href="https://wplake.org/acf-views-pro/"&gt;ACF Views Pro&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Now you can ‘Publish’ your ACF View or click ‘Update’ (if you’ve saved before), then copy the first shortcode and paste it in place, anywhere in the post or page content area. For Gutenberg editor, use the “shortcode” block. Save your page and view the post/page to see the results.&lt;/p&gt;

&lt;p&gt;That’s it, you’re done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k8yoXNQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A4JpyB1BX3S1a_eovQUt_XA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k8yoXNQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A4JpyB1BX3S1a_eovQUt_XA.png" width="800" height="267"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A Google Map with location marker.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;In this tutorial we’ve shown you how to use and display an ACF Google Map field in two ways with an added bonus for native Google Map Embed, the first with coding and the more practical and easier way with using a shortcode.&lt;/p&gt;

&lt;p&gt;An ACF View can contain any number of fields of different types, which means you could extend your &lt;em&gt;View&lt;/em&gt; at any time, ACF Views &lt;a href="https://docs.acfviews.com/getting-started/supported-field-types"&gt;supports all available field types&lt;/a&gt; with &lt;a href="https://wplake.org/acf-views-pro/"&gt;extended support&lt;/a&gt; for complex fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2w01obu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Ahu0v7gL7ieWnNq2xz9JGnw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2w01obu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Ahu0v7gL7ieWnNq2xz9JGnw.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Different types of maps on a stand&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To get more info about the plugin we’ve used in our shortcode example, visit the &lt;a href="https://wplake.org/acf-views/"&gt;official plugin page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Maps don’t always need to be associated with a business, you can create literally any type of map.&lt;/p&gt;

&lt;p&gt;Have fun out there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://wplake.org/blog/acf-google-map-field/"&gt;&lt;em&gt;https://wplake.org&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on May 1, 2023.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpressplugins</category>
      <category>wordpresswebdevelopm</category>
      <category>php</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>How to use and display the ACF Repeater field</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Mon, 03 Apr 2023 14:10:49 +0000</pubDate>
      <link>https://dev.to/wplake/how-to-use-and-display-the-acf-repeater-field-10ii</link>
      <guid>https://dev.to/wplake/how-to-use-and-display-the-acf-repeater-field-10ii</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IwJdt1nG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AgOFLovm36m_sEBvANmkLAg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IwJdt1nG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AgOFLovm36m_sEBvANmkLAg.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  About the Repeater field
&lt;/h3&gt;

&lt;p&gt;The ACF Repeater field is one of many ACF field types and is known as a complex field, mainly due to the fact it allows you to add sub fields that can be repeated over and over again when editing content. So it’s true to its name.&lt;/p&gt;

&lt;p&gt;The Repeater field is similar to the ACF Group field in that allows you to “group” sub fields, although with the Group field you can’t “repeat” the sub fields, only one set of sub fields is allowed.&lt;/p&gt;

&lt;p&gt;A huge benefit of the Repeater field is that you don’t need to create another Custom Post Type (CPT), yet you can still display a list of items.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout options
&lt;/h3&gt;

&lt;p&gt;The Repeater field has Layout settings with 3 options, these are Table, Block and Row. Whichever you’ve chosen here will affect the look of the field for admins and editors.&lt;/p&gt;

&lt;p&gt;When choosing the “Table” layout you’ll be presented with a Table of sub fields on the edit screen, with labels in the table header. When choosing the Block layout the sub fields are displayed in blocks, one after the other, and with Row, sub fields are displayed in a two column table, with labels in the first column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0CRDhKPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AlFfzb9iSbINmeEOkyOOB7A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0CRDhKPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AlFfzb9iSbINmeEOkyOOB7A.jpeg" width="800" height="195"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The three types of layout options for the ACF Repeater field. Fltr: Block Layout, Table Layout and Row Layout&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pagination in the field creation step, when switched on is useful for when you have a large number of repeater rows, this helps to improve the performance while saving (before, saving even just 10 rows took some time).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lZgpFKu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AElGBj_PA-EdP78x2HS354w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lZgpFKu---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AElGBj_PA-EdP78x2HS354w.png" width="800" height="195"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Repeater pagination for a large number of rows to improve performance.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Behind the scenes in ACF
&lt;/h3&gt;

&lt;p&gt;Behind the scenes, the ACF Repeater field stores the data in the Custom Fields’ Meta of the current page (or post).&lt;/p&gt;

&lt;p&gt;As you know, the repeater field allows you to have multiple rows of the same sub fields and stores it within a single main field. It sounds simple enough, but it could lead you to think that in the database, it’s also saved as a single meta field, when really it isn’t.&lt;/p&gt;
&lt;h3&gt;
  
  
  Field Search Queries
&lt;/h3&gt;

&lt;p&gt;Remember, pay close attention to the fact that, users and developers should have the option to make search queries by this field. E.g. You have several posts, each with a repeater with several rows, and you need to find them all, how would you know the specific value exists in one of the rows? This task actually appears much more often than you may think.&lt;/p&gt;

&lt;p&gt;The Developers at Advanced Custom Fields (ACF) already thought about this, and decided to store each sub field in its own meta field, but with a special structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zHYTfOjA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/899/1%2AFsDIqJC7Boztby-W5q_clg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zHYTfOjA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/899/1%2AFsDIqJC7Boztby-W5q_clg.png" width="800" height="372"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Repeater sub fields in the WP meta table.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As seen above each sub field is prefixed with the row number, and the repeater name.&lt;/p&gt;

&lt;p&gt;So, for example, you named your repeater field ‘datasheets’, and your sub fields are called ‘datasheet’ and ‘attachment’, then in the database they’ll be stored in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasheets_{row-number}_datasheet and 
datasheets_{row-number}_attachment, 
where {row-number} begins from zero.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you “request” this field from ACF with the &lt;strong&gt;get_field&lt;/strong&gt; function, ACF does a trick and turns this structure into an array, so you can work in PHP in a similar fashion as with a plain array. In our example, if we need to get the sub field from the first row, we would be able to do this with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$datasheets = get_field('datasheets'); 
echo $datasheets[0]['datasheet']; 
echo $datasheets[0]['attachment'];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s important to know and understand this approach, as querying posts by repeater sub fields requires knowledge of the structure, to be able to write the correct WP_Query. You can learn how to create a WP_Query for repeater subfields in the official &lt;a href="https://www.advancedcustomfields.com/resources/query-posts-custom-fields/#4-sub-custom-field-values"&gt;Docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance
&lt;/h4&gt;

&lt;p&gt;The Repeater field is a useful feature that helps you store multiple data entries, but it also has a drawback, which is the search performance for larger datasets.&lt;/p&gt;

&lt;p&gt;So, although querying by sub fields is possible, it eats much more server resources and takes more time when compared to Custom Post Type (CPT) items and taxonomies.&lt;/p&gt;

&lt;p&gt;You won’t notice the difference in case you’re querying for several posts that have 5 to 10 rows each, but if we talk about a few hundred posts with lots of rows, then it’ll be much slower than the alternative CPT approach. Therefore, if you know there’ll be a lot of data, and you would need to query for them, consider creating a CPT instead. Creating CPT’s is possible with the ACF plugin since version 6.1 beta&lt;/p&gt;

&lt;h3&gt;
  
  
  Display ACF Repeater with PHP code
&lt;/h3&gt;

&lt;p&gt;Displaying Repeater sub fields means you’ll need to create HTML markup, for the fields and for the variables for when fields are filled.&lt;/p&gt;

&lt;p&gt;Here’s some examples of PHP code needed to display sub fields and a basic image slider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PHP code to display Repeater sub field values with the Basic loop.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$items = get_field('repeater');
// value can be null, convert in case the array is empty
$items = $items ?: [];

echo "&amp;lt;div class='view'&amp;gt;";
foreach ($items as $item) {
 printf("&amp;lt;div class='view__address'&amp;gt;%s&amp;lt;/div&amp;gt;", $item['address']);
}
echo "&amp;lt;/div&amp;gt;";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PHP code to display a basic image slider.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$items = get_field('repeater');
// value can be null, convert in case the array is empty
$items = $items ?: [];

echo "&amp;lt;ul class='slides'&amp;gt;";
foreach ($items as $item) {
 // Return Format of the image must be 'ID'
 $imageId = $item['image'];

 // skip if the image is not selected
 if (!$imageId) {
     continue;
 }

 // wp_get_attachment_image() will create HTML markup for the selected image (the img tag)
 printf("&amp;lt;div class='slides__slide'&amp;gt;%s&amp;lt;/div&amp;gt;", wp_get_attachment_image($imageId, 'full'));
}
echo "&amp;lt;/ul&amp;gt;";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the Repeater field in the &lt;a href="https://www.advancedcustomfields.com/resources/repeater/"&gt;official ACF article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gY0WcM2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AGcB8BLfNw_4Pb9foxtgzwQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gY0WcM2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AGcB8BLfNw_4Pb9foxtgzwQ.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in the more technical side of WordPress, consider reading &lt;a href="https://wplake.org/blog/what-must-know-good-wordpress-developer/"&gt;about what good WordPress developers must know&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Display ACF Repeater with a shortcode
&lt;/h3&gt;

&lt;p&gt;Using the code way to display the fields is quite flexible, but has &lt;a href="https://wplake.org/blog/display-acf-fields-beautifully-and-without-coding/#issues-with-displaying-acf-fields-via-code"&gt;many disadvantages&lt;/a&gt;;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It takes some time to write the code.&lt;/li&gt;
&lt;li&gt;You need to know the Return Types of the sub fields.&lt;/li&gt;
&lt;li&gt;Remembering specific key names for the response array.&lt;/li&gt;
&lt;li&gt;You need to manually find the correct page template in your theme and amend it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, there’s another way that allows you to display the ACF Repeater field, (and its sub fields) without having to write the code. The &lt;a href="https://wordpress.org/plugins/acf-views/"&gt;ACF Views plugin&lt;/a&gt; allows you to display ACF fields, and with ACF Views Pro, you can display complex fields which includes the Repeater field, thus making it super easy and saves tons of time.&lt;/p&gt;

&lt;p&gt;ACF Views takes care of the HTML markup. We only need to choose the ACF fields to display, then copy the auto-generated shortcode and paste it into any place on a page, that’s it, there’s nothing else we need to do except style the output.&lt;/p&gt;

&lt;p&gt;Watch a short video tutorial below on how to display ACF Repeater fields using the shortcode way.&lt;br&gt;&lt;br&gt;
There are more video tutorials on the &lt;a href="https://www.youtube.com/@acfviews"&gt;Official YouTube Channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Skip past the video for a full texted based, step-by-step guide for displaying the ACF Repeater.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/VON-Ew_FIW8"&gt;https://youtu.be/VON-Ew_FIW8&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How-to Step-by-step
&lt;/h3&gt;

&lt;p&gt;If you’d like to follow along, then please purchase, install and activate &lt;a href="https://wplake.org/acf-views-pro/"&gt;ACF Views Pro plugin&lt;/a&gt; on your WordPress website. You’ll also need to install and activate Advanced Custom Fields Pro to unlock the Repeater field type.&lt;/p&gt;

&lt;p&gt;Now that you’re ready, let’s continue.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Creating a View
&lt;/h4&gt;

&lt;p&gt;When you activate the ACF Views plugin a new item appears in the admin menu, called “ACF Views”. The item has several sub-items, but in our case, we’ll be working only with the one called “ACF Views”.&lt;/p&gt;

&lt;p&gt;Click that menu item to open the Views page and then click the ‘Add New’ button to create a &lt;em&gt;View&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On the new page give your &lt;em&gt;View&lt;/em&gt; a name, it can be anything that describes the &lt;em&gt;View&lt;/em&gt;. I’ve called my &lt;em&gt;View&lt;/em&gt; “download datasheets”.&lt;/p&gt;

&lt;h4&gt;
  
  
  Assigning fields
&lt;/h4&gt;

&lt;p&gt;It’s time to assign the Repeater field and subfields to your &lt;em&gt;View&lt;/em&gt;. Click on the ‘Add Field’ button and select your ‘Group’ from the dropdown. In my case, I’ve called the group “Download Datasheets”.&lt;/p&gt;

&lt;p&gt;Then continue to select the target field from the list. I’ve selected “Datasheet”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CvYPoF8k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Azt1T21gG_L-PiJevyUsKAw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CvYPoF8k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Azt1T21gG_L-PiJevyUsKAw.png" width="800" height="286"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Assigning ACF fields to your ACF View with a dropdown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that each field will have it’s field type shown in brackets. In this case you should see the type as “repeater”. Continue by switching to the “Sub fields” tab and then add your sub fields from the list, one at a time and then insert a label for each field as required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pro Tip: Add the first field, then duplicate the row and change the field to another. This saves time not having to first select the Field Group each time and then choosing the field.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yXtxFlGM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A30eIFs9GsCGaj5BZj5hFcA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yXtxFlGM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A30eIFs9GsCGaj5BZj5hFcA.png" width="800" height="488"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Assigning Sub fields to an ACF View.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click the “Publish” button to save and publish your &lt;em&gt;View&lt;/em&gt;. Shortcodes are then generated and shown on the right of the View edit screen. Read more about the different &lt;a href="https://docs.acfviews.com/guides/acf-views/basic/shortcode"&gt;shortcodes&lt;/a&gt;and their uses in the &lt;a href="https://docs.acfviews.com/"&gt;ACF Views docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Copy the first shortcode without the &lt;strong&gt;object-id&lt;/strong&gt; parameter to the clipboard.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;E.g. [acf_views view-id=”123" name=”Name of View”]&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2. Paste the shortcode in place
&lt;/h4&gt;

&lt;p&gt;Everything should now be ready to display your ACF Repeater field. Go to your target page (the page where you’ve located your ACF fields) and on the edit screen, paste the copied shortcode anywhere you’d like in the page content.&lt;/p&gt;

&lt;p&gt;In case you’re using the Gutenberg editor: click on the plus button in the top bar and choose the “Shortcode” block from the list, then paste your shortcode in the block.&lt;/p&gt;

&lt;p&gt;Now, all that’s left to do is to fill in the fields (sub fields really). Add more rows of sub fields as you need them.&lt;/p&gt;

&lt;p&gt;In my case I have a ‘Datasheet’ text field, and a file field I’ve called ‘Attachment’. I ended up using three Repeater rows for English, French and German datasheets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eI1Gb3Lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Aqjuda7jZoSnHpUNw10d4sQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eI1Gb3Lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Aqjuda7jZoSnHpUNw10d4sQ.png" width="800" height="433"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Rows of Repeated fields for my Field Group Datasheets.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Be sure to save your page by clicking “Update” or “Publish”, then visit your page. As a result and if you’ve done it all correctly then you should see the list of Repeater items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cdvP_Ogv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/982/1%2AniG45xSWFPnKPAklgow_pQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cdvP_Ogv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/982/1%2AniG45xSWFPnKPAklgow_pQ.png" width="800" height="119"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The final result of my Repeater Datasheets field group with CSS styles.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you don’t see your list of items, then go back and edit the page. For instance, make sure you’ve filled the fields and saved the page, if they are indeed filled, then go further back to your &lt;em&gt;View&lt;/em&gt; edit, were you assigned the fields. Then double check they’re assigned from the correct field group. Consequently if you’ve left the fields unfilled on the page or post then there’ll be nothing to display.&lt;/p&gt;

&lt;p&gt;Learn how to &lt;a href="https://wplake.org/blog/woocommerce-product-page-template-with-acf-fields/#how-to-use-acf-views-to-display-custom-field-values-on-product-pages"&gt;display repeater fields on WooCommerce product&lt;/a&gt; pages, so relevant information is seen by potential customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;In this tutorial we’ve shown you how to use and display ACF Repeater fields in two ways, first with coding and the more practical and easier way with using a shortcode.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;View&lt;/em&gt; can contain any number of fields of different types, which means you could extend your &lt;em&gt;View&lt;/em&gt; at any time, ACF Views supports all available field types with extended support for complex fields.&lt;/p&gt;

&lt;p&gt;Get more info about the plugin we used in our shortcode example, by visiting the &lt;a href="https://wplake.org/acf-views/"&gt;official plugin page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LYZPNdda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Azz8IIF1BRTKr3RilUr9iZg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LYZPNdda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Azz8IIF1BRTKr3RilUr9iZg.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only the sky’s the limit now with what you can achieve with WordPress, Advanced Custom Fields and ACF Views. Good Luck.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://wplake.org/blog/how-to-use-and-display-the-acf-repeater-field/"&gt;&lt;em&gt;https://wplake.org&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on April 3, 2023.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>wordpresswebdevelopm</category>
      <category>repeater</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>5 important things about WordPress Cron</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Tue, 28 Feb 2023 12:24:36 +0000</pubDate>
      <link>https://dev.to/wplake/5-important-things-about-wordpress-cron-48ep</link>
      <guid>https://dev.to/wplake/5-important-things-about-wordpress-cron-48ep</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.amazonaws.com%2Fuploads%2Farticles%2F3f8jvoxvt8eop4nr8mvm.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2F3f8jvoxvt8eop4nr8mvm.jpeg" alt="img" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WordPress Cron provides an easy way to schedule actions without taking care of the implementation. We review specific moments to help you manage Cron properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Cron?
&lt;/h3&gt;

&lt;p&gt;The Cron in short, is a way to run some tasks (or jobs if you will) on a schedule. For example, to keep the currency rates up-to-date on your website you need to update them once a day. Instead of doing it manually, you create a cron task and set it up with the frequency of once a day. Cron is widely used in many WordPress plugins and themes to keep information up-to-date.&lt;/p&gt;

&lt;h3&gt;
  
  
  System Cron VS WordPress Cron
&lt;/h3&gt;

&lt;p&gt;With WordPress you have 2 different ways to do it.&lt;/p&gt;

&lt;h4&gt;
  
  
  System Cron
&lt;/h4&gt;

&lt;p&gt;The first way is adding it in the &lt;a href="https://help.ubuntu.com/community/CronHowto" rel="noopener noreferrer"&gt;Linux system cron&lt;/a&gt; (this is on your hosting server). These tasks are in the &lt;strong&gt;/etc/crontab&lt;/strong&gt; file and with this approach being the most universal and most reliable way to run Cron tasks. Your server operating system (e.g. Linux, Ubuntu or RedHat) guarantees that tasks in our PHP files will be called as they were scheduled, regardless of any external events.&lt;/p&gt;

&lt;p&gt;Though it’s more universal and reliable, it has no integration with WordPress. This means if you call PHP files within your theme directly, then they won’t have access to any WordPress functions and the Database. Due to the fact that WordPress can’t load properly in this way. Furthermore, it’s not suitable for plugins, because if you’re using the plugin, you can’t simply set up the system Cron for all your clients.&lt;/p&gt;

&lt;h4&gt;
  
  
  WordPress Cron
&lt;/h4&gt;

&lt;p&gt;Then here WordPress Cron &lt;a href="https://developer.wordpress.org/plugins/cron/" rel="noopener noreferrer"&gt;comes&lt;/a&gt; in and is the second way to run cron tasks. You can easily registry your cron task within WordPress, and WordPress will take care of loading and executing your tasks within your specified schedule. Sounds sweet, and it’s suitable for plugins.&lt;/p&gt;

&lt;p&gt;What can be better, right? But nothing is perfect and this approach also has drawbacks.&lt;/p&gt;

&lt;p&gt;WordPress is a PHP based application and has no access to your system Cron (OS running on your host). In fact, it means WP needs some external event to check the schedule list and execute relevant tasks. Another thing is that WordPress can ONLY run Cron tasks when someone visits or opens a page on the website. If nobody visits your website, the Cron tasks will NOT run.&lt;/p&gt;

&lt;p&gt;This may sound scary, but the good news is that &lt;strong&gt;WordPress guarantees that tasks will be called&lt;/strong&gt; , there just isn’t a guarantee when exactly they’ll be called. For example, you’ve set up a Cron currency rates task to run once a day. Now in real life it means the task will be executed with the first visitor, after one day at the set time. Not earlier, but perhaps later, maybe even much later if you’ve had no visitors for several days.&lt;/p&gt;

&lt;p&gt;There are some tasks that are okay to run with this approach, however while nobody visits your site, Cron doesn’t run and therefore doesn’t actually “work”. But there are other tasks that require reliability. So if your cron tasks are about pulling info from external sources, or simply very important tasks that need to run, like any API stuff, you can’t rely on this approach.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verdict
&lt;/h4&gt;

&lt;p&gt;Both ways have their advantages and drawbacks.&lt;/p&gt;

&lt;p&gt;For that reason the best solution which is used by professionals, and &lt;a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" rel="noopener noreferrer"&gt;recommended by WordPress&lt;/a&gt;, is a combination of the two approaches, giving you the best of both and having them counteract each others drawbacks.&lt;/p&gt;

&lt;p&gt;As a website owner (or a developer) you need to set up the system Cron job which will call WordPress Cron. Overall, it’s relatively simple to do, and in this way WordPress Cron will be more reliable while keeping the native WordPress approach.&lt;/p&gt;

&lt;p&gt;See point no. 2 below to learn how to go about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  About WordPress Cron
&lt;/h3&gt;

&lt;p&gt;Above, we’ve learned that WordPress a PHP-based application, and its internal Cron relies on external events, i.e. user requests. Besides this fact, there is a list of important aspects, which we’ll go through in more detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. WordPress Cron is asynchronous
&lt;/h3&gt;

&lt;p&gt;Firstly, you should know that WordPress Cron is asynchronous. Asynchronous means it runs Cron jobs in a separate request from the event itself.&lt;/p&gt;

&lt;p&gt;So, a &lt;strong&gt;user request won’t have delays due to WordPress Cron&lt;/strong&gt; , a user that triggered some Cron job won’t wait until the job is finished to see the result of his request. So you can breathe easy knowing there aren’t any delays for your visitors. WordPress handles it all quietly in the background.&lt;/p&gt;

&lt;p&gt;If you’re interested in the finer details, then technically it happens in the following way;&lt;/p&gt;

&lt;p&gt;WordPress checks the Cron schedule on every user request to a website, and if there is a need to run Cron jobs, it creates a new request to the website cron file ( &lt;strong&gt;/cron.php&lt;/strong&gt; ), using the &lt;strong&gt;cURL&lt;/strong&gt; library. The user receives the response to their request immediately, while Cron executes the other cron tasks in a separate request, which is independent.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. WP Cron isn’t reliable (out-of-the-box). Way to fix
&lt;/h3&gt;

&lt;p&gt;As we already know, WordPress Cron out-of-the-box depends on external events. Compared with the system Cron, it has a big drawback. But we can easily fix it by merging the system Cron and WordPress Cron.&lt;/p&gt;

&lt;p&gt;So the solution is creating a Cron job within the system Cron and calling WordPress Cron from there. In this way, we receive a reliable Cron with all WordPress features and support for plugin cron jobs. It may sound a little tricky, but it isn’t really.&lt;/p&gt;

&lt;p&gt;For the official manual &lt;a href="https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/" rel="noopener noreferrer"&gt;see here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The task can be split into 2 steps. The first step is about creating a new system Cron job. Basically, there are two different types of hosting (shared and dedicated), and how to create cron jobs depends on your hosting type.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to add a system Cron job for shared hosting
&lt;/h4&gt;

&lt;p&gt;When using shared hosting, there are many different vendors, and you need to check the features offered by your vendor about the Cron tasks. Usually, a hosting admin panel has a special tab or item about Cron, where you can manage Cron jobs. The most common admin panel is cPanel, you can read more on how to add a Cron job for cPanel in the &lt;a href="https://blog.cpanel.com/how-to-configure-a-cron-job/" rel="noopener noreferrer"&gt;official article&lt;/a&gt;. Below is the official video.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/YwpUjz1tMbA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  How to add a system Cron job for the VPS hosting
&lt;/h4&gt;

&lt;p&gt;In case you’re an owner of VPS (Virtual Private Server i.e. Dedicated), you need to log in to your server with &lt;strong&gt;SSH&lt;/strong&gt; and run the following command to open a file in editing mode.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1. Setup a system Cron job for WordPress Cron
&lt;/h4&gt;

&lt;p&gt;Now it’s time to create a system Cron job that will call WordPress Cron. Add a new line in your cron file and copy the content below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/10 * * * * wget -q -O - https://YOUR_DOMAIN_HERE/wp-cron.php?doing_wp_cron &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: The line above isn’t suitable for use with&lt;/em&gt; &lt;a href="https://wplake.org/blog/wordpress-multisite/" rel="noopener noreferrer"&gt;&lt;em&gt;WordPress Multisite&lt;/em&gt;&lt;/a&gt; &lt;em&gt;mode. If you’re using WP Multisite mode and you need a reliable Cron, then read more about workarounds&lt;/em&gt; &lt;a href="https://wplake.org/blog/wordpress-multisite/#5-cron-tasks" rel="noopener noreferrer"&gt;&lt;em&gt;here&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don’t forget to replace YOUR_DOMAIN_HERE with your domain.&lt;/p&gt;

&lt;p&gt;The first part of the command states that we want our command to be executed once every ten minutes, regardless of an hour number, day of the week, and others options. The middle part uses the &lt;strong&gt;wget&lt;/strong&gt; tool to request the WordPress Cron file, and the last part states that we don’t want to save the output of the Cron file.&lt;/p&gt;

&lt;p&gt;You can read more about the arguments &lt;a href="https://help.ubuntu.com/community/CronHowto" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2. Disable default WordPress checks
&lt;/h4&gt;

&lt;p&gt;As we’ve now integrated WordPress Cron with the system Cron, WordPress no longer needs to check the schedule list within every user request. We tell WordPress by setting up a special constant in the &lt;strong&gt;wp-config.php&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Add any custom values between this line and the "stop editing" line. */

define( 'DISABLE_WP_CRON', true );

/* That's all, stop editing! Happy publishing. */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see the file has a special section for custom values, so you need to add this constant after the first comment, and before the second.&lt;/p&gt;

&lt;p&gt;Don’t worry about the label of the constant, “ &lt;strong&gt;DISABLE_WP_CRON&lt;/strong&gt; ” doesn’t actually mean completely disable WordPress Cron. It disables the checking of the schedule list with every user request. WP Cron should still work properly, as we call the &lt;strong&gt;wp-cron.php&lt;/strong&gt; file directly via the system Cron.&lt;/p&gt;

&lt;p&gt;That’s it! Congrats! Now your WordPress installation has a reliable Cron with all the WordPress features. Cron will run jobs according to the schedule, regardless of the amount of visitors’ to the website.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. How to add and remove Cron jobs
&lt;/h3&gt;

&lt;p&gt;WordPress Cron job is just a bit of PHP code that is executed by some schedule. Furthermore, it can use all the WordPress features and has access to the Database. &lt;a href="https://developer.wordpress.org/plugins/cron/scheduling-wp-cron-events/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; you can find an article for developers from WordPress on this topic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Choosing an interval
&lt;/h4&gt;

&lt;p&gt;Firstly, we need to choose an interval by which Cron will repeat our job. Default WordPress intervals are &lt;strong&gt;hourly&lt;/strong&gt; , &lt;strong&gt;twicedaily&lt;/strong&gt; , &lt;strong&gt;daily&lt;/strong&gt; , &lt;strong&gt;weekly&lt;/strong&gt;. Though it’s good enough for most cases, you may need a custom interval. We can define it by adding the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_filter('cron_schedules', function ($schedules) {
    return array_merge($schedules, [
        // here is the name of our custom interval. used when adding a new job
        'five_hours' =&amp;gt; [
            // the number here represents seconds, so the formula is: 5 hours * 60 minutes * 60 seconds
            'interval' =&amp;gt; 5 * 60 * 60,
            'display' =&amp;gt; esc_html__('Every Five Hours'),
        ],
    ]);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Adding a new reoccurring job
&lt;/h4&gt;

&lt;p&gt;To add a new Cron job we need to add a new shortcode and attach a schedule to the shortcode. The interval (the second argument to the &lt;strong&gt;wp_schedule_event()&lt;/strong&gt; function) must be of one of the default intervals, or some custom name that you can add using the code snippet above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action('YOUR_NAME_HERE', function () {
    /* your job here
    you can use all the WordPress features and functions
    e.g. wp_insert_post() */
});

// we must check that the event wasn't scheduled as each call creates a new one
// otherwise, if we've skipped the check it'll create a new event for each user request
if (!wp_next_scheduled('YOUR_NAME_HERE')) {
     /* the first argument is the timestamp which controls when the job 
       will be run for the first time
       the next argument reflects the chosen interval */
    wp_schedule_event(time(), 'hourly', 'YOUR_NAME_HERE');
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above we’ve created an empty job that Cron runs once an hour.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding a new single job
&lt;/h4&gt;

&lt;p&gt;The example above adds a job that Cron runs in a reoccurring method. But you also have the option to schedule a single job. For this goal, you need to use the &lt;strong&gt;wp_schedule_single_event()&lt;/strong&gt; function instead of the &lt;strong&gt;wp_schedule_event()&lt;/strong&gt;. It can accept two (2) types of arguments: timestamp with the next execution time and the hook that contains the job. So let’s create a job that Cron will run only once, after 5 hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action('YOUR_NAME_HERE', function () {
    /* your job here
     you can use all the WordPress features and functions
     e.g. wp_insert_post() */
});

// it's necessary to check that the event wasn't scheduled as each call creates a new one
// if you've skipped the check it'll create a new event for each user request
if (!wp_next_scheduled('YOUR_NAME_HERE')) {
    // as before, the first argument is the timestamp which controls when the job will be run
    // it's presented in seconds, so the formula is: 5 hours * 60 minutes * 60 seconds
    wp_schedule_single_event(time() + 5 * 60 * 60, 'YOUR_NAME_HERE');
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Removing a Cron job
&lt;/h4&gt;

&lt;p&gt;You can remove a scheduled job that you have previously added. It’s especially useful for reoccurring tasks. For this purpose, we’ll call the &lt;strong&gt;wp_unschedule_event()&lt;/strong&gt; function with a couple of arguments. The first must contain the next timestamp for the event (we get it dynamically) and the second is your job name.&lt;/p&gt;

&lt;p&gt;The code below removes single and reoccurring jobs. Also, pay attention, that for the reoccurring jobs, it’ll not only remove the schedule or the next event but also all future events of the job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_action('YOUR_NAME_HERE', function () {
    /* your job here
     you can use all the WordPress features and functions
     e.g. wp_insert_post() */
});

// it's necessary to check that the event wasn't scheduled as each call creates a new one
// if you've skipped the check it'll create a new event for each user request
if (!wp_next_scheduled('YOUR_NAME_HERE')) {
    // as before, the first argument is the timestamp which controls when the job will be run
    // it's presented in seconds, so the formula is: 5 hours * 60 minutes * 60 seconds
    wp_schedule_single_event(time() + 5 * 60 * 60, 'YOUR_NAME_HERE');
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. How to monitor Cron jobs
&lt;/h3&gt;

&lt;p&gt;Information about all WordPress Cron jobs is stored in the Database. This means we can get a list of all the scheduled jobs and manage them. It’s an important thing for developers and website owners.&lt;/p&gt;

&lt;p&gt;If you have seen the WordPress Database scheme, then you know that there is no separate table for Cron. All Cron tasks are stored within a single option in the Options table. Most likely you already know this table, as any good WordPress developer knows the main tables of the WordPress Database. This knowledge is necessary to clear understand internal processes. If you want to check and improve your knowledge, read &lt;a href="https://wplake.org/blog/what-must-know-good-wordpress-developer/" rel="noopener noreferrer"&gt;what must a good WordPress developer know&lt;/a&gt;.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fufw1bvejph27dmpe0y29.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.amazonaws.com%2Fuploads%2Farticles%2Fufw1bvejph27dmpe0y29.png" alt="img" width="800" height="374"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;WordPress stores Cron tasks as an option in the Options table&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But looking at the serialized data directly via PhpMyAdmin isn’t the best idea. That’s why we’ll use the &lt;a href="https://wordpress.org/plugins/wp-crontrol/" rel="noopener noreferrer"&gt;WP Crontrol&lt;/a&gt; plugin which will give us a clear UI. Using the tool we can debug our Cron jobs or check external ones that are added by plugins. For example, we created our custom Cron job and ran it immediately using the tool to make sure it works correctly.&lt;/p&gt;

&lt;p&gt;The plugin provides the interface for managing both Cron jobs and the intervals. Using it is quite simple. Install and activate the plugin then visit the “Tools” — “Cron events” item in your admin menu.&lt;/p&gt;

&lt;p&gt;Here you can manage Cron jobs (called Cron Events) and Cron Intervals (called Cron Schedules).&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fls6fc4i14qhl44cd5kxa.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.amazonaws.com%2Fuploads%2Farticles%2Fls6fc4i14qhl44cd5kxa.png" alt="img" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;You can control each job individually. Hover your cursor on the target item to see the list of actionsp&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cron and Cache plugins
&lt;/h3&gt;

&lt;p&gt;The important thing that you have to pay attention to is the collaboration of WordPress Cron and cache plugins. Most WordPress websites use some cache plugin to reduce the response time. Overall the approach is good and important for SEO. Also, cache means a lot for &lt;a href="https://wplake.org/blog/9-tips-for-wordpress-speed-optimization/" rel="noopener noreferrer"&gt;WordPress speed optimization&lt;/a&gt;. But in our case, it also means we skip PHP execution and return HTML.&lt;/p&gt;

&lt;p&gt;By default, WordPress Cron relies on user requests. Whether or not WordPress Cron will be called during requests to cached pages depends on the plugin that’s in use. From our experience cache plugins don’t call WordPress Cron or call Cron more rarely than when it’s out-the-box.&lt;/p&gt;

&lt;p&gt;WordPress Cron will still be called during editor visits to the dashboard, but that may not be enough. This approach is okay in case your Cron jobs aren’t important and can be delayed for longer periods. Otherwise, consider the second part of the article.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The solution described in the second part of the article, regarding the merging of the system Cron and WordPress Cron, shows the clear benefits, so if you have used it in that way then you’re set and don’t need to worry about it any longer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The&lt;/em&gt; &lt;strong&gt;&lt;em&gt;wp-cron.php&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;file is called directly and WordPress Cron will work properly.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We’ve reviewed the 5 important things about WordPress Cron. We recommend following the second part (merging system Cron and WordPress Cron) for each of your WordPress sites. It’ll save you from tonnes of headaches in the future. Also, don’t forget about the tools that allow you to monitor your Cron jobs. They’re quite useful.&lt;/p&gt;

&lt;p&gt;We hope our article was helpful to you, and that you now have a clear picture of WordPress Cron and how to add your own Cron jobs using the code snippets.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>cron</category>
      <category>wordpresswebdevelopm</category>
      <category>wordpressdevelopment</category>
    </item>
    <item>
      <title>Guide for WordPress Multisite</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Fri, 30 Dec 2022 12:45:15 +0000</pubDate>
      <link>https://dev.to/wplake/guide-for-wordpress-multisite-23m4</link>
      <guid>https://dev.to/wplake/guide-for-wordpress-multisite-23m4</guid>
      <description>&lt;p&gt;Learn about WordPress Multisite Network, when you may need it and how to setup. We’ll review advantages and drawbacks, and also will share tips that will help to create, manage, or split your own multisite.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fq4mjxwuj21re6jxbnska.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Fq4mjxwuj21re6jxbnska.jpeg" alt="cover" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  About WordPress Multisite
&lt;/h3&gt;

&lt;p&gt;You can be familiar with WordPress for many years, but may not know what is WordPress Multisite. That’s because most WordPress websites are a single installation. This feature is used pretty rarely, but it’s an essential feature of WordPress, and in some cases can be indispensable.&lt;/p&gt;

&lt;p&gt;What is a WordPress Multisite Network? It’s a couple, or more, WordPress websites that use a single WordPress installation. It means they have a common file system and a common Database. Don’t worry if it sounds foggy, we’re going to review it in a simple example.&lt;/p&gt;

&lt;h4&gt;
  
  
  An example with flowers
&lt;/h4&gt;

&lt;p&gt;Let’s say we have a website about flowers, and our domain is &lt;strong&gt;flowers&lt;/strong&gt;. We review all kinds of flowers here, but there are very popular kinds, like &lt;strong&gt;roses&lt;/strong&gt;. So we have a lot of requests about them and want to dedicate a separate website, which will cover only them. It may be either &lt;strong&gt;roses.flowers&lt;/strong&gt; or &lt;strong&gt;flowers/roses&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A separate website means a lot of work, we’ve to create a new theme, and set up and configure all plugins. And what if we want to have a separate website for several kinds of flowers, not only roses? It means we’ll have many different WordPress websites, with separate dashboards.&lt;/p&gt;

&lt;p&gt;Just imagine that you’ll need to login into all of them from time to time. Updating plugins on each of them will be a problem. But the real nightmare will be updating a theme, if you’ve made changes to one of the websites, you’ll need to repeat the update on each of them.&lt;/p&gt;

&lt;p&gt;WordPress Multisite feature suggests a solution. A network of WordPress websites that use the same installation. In our case with flowers, we can have an unlimited number of subdomains (kinds of flowers) for our primary &lt;strong&gt;flowers&lt;/strong&gt; website, and all of them will be available via one dashboard. They’ll use one installation, which means all of them will have an access to the primary plugins and your theme. You’ll update plugins or make changes to the theme only once, and all subdomains will display it immediately. It’s possible thanks to the common file system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of Multisite
&lt;/h3&gt;

&lt;p&gt;We hope it’s clear to you now overall, what is WordPress Multisite. Let’s talk about limitations, to understand in which cases it won’t work.&lt;/p&gt;

&lt;p&gt;The biggest limitation is the domain. WordPress Multisite feature can work only with subdomains or subfolders. It means we technically can’t join several different domains, like &lt;strong&gt;flowers&lt;/strong&gt; and &lt;strong&gt;clothes&lt;/strong&gt;. The feature allows joining only subdomains of one domain.&lt;/p&gt;

&lt;p&gt;A subdomain is a secondary domain that is based on the primary. For example, in the case with &lt;strong&gt;roses.flowers&lt;/strong&gt; the &lt;strong&gt;flowers&lt;/strong&gt; is a primary domain, and a subdomain.&lt;/p&gt;

&lt;p&gt;A subfolder looks like a plain permalink, but instead of a page contains a whole website. For example, in the case of &lt;strong&gt;flowers/roses&lt;/strong&gt; , the &lt;strong&gt;roses&lt;/strong&gt; will be not a page, but a website. It means that all pages of the roses website will have the such prefix. For example, &lt;strong&gt;flowers/roses/history&lt;/strong&gt; and &lt;strong&gt;flowers/roses/how-to-care&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WordPress Multisite allows joining the primary website and all subdomains into a single installation, single network. If you want to have different domains, unfortunately, you must use several single installations.&lt;/p&gt;

&lt;p&gt;There are other differences between single and multisite installations, but they aren’t limit us. We’ll review them a bit later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features of Multisite
&lt;/h3&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fb0rrnmaus89oa05g1pkp.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Fb0rrnmaus89oa05g1pkp.jpeg" alt="features" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s review the key principles of the Multisite.&lt;/p&gt;

&lt;h4&gt;
  
  
  a) Common file system
&lt;/h4&gt;

&lt;p&gt;All websites in the Multisite Network use the same single folder. It means a common list of plugins and themes. Don’t worry, each website has its own active list, so you can activate some plugins or a theme only for one website of a Network.&lt;/p&gt;

&lt;p&gt;Uploads of the primary website are still stored within the ordinary &lt;strong&gt;/wp-content/uploads/&lt;/strong&gt; folder. Uploads for secondary websites (subdomains) are also stored within the &lt;strong&gt;/wp-content/uploads/&lt;/strong&gt; folder, but within adding subfolders.&lt;/p&gt;

&lt;p&gt;For example, a newly uploaded &lt;strong&gt;tulip.png&lt;/strong&gt; to the primary website will be saved as &lt;strong&gt;/wp-content/uploads/2022/12/tulip.png&lt;/strong&gt;. A newly uploaded &lt;strong&gt;rose.png&lt;/strong&gt; to the secondary website will be added to a subfolder, like &lt;strong&gt;/wp-content/uploads/sites/2/2022/12/rose.png&lt;/strong&gt;. Where &lt;strong&gt;2&lt;/strong&gt; in this path means an ID of the secondary website. Each secondary website in the Network will have its own ID, but you shouldn’t take care of it.&lt;/p&gt;

&lt;h4&gt;
  
  
  b) Shared Database
&lt;/h4&gt;

&lt;p&gt;All websites will use a single Database. But each website will have its own tables for content. Posts, PostMeta, Taxonomies, Options, all of it will be unique for every website. It means you can have different pages, categories, and settings for each website, just like in the case of independent websites.&lt;/p&gt;

&lt;p&gt;An exception is the Users, they’ll be common for the whole network. It’s a good thing, otherwise, editors and users would have to sign up separately for each of them. But don’t worry, user roles are unique for every website, so you can easily manage access. For example, you can deny users from website A to use of their credentials to log in to website B. Of course, if it needs.&lt;/p&gt;

&lt;p&gt;WordPress Multisite creates several extra tables, like &lt;strong&gt;wp_blogs&lt;/strong&gt; and &lt;strong&gt;wp_sites&lt;/strong&gt; , but you shouldn’t worry about them. Those tables are needed for WordPress to manage the Network.&lt;/p&gt;

&lt;h4&gt;
  
  
  c) Dashboards
&lt;/h4&gt;

&lt;p&gt;There is one common dashboard for a whole Network, and separate for websites.&lt;/p&gt;

&lt;p&gt;A new role appears, Super Admin. By default, this role will have that administrator, which has created the Network. This administrator will be able to access a Network dashboard, which is common for all the websites. In this dashboard, he will be able to install, update or remove plugins and themes.&lt;/p&gt;

&lt;p&gt;Each website still will have a separate dashboard, where its content can be managed in the usual way. Only one new limitation appears here: administrators won’t be able to add, update or remove plugins anymore. Administrators now have only the right to activate or deactivate plugins and themes for the current website. The right to install, update and remove plugins and themes belongs completely to the Super Admin. These changes can be done only via the Network Dashboard. So, you can update your plugins for all the websites in one place. It’s beautiful.&lt;/p&gt;

&lt;h4&gt;
  
  
  d) robots.txt and .htaccess
&lt;/h4&gt;

&lt;p&gt;It’s important to mention these files separately. &lt;strong&gt;robots.txt&lt;/strong&gt; relates to SEO, and  &lt;strong&gt;.htaccess&lt;/strong&gt; manages permalinks (navigation). These files are a part of the file system and will be common for all the websites in WordPress Network.&lt;/p&gt;

&lt;p&gt;But there is a major change: WordPress won’t rewrite these files automatically anymore. Before it was so, and every time you visited the Settings — Permalinks page, WordPress rewrote that file automatically. With the Network, you’ll be able to change those files only manually. It’s not a problem, just keep in mind that if your theme has any code that modifies those files, for example, uses the &lt;strong&gt;mod_rewrite_rules&lt;/strong&gt; hook, it won’t work anymore.&lt;/p&gt;

&lt;p&gt;Also, some plugins rely on similar hooks, like cache plugins. But don’t worry, all big plugins support the Multisite mode and will show notices with a guide, in case they’ll need to modify  &lt;strong&gt;.htaccess&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  e) Cron tasks
&lt;/h4&gt;

&lt;p&gt;Cron tasks will work in the usual way. In case you’ve made no changes and your website uses the built-in WordPress cron, then everything will be the same for you.&lt;/p&gt;

&lt;p&gt;But, in case you’ve disabled the built-in WordPress cron, and used &lt;strong&gt;crontab&lt;/strong&gt; (e.g. to guarantee calls with fixed intervals) then your current approach won’t work anymore. You’ll have several websites, and the previous line in &lt;strong&gt;crontab&lt;/strong&gt; won’t cover all of them. To keep using &lt;strong&gt;crontab&lt;/strong&gt; for cron, you must install &lt;strong&gt;wp-cli&lt;/strong&gt; and use a special bash script. You can read more about the setup &lt;a href="https://servebolt.com/help/article/how-to-setup-server-side-cron-for-wordpress-multisite/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You can also use the bash snippet from that article, just don’t forget to change the relative path in the bash code to absolute.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to setup WordPress Multisite
&lt;/h3&gt;

&lt;p&gt;Note: before setting up a WordPress Multisite, think twice to make sure it fits your requirements. It’s pretty easy to create a WordPress Multisite, but there is no simple way to split or unmerge the Multisite Network back to single installations. Create Multisite only when you’re sure that you won’t need to have them as separate installations.&lt;/p&gt;

&lt;p&gt;There is a great official guide from WordPress, which you can find &lt;a href="https://wordpress.org/support/article/create-a-network/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Overall, the process is pretty simple and consists of several steps.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Important: Don’t forget to make a backup before any changes.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;1. Define a special constant&lt;/strong&gt; (in the &lt;strong&gt;wp-config.php&lt;/strong&gt; )&lt;/p&gt;

&lt;p&gt;It’s necessary to unlock the Multisite feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;define( 'WP_ALLOW_MULTISITE', true );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Deactivate all your plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s necessary to make sure you’ll have no conflicts during the setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Visit the Tools — Network setup page&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here you have to choose a Network type (subdomain or a subfolder) and you’ll receive pieces of code for your &lt;strong&gt;wp-config.php&lt;/strong&gt; and  &lt;strong&gt;.htaccess&lt;/strong&gt;  . You’ll need to follow the instructions and paste those pieces into the files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Final. Log in and activation of the plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the steps above, you’ll see a message that asks you to log in again. When you’ve logged in, you’ll see the common Network dashboard and links to the specific website dashboards. Now you can activate your plugins for specific websites.&lt;/p&gt;

&lt;p&gt;Congrats! Now you have the Multisite installation!&lt;/p&gt;

&lt;h3&gt;
  
  
  How to transfer WordPress Multisite
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;To another hosting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You may need to transfer your WordPress Multisite to another hosting. In this case, no extra actions are required. Everything will be the same as with a single installation. If you’re unfamiliar, you can read the &lt;a href="https://wordpress.org/support/article/moving-wordpress/" rel="noopener noreferrer"&gt;official article&lt;/a&gt; from WordPress. You have to move your files, and your Database to the new hosting, and then update DNS records to point to a new hosting. That’s it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To another domain&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s pretty easy to move WordPress to another domain if we talk about a single installation. In the case of the Multisite Network, the task can’t be done using built-in tools, as the whole Network depends on a primary domain. But don’t worry, we can achieve it with manual updates in the Database. For example, via PhpMyAdmin.&lt;/p&gt;

&lt;p&gt;You can copy your Live database, import it to a new place, and then perform the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Changes in the &lt;strong&gt;wp_site&lt;/strong&gt; table
Replace the old domain with the new for values in the &lt;strong&gt;domain&lt;/strong&gt;  column&lt;/li&gt;
&lt;li&gt;Changes in the &lt;strong&gt;wp_sitemeta&lt;/strong&gt; table
Replace the old domain with the new one for &lt;strong&gt;meta_value&lt;/strong&gt; in the rows where &lt;strong&gt;meta_key = ‘siteurl’&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Changes in the &lt;strong&gt;wp_blogs&lt;/strong&gt; table
Replace the old domain with the new for values in the &lt;strong&gt;domain&lt;/strong&gt;  column&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that, don’t forget to update the &lt;strong&gt;DOMAIN_CURRENT_SITE&lt;/strong&gt; constant in the &lt;strong&gt;wp-config.php&lt;/strong&gt; to the new domain.&lt;/p&gt;

&lt;p&gt;Also, don’t forget to make ordinary changes that are necessary for a single installation. I mean changes in the &lt;strong&gt;wp_options&lt;/strong&gt; table for the rows where &lt;strong&gt;option_name = ‘siteurl’&lt;/strong&gt; or &lt;strong&gt;option_name = ‘home’&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s it. Following the steps, we’ve moved a WordPress Multisite to another domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to split WordPress Multisite
&lt;/h3&gt;

&lt;p&gt;By default, WordPress doesn’t provide an option to ‘unmerge’ WordPress Multisite Network. The task is pretty unusual, that’s why developers didn’t make a feature for it.&lt;/p&gt;

&lt;p&gt;There are only a few reasons why you need to split WordPress Multisite into several single installations. One of them can be a case when the subsites have grown and now have nothing in common with the primary website. So there is no sense to keep them within a Multisite Network anymore.&lt;/p&gt;

&lt;p&gt;Usually, in this case, it’s better to create a new installation for each of them, and then import content from the related subsite manually. And then just remove the related subsite in the Network.&lt;/p&gt;

&lt;p&gt;As a plan B, you can try to split the Multisite with manual changes in the Database. But you need to have strong WordPress knowledge and skills to work with dumps. Here are the steps that you need to follow for each website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a MySql dump
With all tables related to one website, plus common tables: &lt;strong&gt;wp_users&lt;/strong&gt; and &lt;strong&gt;wp_usermeta&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Import the dump into a new Database
Don’t forget to update a table prefix to the ordinary ( &lt;strong&gt;wp_&lt;/strong&gt; ), in case it was a subsite.&lt;/li&gt;
&lt;li&gt;Log in, to make sure everything works
If you had roles only on of the websites, you may need to recreate them again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can easily recognize which tables belong to which website. As we’ve mentioned each website in the Network has an ID. This ID is added to all website tables. For example, the Posts table for the first subsite will have a &lt;strong&gt;wp_1_posts&lt;/strong&gt; name, for the second will have a &lt;strong&gt;wp_2_posts&lt;/strong&gt; name, and go on and go forth. Only the primary website tables will have no prefixes. So, the Posts table name will look like as usual, &lt;strong&gt;wp_posts&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;We’ve reviewed all important topics related to the WordPress Multisite feature. We hope that now you understand how it works, and what the goal of the feature is.&lt;/p&gt;

&lt;p&gt;Using a Multisite Network can solve a row of problems. In case Multisite fits your requirements, use this knowledge to easily create and manage your own WordPress Network.&lt;/p&gt;

&lt;p&gt;Thank you for reading! Follow us to get notified about new articles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://wplake.org/blog/wordpress-multisite/" rel="noopener noreferrer"&gt;&lt;em&gt;https://wplake.org&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on December 30, 2022.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpressdevelopment</category>
      <category>php</category>
      <category>development</category>
      <category>cmsdevelopment</category>
    </item>
    <item>
      <title>9 tips for WordPress speed optimization</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Thu, 29 Dec 2022 15:18:49 +0000</pubDate>
      <link>https://dev.to/wplake/9-tips-for-wordpress-speed-optimization-4j1o</link>
      <guid>https://dev.to/wplake/9-tips-for-wordpress-speed-optimization-4j1o</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.amazonaws.com%2Fuploads%2Farticles%2Fqq9esegkgmc3ugpwubmj.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Fqq9esegkgmc3ugpwubmj.jpeg" alt="Cover" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WordPress is designed to be fast. We’ll describe deadly mistakes that break it, and features that will help to improve the speed. These 9 practical tips will help you speed up your WordPress website.&lt;/p&gt;

&lt;p&gt;Loading time is less than a second and a permanent place in the green area of the PageSpeed Insights tool isn’t a miracle for a WordPress website, but a reachable goal.&lt;/p&gt;

&lt;p&gt;In this article, we’ll share practical tips about WordPress speed optimization, and we promise that we won’t lie. We aren’t going to tell you that any existing WordPress website that is super slow, regardless of a number of issues, can be optimized to the 100% using some magic plugin or tool.&lt;/p&gt;

&lt;p&gt;Speed is a topic that should be considered from the first day of creating a website. Our tips will definitely help you to improve the speed of your website, but how good results will you achieve, will depend on your WordPress theme.&lt;/p&gt;

&lt;p&gt;Developers can learn best practices to build a theme with great performance. In case you’re a website owner and your website is already ready, don’t worry — some tips below will work for all WordPress websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to measure the speed of a website
&lt;/h3&gt;

&lt;p&gt;Note: this chapter is for beginners, feel free to skip it in case you already know it.&lt;/p&gt;

&lt;p&gt;First of all, let’s clarify how usually we measure the speed of a website. In this area, we can’t rely on our personal feelings, that say to us was this or that website loaded fast. We use special tools and metrics that show special numbers and reports to us. There are many tools, but we’ll review the most popular.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. PageSpeed Insights tool
&lt;/h3&gt;

&lt;p&gt;It’s a tool from Google that is available for everyone for free. The tool uses a row of factors, like LCP (First Contentful Paint), TTI (Time To Interactive), TTFB (Time to First Byte), and others. You can read more about them in detail on the &lt;a href="https://web.dev/user-centric-performance-metrics/#important-metrics-to-measure" rel="noopener noreferrer"&gt;official website&lt;/a&gt; from Google.&lt;/p&gt;

&lt;p&gt;There are two ways to use the tool:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a) Via the website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;tool website&lt;/a&gt;, you just need to paste any url and press the Analyze button, and after some time the tool will provide a report. The main score will be in a range from 1 to 100 and will consist of many factors. It’s a very useful tool, as besides the score the report will also contain specific tips for this url, that will help to improve the score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b) Via Chrome/Brave browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These browsers have a ‘Lighthouse’ tab in the Developer tools, and you can test any website in the same manner. When you have to make a row of tests this way may be preferred.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Developer tools panel
&lt;/h3&gt;

&lt;p&gt;Every browser has its own Developer tools panel, you can open this panel by pressing F12 on your keyboard (your browser must be open of course at this time). I mean here first of all a Network tab. If we talk about estimation, this way will give you only a couple of numbers (DOMContentLoaded and Load), but it’ll be enough for a primary estimation.&lt;/p&gt;

&lt;p&gt;This way shows its full power when you need to debug speed issues in detail, as only this way allows easily to get a list of all page resources by categories (fonts, JS, CSS) and their personal load time. So, developers usually use both of these tools, PageSpeed Insights, and Developer tools together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why you can trust us
&lt;/h3&gt;

&lt;p&gt;Before we’ll go with the tips, we have to confirm our expertise, so you’ll trust our words and advice. The best confirmation in our view will be not portfolio jobs or screenshots, but something that is easy to see and check. It’s the current website — &lt;a href="https://wplake.org/" rel="noopener noreferrer"&gt;wplake.org&lt;/a&gt;. It’s a WordPress website and his pages (including the current article) have a loading time of less than a second and a permanent place in the greed area of the PageSpeed Insights tool.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fllonaua5bsxidjpry5f1.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.amazonaws.com%2Fuploads%2Farticles%2Fllonaua5bsxidjpry5f1.png" alt="100% score for mobile" width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;100% score for mobile&lt;/em&gt;&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F0ivm5wkimbi5g3s1lay6.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.amazonaws.com%2Fuploads%2Farticles%2F0ivm5wkimbi5g3s1lay6.png" alt="Loading time is 278MS" width="800" height="207"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Loading time is 278MS (a quarter of a second)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve shared screenshots for our &lt;a href="https://wplake.org/blog/" rel="noopener noreferrer"&gt;blog overview page&lt;/a&gt;, you can easily check the results yourself using DevTools of your browser or the &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights tool&lt;/a&gt;. All pages of &lt;a href="https://wplake.org/" rel="noopener noreferrer"&gt;wplake.org&lt;/a&gt; have such results, but of course, the final numbers will be a little varied depending on the server loading and the quality of your internet connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. Don’t use visual page builders
&lt;/h3&gt;

&lt;p&gt;Like Elementor or any others. There are no visual page builders for WordPress, that allow the building of pages with good performance. It’s the nasty truth, but we promised to tell the truth only. These visual builders are attractive for beginners in development or website owners, as give an easy way to build beautiful pages. But everything has a price in this world, and in this case, the price is performance and page loading time.&lt;/p&gt;

&lt;p&gt;Pages built by visual page builders have a lot of heavy JavaScript, dirty and overloaded HTML markup and your users will never receive good UX. You can use builders only if you’re ready to sacrifice SEO and user experience to receive easy editing.&lt;/p&gt;

&lt;p&gt;Don’t trust their sweet stories that exactly their visual builder is optimized and your website will be fast — trust yourself and check yourself. It’s so easy to open the PageSpeed Insights tool, paste a url of their official website and check the report. For example, I’ve checked elementor.com for you and sharing the result:&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fz8m16s4bltqgaafgm7lp.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.amazonaws.com%2Fuploads%2Farticles%2Fz8m16s4bltqgaafgm7lp.png" alt="pagebuilder speed" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think no extra comments are needed. If the official website of the visual page builder with a big team of developers, that already used all tricks to improve the score, isn’t able to get good results, do you think your website will?&lt;/p&gt;

&lt;p&gt;Just look at the markup of their website:&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F0ht5ys0kbq1mn3owax6n.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.amazonaws.com%2Fuploads%2Farticles%2F0ht5ys0kbq1mn3owax6n.png" alt="pagebuilder markup" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How many classes every element has, this markup is unreadable and stretches the page length, which is harmful to page speed and SEO.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Choose the right theme
&lt;/h3&gt;

&lt;p&gt;As we mentioned above, exactly your theme will predict, which the maximum score your website will have. The perfect case is when a theme is custom and built by professional WordPress developers. In this case, they’ll able to use best practices, like using pure JS and avoiding unused CSS/JS code on pages. These things are predicted by a theme, and can’t be changed easily. They can use some bones or frameworks to decrease developing time, but it won’t decrease performance.&lt;/p&gt;

&lt;p&gt;In case you don’t have this opportunity and have to choose something from ready WordPress themes, then the same advice — don’t trust promises, trust yourself and check yourself. Check their website via the PageSpeed Insights tool, find and check famous websites that were built using that theme, and only after that purchase and use that theme. It’s not a problem, as usual, themes highlight their top clients.&lt;/p&gt;

&lt;p&gt;Theme isn’t a glove, in most cases you won’t be able to change it on a fly. Every theme has its own unique settings and style, that can’t be migrated to a new one automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use CDN for static assets
&lt;/h3&gt;

&lt;p&gt;Content Delivery Network, CDN — is the most simple and cheap way to decrease page loading time. By default, static assets, like images, scripts and styles are placed only on a server of your hosting vendor. It’s a specific place, which means users from other countries, especially those that are away from this location, will have a big loading time of these assets.&lt;/p&gt;

&lt;p&gt;CDN vendors offer a solution, a worldwide network of their own servers, that stores copies of your assets. In this way, a user that requests your website will receive assets from the nearest server, which decreases page loading time significantly.&lt;/p&gt;

&lt;p&gt;This solution won’t be very useful in case your website reflects a local business. In this case, your visitors always visit your website only from one region. CDN still will be helpful for SEO, as SEO robots are placed in different locations, and consider page loading time. But your users won’t see a big difference.&lt;/p&gt;

&lt;p&gt;For our website we use &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt;, it’s one of the most famous CDN vendors, furthermore, it has a free tariff which will be enough for most websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use SSD hosting
&lt;/h3&gt;

&lt;p&gt;Chose a good SSD hosting vendor. SSD hosting means disks on servers are SSD, not HDD. It will significantly reduce delays in all operations with files and will decrease page loading time.&lt;/p&gt;

&lt;p&gt;Even in case you use some CDN, SSD hosting still will make a change. WordPress website consists not only of images and styles: it’s a lot of PHP files that are included within every request. It’s hundreds of requests to the Database (which is also a file btw) and many other operations with files.&lt;/p&gt;

&lt;p&gt;It’s not a paid article, we’ve no advertising here, so I’m not going to promote any hosting vendors. Just try to search ‘SSD hosting’ and choose the best, or make sure that your hosting vendor uses SSD disks.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Use server cache
&lt;/h3&gt;

&lt;p&gt;By default, when a user requests some page on your website it’s being generated by WordPress from a scratch. It means hundreds of requests to the Database and many PHP operations. Time that came since a user requested some page to the moment when his browser received an HTML code is pretty big. This delay slow downs your website and is harmful to SEO.&lt;/p&gt;

&lt;p&gt;You can decrease this time by using the server cache. It means the page generation process happens only once, and then users receive ready HTML without the execution of expensive PHP code.&lt;/p&gt;

&lt;p&gt;The are many good cache plugins for WordPress, that take care of everything and make the server cache feature for you the same easy as enabling a plugin. For our website, we use &lt;a href="https://wordpress.org/plugins/wp-super-cache/" rel="noopener noreferrer"&gt;WPSuperCache&lt;/a&gt;, which is free.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Use lazy loading for images
&lt;/h3&gt;

&lt;p&gt;It’s another very efficient and simple way to decrease page loading time. By default browser parses the HTML of your page and loads all found images immediately. It forces users to wait more and makes a website slower.&lt;/p&gt;

&lt;p&gt;Lazy loading says to a browser that images on a page must be loaded only when a user is near them and scrolling a page. In this way downloading images that are at the bottom and invisible delays, and makes loading time better. Then, when a user scrolls, a browser loads images step by step, but in ahead, so it all happens in the background and unnoticable to the user.&lt;/p&gt;

&lt;p&gt;Before developers had to write their own JS code to implement lazy loading, but now all major browsers support it. You can use the loading="lazy" attribute for images. Read more about the attribute &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;WordPress since 5.4 has it by default for all images that are added to a page via the Gutenberg editor. But it also depends on your theme, many images can be out of the Gutenberg content and have no lazy loading. You need to check if the loading="lazy" attribute is presented for all of the images, and in case it isn't, then you've to amend your theme or ask your developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Use pure JavaScript
&lt;/h3&gt;

&lt;p&gt;Under pure JavaScript and I mean avoiding using any big libraries, like jQuery. Using TypeScript is still a good idea, as it makes your JS code better, and after compiling turns it into plain JavaScript.&lt;/p&gt;

&lt;p&gt;There are so many libraries and tools that use jQuery, so many sliders, tables, and data charts. Forget about them. Do not use jQuery, use only pure JavaScript and libraries that don’t require jQuery.&lt;/p&gt;

&lt;p&gt;Using jQuery on a front will slow down your website, as the library contains hundreds of functions. From them you’ll use only a few on a page, others will just waste precious loading time. Furthermore, jQuery code isn’t optimized regarding working with DOM. It means this library can’t provide a good and smooth experience for users.&lt;/p&gt;

&lt;p&gt;It may sound complex and difficult to forget about jQuery, but believe us, you’ll get used to pure JS very quickly. Prety soon you’ll ask yourself: why did I use it before? After some time you’ll have your own list of pure-js libraries that will help you in solving different tasks.&lt;/p&gt;

&lt;p&gt;We can suggest using the @splidejs/splide package to create sliders and carousels, this package is written on pure JS and helps to create sliders very easily, is also flexible, and has a lot of settings. You can read more about the package on the &lt;a href="https://splidejs.com/" rel="noopener noreferrer"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open to yourself WebComponents. This feature is supported by all major browsers and allows you to work with HTML elements easily. You can read more about it &lt;a href="https://kinsta.com/blog/web-components/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. On our website we use &lt;a href="https://catalyst.rocks/guide/introduction/" rel="noopener noreferrer"&gt;Catalyst&lt;/a&gt;. It’s a tiny TypeScript library that makes working with WebComponents super easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Use minification and avoid unused CSS and JS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unused CSS and JS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A large amount of unused CSS rules and JS code is one of the main reasons that make your website slow. Usually, it happens when you’ve global styles and scripts for a whole website or for many different templates.&lt;/p&gt;

&lt;p&gt;In this case the story with jQuery repeats, a page uses only a bit of a CSS or JS file, but a browser has to load all the file. Precious time is spent, as CSS and JS files require not only loading, but also parsing and executing, which is expensive about resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your CSS and JS code is written by humans. It means variable names must be pretty long there (to be clear), and formatting (spaces, tabs) is used to make code readable. But any browser is software and doesn’t require it. Empty spaces and tabs can take more than half of the size of a not minified file. Minification is a process that removes all spaces from human-written code and replaces variable names with shorter. For example, var screenSize=1920; will be replaced with var o=1920;. Do you see how shorter it is? Programmers can't use such short names, as this code isn't readable for humans, but a browser won't have any issues with such code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution for both is creating template-specific CSS and JS bundles. Bundle is a file that contains a row of small CSS/JS modules. It can be done only within a theme, so if you’re a website owner without coding skills then you can’t change it for an already existing theme.&lt;/p&gt;

&lt;p&gt;In case you’re a developer, consider &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; or alternative solutions that allow keep CSS and JS in modules and provide an automatic minification process.&lt;/p&gt;

&lt;p&gt;In this way for example homepage and contact page will have different CSS and JS files, that will contain rules only for presented elements. Don’t worry, you still can have common elements, as modules can be imported many times, so you won’t need to write the same code multiple times.&lt;/p&gt;

&lt;p&gt;On our website we use &lt;a href="https://laravel-mix.com/" rel="noopener noreferrer"&gt;LaravelMix&lt;/a&gt;, a Webpack extension that takes care of all settings of Webpack and provides a super easy way to work with Webpack.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Inline critical CSS
&lt;/h3&gt;

&lt;p&gt;This is a powerful way to improve user experience and many PageSpeed Insights metrics, like FCP and CLS. It means that all CSS that is critical should be added right to the head tag of the HTML code of a page, instead of the ordinary way including it as a separate file via the link element.&lt;/p&gt;

&lt;p&gt;In this way, a browser shouldn’t spend time loading the file, with it parsing and applying CSS rules beginning much earlier, which reduces page loading time.&lt;/p&gt;

&lt;p&gt;To reach it you’ve to use a custom theme, and the developer of the theme must implement this feature. You can read more about critical CSS &lt;a href="https://web.dev/extract-critical-css/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On our website we have page specific bandles, and inline all the CSS code from a bandle, without splitting into critical and not critical. It saves a lot of time. The main thing in this approach is that you must have a clear and simple design. Otherwise, a number of CSS rules will be too big and it’ll stretch the size of an HTML document, which will increase page loading time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;We’ve reviewed all the main items that you must know in order to optimize WordPress speed.&lt;/p&gt;

&lt;p&gt;Some of them require programming skills and can’t be done by website owners, those items are for developers, please consider them while creating a new theme.&lt;/p&gt;

&lt;p&gt;But some of the items don’t require programming skills at all, in case you’re an owner of a slow website and want to speed up your WordPress, then it’s easy to set up CDN yourself or install a cache plugin. Those items will help you even in case you don’t know how to modify a theme code.&lt;/p&gt;

&lt;p&gt;We’ve considered speed optimization, but security is also an important topic. Read &lt;a href="https://wplake.org/blog/5-pro-tips-to-secure-wordpress/" rel="noopener noreferrer"&gt;5 Pro Tips to secure WordPress from hacking&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>wordpress</category>
      <category>development</category>
      <category>speedoptimization</category>
    </item>
    <item>
      <title>10 Best add-ons for the Advanced Custom Fields plugin</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Thu, 29 Dec 2022 07:29:34 +0000</pubDate>
      <link>https://dev.to/wplake/10-best-add-ons-for-the-advanced-custom-fields-plugin-4df1</link>
      <guid>https://dev.to/wplake/10-best-add-ons-for-the-advanced-custom-fields-plugin-4df1</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.amazonaws.com%2Fuploads%2Farticles%2F3td6lonz83aw1oo5bvyp.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F3td6lonz83aw1oo5bvyp.jpg" alt="Addons" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are plenty of add-ons for ACF and it may be difficult for you to navigate by them, that's why we've created our list of the 10 best add-ons that extend the ACF plugin and make ACF even more useful. &lt;/p&gt;

&lt;p&gt;Advanced Custom Fields (ACF) plugin is one of the most popular plugins in the WordPress universe. It gives you full control over meta fields and supports many field types. Using the plugin you can add and manage any new characters to any post type with a few clicks. Below you'll find our list of the 10 best ACF add-ons which add new features or integrations and make your experience better.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. ACF: Better Search
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fqntlkajgvc36gr6vsdyv.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.amazonaws.com%2Fuploads%2Farticles%2Fqntlkajgvc36gr6vsdyv.png" alt="Better search" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want the build WordPress search to be able to find posts and pages by ACF fields? This addon improves the built-in WordPress search by including meta fields in the request. It works both for the admin area and for the front, which means both editors and users will be able to use this feature. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-better-search/" rel="noopener noreferrer"&gt;ACF: Better Search in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ACF Quick Edit Fields
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fc7tvcbs0g8jmbmubusfj.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.amazonaws.com%2Fuploads%2Farticles%2Fc7tvcbs0g8jmbmubusfj.png" alt="Quick Edit" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to be able to amend ACF fields right on the page overview table screen? It's possible with this addon, you can choose which fields must be available for quick editing without hassle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-quickedit-fields/" rel="noopener noreferrer"&gt;ACF Quick Edit Fields in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ACF Views
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fcd3xj3koc5fglepji8o9.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.amazonaws.com%2Fuploads%2Farticles%2Fcd3xj3koc5fglepji8o9.png" alt="ACF Views" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to display ACF fields on the front quickly, without modifying your theme and reading ACF's docs about the target field's type? This plugin allows to display selected ACF fields (or even Posts) anywhere using shortcodes, HTML markup is created automatically by the plugin. You'll need only to choose the fields to display, copy and paste a shortcode. Everything else will be done by the plugin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-views/" rel="noopener noreferrer"&gt;ACF Views in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Image Aspect Ratio Crop Field
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fj4gz9l0xqf5latl3zgt4.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fj4gz9l0xqf5latl3zgt4.jpg" alt="Crop Image" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to make sure that the selected image will be displayed with a specific ratio? This addon adds a new field type, that looks and works like the native ACF field, beside the native features it gives a popup using which you can crop any selected image to a specific ratio or size. This field gives you a way to configure the size for a specific ACF image field and be sure, that all images in this field will have the chosen ratio (or size). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-image-aspect-ratio-crop/" rel="noopener noreferrer"&gt;Image Aspect Ratio Crop Field in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Table Field
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fq0a50k7mrciejcoezff8.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.amazonaws.com%2Fuploads%2Farticles%2Fq0a50k7mrciejcoezff8.png" alt="Table field" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to have a meta table with flexible columns for editors, and be able to easily display it on the front for users? This addon adds a new field type and makes it possible. Allows easy to create tables and to manage columns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/advanced-custom-fields-table-field/" rel="noopener noreferrer"&gt;Table Field in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. ACF Options Page Admin
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F409ldiqkkmpbk5bmvjhc.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.amazonaws.com%2Fuploads%2Farticles%2F409ldiqkkmpbk5bmvjhc.png" alt="Options" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ACF provides the &lt;a href="https://www.advancedcustomfields.com/resources/options-page/" rel="noopener noreferrer"&gt;option's page feature&lt;/a&gt;, using which you can create a settings page and attach any fields there using the ordinary ACF UI, then access values in an ordinary way. Do you want to create these pages without coding? This addon makes it possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/options-page-admin-for-acf/" rel="noopener noreferrer"&gt;ACF Options Page Admin in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Admin Columns
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F5rojm3dohlpvv5nsqz5y.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.amazonaws.com%2Fuploads%2Farticles%2F5rojm3dohlpvv5nsqz5y.png" alt="Admin columns" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want some ACF fields to be displayed on the page overview table screen as extra columns? This addon gives your full control of all overview table columns and allows you to add new, reorder or remove current columns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/codepress-admin-columns/" rel="noopener noreferrer"&gt;Admin Columns in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. ACF Content Analysis for Yoast
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fsv3ri8u5zhqqipkn2941.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.amazonaws.com%2Fuploads%2Farticles%2Fsv3ri8u5zhqqipkn2941.png" alt="Yoast" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you use Yoast and want to improve SEO reports by including ACF fields in the analysis? The plugin does it for you without hassle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-content-analysis-for-yoast-seo/" rel="noopener noreferrer"&gt;ACF Content Analysis For Yoast in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Font Awesome Field
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fb4ch26avnov4xiwghpmh.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fb4ch26avnov4xiwghpmh.jpg" alt="Font Awesome" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to display some FontAwesome icons on a page, be able to change the choice later without changing a code, and don't enquire FontAwesome on pages manually? This addon adds a new field type to your ACF, called FontAwesome. You can select any icon and display it like an ordinary ACF field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/advanced-custom-fields-font-awesome/" rel="noopener noreferrer"&gt;Font Awesome Field in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Gravity Forms Add-on
&lt;/h2&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fx0k5gjaq2g8vzguy9d7h.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.amazonaws.com%2Fuploads%2Farticles%2Fx0k5gjaq2g8vzguy9d7h.png" alt="Gravity" width="772" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you want to be able to select any existing Gravity form in an ACF select field? The addon adds a new ACF field type (Forms) and makes it possible. Now you can 'attach' any Gravity form to your page or any post type, it may be useful if you want to be able to get or display Gravity forms dynamically, depending on a choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-gravityforms-add-on/" rel="noopener noreferrer"&gt;Gravity Forms Add-on in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;You've reviewed our list of the best ACF add-ons. ACF gives endless opportunities about meta fields, just learn them and you won't have any issues with metadata anymore.&lt;/p&gt;

&lt;p&gt;ACF plays nice with WooCommerce, read how to &lt;a href="https://wplake.org/blog/display-acf-fields-on-the-woocommerce-shop-page/" rel="noopener noreferrer"&gt;display ACF fields on the WooCommerce shop page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>5 Pro Tips to secure WordPress from hacking</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Wed, 28 Dec 2022 15:11:34 +0000</pubDate>
      <link>https://dev.to/wplake/5-pro-tips-to-secure-wordpress-from-hacking-5hm7</link>
      <guid>https://dev.to/wplake/5-pro-tips-to-secure-wordpress-from-hacking-5hm7</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3KJ1drRk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A2zYpSP_d0QrkxbYO-b7vBQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3KJ1drRk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A2zYpSP_d0QrkxbYO-b7vBQ.jpeg" alt="Cover" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow these 5 Pro Tips to secure your WordPress installation like Pro. After implementation, you’ll receive a great level of protection, which will be enough for most websites.&lt;/p&gt;

&lt;p&gt;Some people feel lazy when hear talking about security and don’t follow very basic instructions (as long won’t hacked). Others become crazy and lock everything that can be locked, making the user experience worse. Let’s fit the gold middle and secure WordPress from the most often threats, but without becoming paranoid. Following these tips will save you from using heavy and expensive security plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic for beginners
&lt;/h3&gt;

&lt;p&gt;Here I must say a couple of words about very basic things that are related to security. The next information is only for beginners, feel free to skip it if you feel self-confidence in basic security questions.&lt;/p&gt;

&lt;p&gt;So, as a beginner, you must know that most hacks happen not due to vulnerability in code, but due to rough mistakes in using the software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passwords
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A. Don’t use weak passwords&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Any password must contain 16+ random characters, including special characters. Hackers have a list of commonly used phrases and can easily get access to your admin panel, in case your password isn’t a random string. Never, ever use names or words in your password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Don’t share passwords with third party faces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even with developers. If you need to get some work done by them, create them a separate account, and change the password for the account after they’ve done tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C. Keep password in a safe place&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing on paper is a bad idea. Storing in online services also may be a bad idea. The best case is using an offline app. There are plenty of programs that allow storing passwords securely. For example, I use KeePassXC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D. Don’t share passwords via insecure channels&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you need to share a password (for example you’re a developer and have created an account for your client) do it via secure channels or require changing the password after sharing. Slack or messenger channels aren’t fit for this goal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unverified themes and plugins
&lt;/h3&gt;

&lt;p&gt;It’s the most common mistake of beginners. They see how many options there are to customize a website and begin to install all plugins and try all themes that can be found. Usually, prefer free or nulled (hacked). Most of these plugins WILL contain viruses or vulnerabilities, it’s a price of accessibility.&lt;/p&gt;

&lt;p&gt;If we talk about free plugins, download plugins ONLY from the &lt;a href="https://wordpress.org/plugins/"&gt;official WordPress repository&lt;/a&gt; (or from the built-in Plugins item in your WordPress admin menu). Also, check the number of active installations (must be bigger than 100) and the last updated date (must be less than a year ago). If we talk about paid Pro plugins, make sure that you’re purchasing on an official website, and that there are independent reviews that confirm the quality of the plugin. (Not on their website, but on other resources).&lt;/p&gt;

&lt;h3&gt;
  
  
  5 Pro Tips to secure WordPress
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. Change your login url and add a captcha
&lt;/h3&gt;

&lt;p&gt;The brute force of login credentials is the most often practice of hackers. They send hundreds of requests to your website via the login page, and even if you have a strong password it creates extra loading to your website. Plus, if you’re a developer, you can’t guarantee that owner or his editors won’t use weak passwords. This step will be an extra layer that will protect admin accounts.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="https://wordpress.org/plugins/wps-hide-login/"&gt;WPS Hide Login&lt;/a&gt; plugin to change the login url. It can be done easily, and furthermore, it won’t change the admin url for authorized users. So it only locks ‘wp-admin’ and ‘wp-login.php’ from unauthorized users, editors and admins must visit a new url to login, and then can use ordinary ‘wp-admin’ to access the admin panel.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="https://wordpress.org/plugins/simple-google-recaptcha/"&gt;Simple Google Captcha&lt;/a&gt; plugin to add a captcha to the login page. It works well with the first plugin and adds a captcha via code hooks, which means it will work with any custom login url. The plugin only adds Google Captcha code, but doesn’t sign up a Google account for you. You must sign up and get credentials from &lt;a href="https://www.google.com/recaptcha/about/"&gt;Google ReCaptcha&lt;/a&gt; to use the plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Disable REST API for unauthorized users
&lt;/h3&gt;

&lt;p&gt;REST API allows getting a list of usernames without trouble. Hackers still will need to get a password, but hey, let’s make their life even worse. Plus, even the names (usernames) of editors of your website can be a part of private information, what is the goal to share it with the whole world? REST API is used in WordPress for example by Gutenberg editor (and different plugins), so completely disabling will break the work of a WordPress website.&lt;/p&gt;

&lt;p&gt;The golden middle is disabling REST API for unauthorized users, it won’t create any issues for authorized editors and admins, but will protect against leaking usernames to third-party faces.&lt;/p&gt;

&lt;p&gt;I use the &lt;a href="https://wordpress.org/plugins/disable-json-api/"&gt;Disable REST API&lt;/a&gt; plugin for this goal, and by default its configured to disable only for unauthorized users, so you can just enable the plugin and don’t make any extra steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Disable directory browsing
&lt;/h3&gt;

&lt;p&gt;Directory browsing is an insecure feature, that allows seeing a list of files in a request folder via browser, in case the ‘index.php’ file is missing. To find vulnerabilities in your plugins and themes hackers must know their file structure.&lt;/p&gt;

&lt;p&gt;WordPress has the index.php stub for all the basic folders, like /wp-content/, /wp-content/plugins/, and /wp-content/themes/, but can't guarantee that every plugin or theme developer does the same. For this goal, you need to make sure that your web server doesn't allow directory browsing.&lt;/p&gt;

&lt;p&gt;Try to visit YOUR_DOMAIN/wp-includes/ in your browser and in case you see the 'Not Found' message, it means your server already doesn't allow it, you can skip this item. In case you see a list of files, you must do extra actions to disable directory browsing.&lt;/p&gt;

&lt;p&gt;To do this, we need to modify the .htaccess file. I don't recommend editing this file via FTP (like many others suggest), due to the fact that this file is regularly recreated by many plugins (like all cache plugins or others) and your change will be lost after some time. We can add our modification on a permanent basis with the following code snippet, which you can add to your functions.php:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_filter('mod_rewrite_rules', function ($rules) { 
  return $rules . "\n" . 
  "#Disable directory browsing\n" . 
  "Options -Indexes\n"; 
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then visit the Settings-Permalinks page in your admin panel, it’ll force WordPress to recreate the .htaccess file immediately. After, you can visit YOUR_DOMAIN/wp-includes/ again to make sure that now it displays the 404 message (which is good).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Deny access to the git folder
&lt;/h3&gt;

&lt;p&gt;Skip this step in case you use plain hosting or your website was uploaded manually without GIT.&lt;/p&gt;

&lt;p&gt;Many developers use GIT, which is a wonderful tool. Using GIT on production is also a good idea, but you must disable access to the .git folder via a browser in this case. Otherwise using the .git/HEAD file they'll be able to recreate all a tree of commits and get the source code of your website.&lt;/p&gt;

&lt;p&gt;For this goal, you need to lock all /.git/* requests. You can contact your system administrator or hosting provider, or if you've enough skills and your server uses NGINX then you can reach it by adding the following code (add to the NGINX config file of your website):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# deny access to all special files and folders, like .git, .htaccess
location ~ /\. {
  deny all; 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to restart NGINX after it.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Deny access to the xmlrpc.php file
&lt;/h3&gt;

&lt;p&gt;XML-RPC is an old feature of WordPress, which almost isn’t used nowadays and left just from compatibility with old software reasons. This feature allows interaction with your website directly, without opening in a browser. The bad thing is that hackers can brute force passwords using this feature because even if you’ve changed the login url and added a captcha from the steps above, this feature still gives an option for them to check passwords.&lt;/p&gt;

&lt;p&gt;To deny access to the file you can contact your system administrator or hosting provider, or if you’ve enough skills and your server uses NGINX then you can reach it by adding the following code (add to the NGINX config file of your website):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# wordpress xmlrpc 
location /xmlrpc.php { 
  deny all; 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to restart NGINX after it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;We’ve reviewed the 5 most important steps to protect your installation from hacking. In fact, steps 3–5 can be done by a system administrator (or hosting provider) only once and will work for all websites on your account. Since you’ve done them, you can be sure that you have a very good level of protection, and you can don’t purchase security plugins, which are often heavy and slow down a website.&lt;/p&gt;

&lt;p&gt;In case you’re a WordPress developer, read &lt;a href="https://wplake.org/blog/what-must-know-good-wordpress-developer/"&gt;what must know good WordPress developer&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>wordpressdevelopment</category>
      <category>wordpresssecurity</category>
    </item>
    <item>
      <title>What must know good WordPress developer?</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Wed, 28 Dec 2022 11:54:12 +0000</pubDate>
      <link>https://dev.to/wplake/what-must-know-good-wordpress-developer-2je2</link>
      <guid>https://dev.to/wplake/what-must-know-good-wordpress-developer-2je2</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.amazonaws.com%2Fuploads%2Farticles%2Foqw7pukqlowhnt7gypkg.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Foqw7pukqlowhnt7gypkg.jpeg" alt="Cover" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of us want to be good professionals, regardless of our field. This aim lets us grow and makes us better. I’ll share my list of must-know items, that I’ve learned through the years.&lt;/p&gt;

&lt;p&gt;Below you can find my must-know list for WordPress, split by levels, just like in case when you learn a new language.&lt;/p&gt;

&lt;p&gt;It’s very useful if you learn WordPress or you can use this list to check yourself and fix gaps in your knowledge of WordPress if there are any.&lt;/p&gt;

&lt;p&gt;For each level, I’ve highlighted the main items that you must know. Every item contains a row of topics behind itself, and if you feel that your knowledge is weak when you’re thinking about some item, write down this item in your notes and read more about those items later, dig into that topics to fix gaps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Level A. Getting familiar
&lt;/h3&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fa6iomc9wueu0es2fa38m.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Fa6iomc9wueu0es2fa38m.jpeg" alt="Familiar" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s a minimum level for people that want to have a WordPress website. Even in case you’re not a programmer, you must know the very basic things to be able to manage your WordPress website without issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is WordPress&lt;/li&gt;
&lt;li&gt;How to manage all types of content
(posts, images, taxonomies, users)&lt;/li&gt;
&lt;li&gt;What are CPT and PostMeta&lt;/li&gt;
&lt;li&gt;The file structure of WordPress
(where are unique to specific installation files, where are common)&lt;/li&gt;
&lt;li&gt;How to move a WordPress website from one hosting to another&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Level B. Beginner
&lt;/h3&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fb4w9cw9txdcx6f4umejf.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Fb4w9cw9txdcx6f4umejf.jpeg" alt="Beginner" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s a minimum level for people that want to create WordPress websites. Even if you’re not going to create code, and want to build websites only using ready solutions, you must know these items.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List of most popular plugins and how to use them
(like ACF, WooCommerce, SuperCache, All in One Seo, Redirection and others)&lt;/li&gt;
&lt;li&gt;How WordPress works
(the chain of main hooks and common filters, in which order themes and plugins are loading)&lt;/li&gt;
&lt;li&gt;What are Taxonomy and Terms&lt;/li&gt;
&lt;li&gt;Default thumbnail sizes, how to customize&lt;/li&gt;
&lt;li&gt;Theme’s templates
(all built-in template names, what are page templates, how to add them)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Level C1. Medium
&lt;/h3&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Ffedi6q8vibatbh0zqiw2.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2Ffedi6q8vibatbh0zqiw2.jpeg" alt="medium" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These knowledge will allow you to solve different kinds of tasks with WordPress and build fast websites.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database scheme
(all main tables and their main columns, e.g. posts-postmeta, users-usermeta, terms-taxonomy, options)&lt;/li&gt;
&lt;li&gt;How to use all options of WP_Query
(e.g. with complex filters by meta and taxonomy)&lt;/li&gt;
&lt;li&gt;WooCommerce templates and main hooks, how to customize the checkout&lt;/li&gt;
&lt;li&gt;Cron tasks
(how to create, manage and debug them)&lt;/li&gt;
&lt;li&gt;How WP permalinks work behind the scene, custom rewrite rules
(e.g. how to support several variants of one url, like product1-vs-product2 and product2-vs-product1)&lt;/li&gt;
&lt;li&gt;WP internal cache
(to clearly understand what happens when you call ‘get_post’ or ‘get_post_meta’, to be able to reduce resources usage by your code)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Level C2. Extended
&lt;/h3&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F8hsmo18c8lrst9vwb40o.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2F8hsmo18c8lrst9vwb40o.jpeg" alt="extended" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this level, you feel yourself with WordPress just like a fish in a lake, you always know the best way to solve any task and nothing is too complex for you about WordPress.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multisite mode
(what are common and separated here, how do robots.txt and .htaccess act, and what about users)&lt;/li&gt;
&lt;li&gt;Transients
(what is it, where is it stored, and how to use)&lt;/li&gt;
&lt;li&gt;WP CLI
(e.g. how to regenerate all thumbnails without plugins)&lt;/li&gt;
&lt;li&gt;Weak points of WordPress, how to prevent hacking from them
(brute force of login form, xmlrpc.php, parsing user names via REST API and etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;You’ve read my list of must-know items, and I hope that you’ve found some items interesting for your next learning, and this article will be a small push to improve your knowledge. Do you feel strong? Don’t relax — here I’ve covered only basic topics. Try to dig into tools and topics which you meet every day and you’ll find plenty of new and unknown things. Like with big plugins, each of them is like an ocean, wide and deep. Do you use ACF? Make sure you know all its features, e.g. how to create Gutenberg blocks using the plugin.&lt;/p&gt;

&lt;p&gt;Let’s make WEB and the world better — don’t forget to contribute. Help other people to learn and share your features with others. For this goal, you can publish your own WordPress plugin. You can read &lt;a href="https://wplake.org/blog/how-to-publish-your-own-wordpress-plugin/" rel="noopener noreferrer"&gt;how to publish your own WordPress plugin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
      <category>wordpressthemes</category>
      <category>wordpressplugins</category>
    </item>
    <item>
      <title>10 WordPress plugins that you must know in 2023</title>
      <dc:creator>WPLake</dc:creator>
      <pubDate>Tue, 27 Dec 2022 12:23:05 +0000</pubDate>
      <link>https://dev.to/wplake/10-wordpress-plugins-that-you-must-know-in-2023-o6c</link>
      <guid>https://dev.to/wplake/10-wordpress-plugins-that-you-must-know-in-2023-o6c</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.amazonaws.com%2Fuploads%2Farticles%2Fub9umehp0rxctx68a0wu.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fub9umehp0rxctx68a0wu.jpg" alt="Laptop" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;List of plugins that change the game. Great mix of famous and unknown plugins, that will make your life in 2023 easier.&lt;/p&gt;

&lt;p&gt;I’m an experienced WordPress developer and would like to share with you plugins that I use in development. These plugins are super powerful, but also easy to use, so can be used both by developers and by website owners without deep technical skills.&lt;/p&gt;

&lt;p&gt;The list below contains only free plugins, each item has a short description, my personal comment and a link to download. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. CPT UI
&lt;/h2&gt;

&lt;p&gt;Provides an interface for registering and managing custom post types and taxonomies for your website.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I always create CPT and taxonomies using this plugin instead of adding via code, as it’s very simple, has all the necessary options and can be changed by clients later.&lt;/p&gt;

&lt;p&gt;If you don’t know what is CPT, then you must learn it, as it is one of the main bricks in WordPress. &lt;a href="https://webdesign.tutsplus.com/tutorials/how-to-display-posts-and-acf-fields-in-wordpress--cms-93482" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a good article about CPT and PostMeta.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/custom-post-type-ui/" rel="noopener noreferrer"&gt;CPT UI in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Advanced Custom Fields (ACF)
&lt;/h2&gt;

&lt;p&gt;Gives you full control of your WordPress edit screens &amp;amp; custom field data. Allows you to quickly and easily add fields to WP edit screens with only the click of a few buttons.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most famous plugin in the WordPress universe. It’s the best wrapper for WordPress Meta fields that allows you to manage them without hassle.&lt;/p&gt;

&lt;p&gt;By default WordPress Posts and Pages (and any other post types) have static set of fields. ACF allows you to add any custom characters to your Pages. ACF supports more than 30 different field types: strings, selects, checkboxes, links, images, maps and more.&lt;/p&gt;

&lt;p&gt;Post Meta is the other main brick of WordPress. If you’re a developer, you must know how Meta fields work behind the scene, as ACF is only a wrapper on them. &lt;a href="https://webdesign.tutsplus.com/tutorials/how-to-display-posts-and-acf-fields-in-wordpress--cms-93482" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a good article about it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/advanced-custom-fields/" rel="noopener noreferrer"&gt;ACF in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ACF Views
&lt;/h2&gt;

&lt;p&gt;Allows to display selected ACF fields or Posts anywhere using shortcodes, HTML markup is created automatically by the plugin.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, ACF allows to add fields via UI, but requires coding to display them. This amazing ACF addon extends ACF and allows to display fields without coding.&lt;/p&gt;

&lt;p&gt;This plugin saves a lot of time and efforts, and the main thing for me that his automatic generated markup has a great quality, the same as hand make. The difference that you don’t spend time to create it. As a user you won’t even deal with HTML directly, as plugin generates shortcodes that you paste in target places.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/acf-views/" rel="noopener noreferrer"&gt;ACF Views in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Admin Menu Groups
&lt;/h2&gt;

&lt;p&gt;Allows to create nested menus in the WordPress admin sidebar navigation and organize all menu items in groups as needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WordPress has the great admin UI from the box, but after some time the left (sidebar) menu becomes overloaded, as it reflects all the website data: every Post Type, every plugin has its own menu item there. Navigation becomes a nightmare.&lt;/p&gt;

&lt;p&gt;This plugin solves the issue and allows to create groups and attach every menu item to specific group. It means by default only groups are visible, and items are hidden in a submenu of the related group. For example, I always create 3 groups: WordPress, [Project Name], Plugins. The first contains all the built-in WP items, the second all project related things, like theme settings, theme’s Post Types and similar. The last contains plugin’s items.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/admin-menu-groups/" rel="noopener noreferrer"&gt;Admin Menu Groups in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Members
&lt;/h2&gt;

&lt;p&gt;Allows to set permissions to restrict content on your site by providing a simple user interface (UI) for WordPress’ powerful roles and capabilities system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I always add new roles using this plugin instead of adding via code, as it’s very simple, has all the necessary options and can be changed by clients later.&lt;/p&gt;

&lt;p&gt;If you website must support any type of membership then this plugin will make your life much more easier. You can easy see list of roles that available on your website, unlike the code way, that requires you to research by theme files. Also you can change role’s capatibilies with simple checkboxes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/members/" rel="noopener noreferrer"&gt;Members in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. WP Super Cache
&lt;/h2&gt;

&lt;p&gt;Generates static html files from your dynamic WordPress blog. After a html file is generated your webserver will serve that file instead of processing the comparatively heavier and more expensive WordPress PHP scripts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One of the best cache plugins that I’ve used ever. Simple, free and very efficient. Easy to setup.&lt;/p&gt;

&lt;p&gt;Using some caching plugin in WordPress is a necessary thing to decrease page loading time for your users, it’s also one of the main SEO factors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/wp-super-cache/" rel="noopener noreferrer"&gt;WP Super Cache in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Redirection
&lt;/h2&gt;

&lt;p&gt;Allows easily manage 301 redirections, keep track of 404 errors, and generally tidy up any loose ends your site may have. This can help reduce errors and improve your site ranking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Very useful plugin, whenever you’re changing a page or product slug (permalink), you must create a 301 redirect from the old url to the new one, to make sure that users that request the old url will be redirected to the new one. Also, it’s very important for SEO.&lt;/p&gt;

&lt;p&gt;The plugin has a very clear UI, so you can easily create a new redirect rule or review already existing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/redirection/" rel="noopener noreferrer"&gt;Redirection in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Better search and replace
&lt;/h2&gt;

&lt;p&gt;When moving your WordPress site to a new domain or server, you will likely run into a need to run a search/replace on the database for everything to work correctly. The task can be done easily using this plugin.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every time I have a task to transfer a website, I use this plugin to update urls in a database. It works perfect regardless of amount of data on your website, and it’s the very important moment, as some my clients had large databases (usually shops), and other plugins weren’t able to perform the update.&lt;/p&gt;

&lt;p&gt;The plugin has a great UI, clear report and allows to have ‘a dry run’, which allows you to see tables (and records) that will be changed, without performing the action by the fact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/better-search-replace/" rel="noopener noreferrer"&gt;Better Search Replace in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Updraft Plus
&lt;/h2&gt;

&lt;p&gt;Backup your files and database backups into the cloud and restore with a single click.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Business must be sustainable. Unfortunately your hosting vendor can have issues, regardless of his reputation and number of clients. There is no place that is guaranteed from fires or floods. You must not rely on your vendor only and regularly make backups of your website (database and files) to another place, like any Cloud drives.&lt;/p&gt;

&lt;p&gt;This plugin allows you to make a setup and forget about this task. The plugin creates automatic backups and uploads them to the selected Cloud by schedule. Important moment for me, that the plugin is able to handle large backups, e.g. 20GB (usually shops have big number of images, that important for them like any other data).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/updraftplus/" rel="noopener noreferrer"&gt;Updraft Plus in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. All in One SEO
&lt;/h2&gt;

&lt;p&gt;Improve your WordPress SEO rankings and traffic with comprehensive SEO tools and smart SEO optimizations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As long your website isn’t a closed resource you must use some SEO plugin, that will make your website SEO friendly. There are so many things, that aren’t presented by default, like structured data, and SEO plugins take care of it.&lt;/p&gt;

&lt;p&gt;There are several good and famous SEO plugins, but my personal choice is “All in One SEO”, it gives better results for my clients than others.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/all-in-one-seo-pack/" rel="noopener noreferrer"&gt;All in One SEO in the WordPress repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I hope you’ve picked up something new from this list for your current and further projects. Share plugins that you use in comments, and we’ll make the article even more helpful.&lt;/p&gt;

&lt;p&gt;If you found this article useful, don’t forget to ‘applaud’ and share with friends.&lt;/p&gt;

&lt;p&gt;Good luck in the 2023 year.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>news</category>
    </item>
  </channel>
</rss>
