<?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: Alex Standiford</title>
    <description>The latest articles on DEV Community by Alex Standiford (@alexstandiford).</description>
    <link>https://dev.to/alexstandiford</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%2F554993%2Fca466ef6-cb24-4f36-879f-c75564b85719.jpeg</url>
      <title>DEV Community: Alex Standiford</title>
      <link>https://dev.to/alexstandiford</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexstandiford"/>
    <language>en</language>
    <item>
      <title>Make Webpack Configuration Easy With wordpress/scripts</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Fri, 09 Jul 2021 19:27:33 +0000</pubDate>
      <link>https://dev.to/alexstandiford/make-webpack-configuration-easy-with-wordpress-scripts-26kk</link>
      <guid>https://dev.to/alexstandiford/make-webpack-configuration-easy-with-wordpress-scripts-26kk</guid>
      <description>&lt;p&gt;I believe the single-biggest barriers to entry for modern Javascript development are the build tools that are necessary to use it effectively. It’s all a black box, breaks with vague error messages, that ultimately lead you down a tunnel of “google and pray”. Unless you’ve spent &lt;em&gt;a lot&lt;/em&gt; of time with Webpack, it’s hard to troubleshoot when it inevitably breaks. (and seriously, who freaking has time to learn all the idiosyncrasies of Webpack?)&lt;/p&gt;

&lt;p&gt;But even with that, the benefits vastly outpace the drawbacks. When Javascript build tools work, they work beautifully – compiling your scripts down to the smallest size, ensuring that it works on all web browsers, and in the case of WordPress, even automatically enqueuing dependent scripts that are a part of WordPress core.&lt;/p&gt;

&lt;p&gt;Thankfully, WordPress has its own Webpack configuration that is built specifically to make developing in WordPress easier. It includes all of the babel configurations, and build tools you need to compile WordPress-specific Javascript as effectively as possible. This amazing, time-saving, godsend utility is an &lt;a href="https://www.npmjs.com/"&gt;NPM&lt;/a&gt; package, and it is called &lt;a href="//https//developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/"&gt;@wordpress/scripts&lt;/a&gt;. It’s not perfect, and you’ll still find yourself occasionally scratching your head wondering &lt;em&gt;“what the hell is this error?”&lt;/em&gt; but, in the grand scheme of things I’ve found that I’m &lt;em&gt;a lot&lt;/em&gt; less frustrated when using this package. It usually just &lt;em&gt;works&lt;/em&gt;, and that feels pretty solid.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up @wordpress/scripts
&lt;/h2&gt;

&lt;p&gt;Regardless of if you’re working with a theme or a plugin, the process is the same – install the package using NPM. Go to the root directory of your plugin, or theme, and run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @wordpress/scripts –save-dev&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up package.json
&lt;/h3&gt;

&lt;p&gt;Once installed, you need to add some scripts to your &lt;code&gt;package.json&lt;/code&gt; file. At minimum, you’re going to need &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;start&lt;/code&gt;. Your JSON would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plugin_name_replace_me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Plugin Description Replace Me"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-scripts build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-scripts start"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@wordpress/scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"15.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above is the &lt;code&gt;package.json&lt;/code&gt; used in the &lt;a href="https://github.com/Underpin-WP/underpin-plugin-boilerplate"&gt;Underpin plugin boilerplate&lt;/a&gt;, but it would work in just about any plugin or theme. The key part is the &lt;code&gt;scripts&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-scripts build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-scripts start"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;scripts&lt;/code&gt; object may have additional scripts, and that’s fine. All this does is register command line scripts that can be ran inside the directory that contains this &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Your Javascript File
&lt;/h3&gt;

&lt;p&gt;Now that your &lt;code&gt;package.json&lt;/code&gt; is all-set, it’s time to create your Javascript file. This file is going to be what @wordpress/scripts uses to create the &lt;em&gt;actual&lt;/em&gt; Javascript file that the site will be used by your browser. By default,&lt;br&gt;
this script file must be placed inside &lt;code&gt;./src/index.js&lt;/code&gt;, but this can be customized to be something else if-needed. More on that later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using Build and Start
&lt;/h2&gt;

&lt;p&gt;For example, if you run &lt;code&gt;npm run start&lt;/code&gt; it will actually run the &lt;code&gt;start&lt;/code&gt; command inside the @wordpress/scripts package. Conversely, if you run &lt;code&gt;npm run build&lt;/code&gt;, it will run the &lt;code&gt;build&lt;/code&gt; command inside the @wordpress/scripts package. There are a handful of other useful commands, such as linters and translation compilation built into this command, but we’re not going to cover those in this post. You can see them in the &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#available-scripts"&gt;@wordpress/scripts documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;start&lt;/code&gt; will compile your Javascript and your CSS/SCSS into something all web browsers can read, but each one does this a little differently.&lt;/p&gt;
&lt;h3&gt;
  
  
  Start
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm run start&lt;/code&gt; will create an un-minified version of your script, and include a map file so that you can easily debug your scripts. Without this map file, you’d get vague errors that point to the wrong file because the browser wouldn’t otherwise know where these errors are.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;start&lt;/code&gt; is ran, it will continue to run in the background, and automatically regenerate your scripts and styles when the files are changed. This is perfect for when you’re still building your scripts and styles, since it runs quietly in the background, and automatically regenerates everything for you.&lt;/p&gt;
&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;start&lt;/code&gt;‘s priority is to help you while you’re developing, but because of this your script files will be &lt;em&gt;way&lt;/em&gt; bigger than you’d want them to be on your live site (we’re talking megabytes people_, megabytes!_). This is where &lt;code&gt;build&lt;/code&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt; will create the absolute smallest file sizes it can generate by minifying scripts, and optimizing their contents. This is ideal for when you’re ready to use these themes on a live site (production). Unlike &lt;code&gt;start&lt;/code&gt;, this command will generate your scripts and styles &lt;em&gt;one time&lt;/em&gt;, instead of running in the background. The scripts and styles generated by build will look garbled. With variable names minified, and everything compressed to a single line of code, so you don’t want to use this when developing. Instead, you’ll want to run this as a step in your deployment process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enqueuing Your Scripts
&lt;/h2&gt;

&lt;p&gt;After you run either &lt;code&gt;build&lt;/code&gt; or &lt;code&gt;watch&lt;/code&gt;, a compiled version of your script will be located inside the build directory.&lt;br&gt;
Now you have to instruct WordPress when to add this script to your site’s &lt;code&gt;head&lt;/code&gt; tag. In WordPress this is done by&lt;br&gt;
“enqueuing” the script.&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic WordPress Enqueue Script Example
&lt;/h3&gt;

&lt;p&gt;To enqueue your script, you have to first register it, and then enqueue it at the right time. If you’re using &lt;a href="https://www.wpdev.academy/concepts/introducing-underpin-a-modern-wordpress-framework/"&gt;Underpin&lt;/a&gt;, that would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'index.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'The description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Script'&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above instructs Underpin to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register a new script called &lt;code&gt;test&lt;/code&gt;, where the JS URL is &lt;code&gt;build/index.js&lt;/code&gt;. Underpin’s &lt;code&gt;js_url()&lt;/code&gt; method defaults to the &lt;code&gt;build&lt;/code&gt; in your plugin or theme.&lt;/li&gt;
&lt;li&gt;Automatically enqueue this script on the front-end. This is done inside the &lt;code&gt;Enqueue_Script&lt;/code&gt; middleware. You can learn more about how script middleware works &lt;a href="https://github.com/underpin-wp/script-loader#script-middleware"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you wanted to-do this &lt;em&gt;without&lt;/em&gt; Underpin, it would look more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/index.js'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatically Setting Script Dependencies
&lt;/h3&gt;

&lt;p&gt;A key feature of @wordpress/scripts is&lt;br&gt;
the &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/"&gt;dependency-extraction-webpack-plugin&lt;/a&gt;. This wonderful, glorious, amazing Webpack loader will automatically detect if your script has imported any WordPress core library, and will remove it from your compiled script. This keeps your script as small as possible, and also ensures that your script doesn’t somehow clash with another plugin’s script.&lt;/p&gt;

&lt;p&gt;The problem, however, is that by doing this, your script &lt;em&gt;won’t work&lt;/em&gt; unless &lt;em&gt;all&lt;/em&gt; of the these imported scripts are loaded &lt;em&gt;before&lt;/em&gt; your script is loaded. This means, that you would have to manually enqueue every single script you imported, as well as any script &lt;em&gt;those scripts&lt;/em&gt; imported, too. As you can imagine, this would be a nightmare to&lt;br&gt;
maintain.&lt;/p&gt;

&lt;p&gt;To work around this, dependency-extraction-webpack-plugin will automatically generate a PHP file with an array of all of the dependencies your script. This array can be passed directly to your registered script, and it will automatically enqueue all of the necessary scripts just before your script automatically.&lt;/p&gt;

&lt;p&gt;And the best part? This happens when your script is compiled using either &lt;code&gt;build&lt;/code&gt; or &lt;code&gt;watch&lt;/code&gt;, and when it’s all set-up it works seamlessly. You won’t even notice that these scripts are not included in your file.&lt;/p&gt;

&lt;p&gt;The generated file will be compiled inside your build directory along with your script. It’s just a matter of using that PHP file when you register your script.&lt;/p&gt;

&lt;p&gt;With Underpin, that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'index.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'The description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'deps'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/index.asset.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// path to dependency file generated by webpack&lt;/span&gt;
        &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Script'&lt;/span&gt; &lt;span class="c1"&gt;// Enqueue the script on the front-end&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key change is the &lt;code&gt;deps&lt;/code&gt; argument. &lt;code&gt;deps&lt;/code&gt; can be an array of registered script handles, or a path fo a PHP file. If the path to the asset file exists, it will automatically set the dependencies from the file.&lt;/p&gt;

&lt;p&gt;Without Underpin, this can also be done but requires a bit of extra logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check to see if the file exists.&lt;/span&gt;
&lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/index.asset.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Set default fallback to dependencies array&lt;/span&gt;
&lt;span class="nv"&gt;$deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// If the file can be found, use it to set the dependencies array.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$deps&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'dependencies'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Register script&lt;/span&gt;
&lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/index.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deps&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Enqueue the script later-on&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Customize Your Webpack Configuration
&lt;/h2&gt;

&lt;p&gt;@wordpress/scripts includes a default Webpack configuration, but this can be overridden with your own webpack configuration. This is done by adding a &lt;code&gt;webpack.config.js&lt;/code&gt; file in the root directory of your plugin or theme. When this is added, @wordpress/scripts will automatically use &lt;em&gt;your&lt;/em&gt; Webpack config instead of the one that comes with @wordpress/scripts.&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; add your 100% custom Webpack config to this file, and completely override the configuration that comes with @wordpress/scripts, but at that point, there’s not much point in using @wordpress/scripts. Instead, I find that it makes a lot more sense to &lt;em&gt;extend&lt;/em&gt; the config that comes with @wordpress/scripts, and modify the parts you need to modify, instead. The Underpin plugin boilerplate accomplishes this like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * WordPress Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/scripts/config/webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add any overrides to the default here.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above example uses Javascript’s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax"&gt;spread operator&lt;/a&gt; to take the default Webpack configuration included in @wordpress/scripts, and override sections of the configuration with your own customized config. This allows you to change the parts you want, and still use @wordpress/scripts to its fullest potential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Custom Entrypoints With @wordpress/scripts
&lt;/h3&gt;

&lt;p&gt;By default, @wordpress/scripts will allow you to create a single entrypoint file – &lt;code&gt;src/index.js&lt;/code&gt;, but what happens if you want to create &lt;em&gt;multiple&lt;/em&gt; Javascript files? Maybe you need to have one script for an admin interface, and another for the front-end of the site. Using the method above, you can override the &lt;code&gt;entry&lt;/code&gt; configuration of your Webpack config, and instruct @wordpress/scripts to create two files, instead.&lt;/p&gt;

&lt;p&gt;Here is an example of the webpack.config.js file that is used in our &lt;a href="https://www.wpdev.academy/course/beer-lister-plugin/"&gt;WordPress plugin development course&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * External Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * WordPress Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/scripts/config/webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...{&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beer-admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer-admin.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beer-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer-list.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Javascript’s spread operator, this configuration extends the default @wordpress/scripts configuration object and replace the &lt;code&gt;entry&lt;/code&gt; configuration. Instead of creating the default index.js file, this instructs Webpack to create three files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;build/admin.js will be created from src/admin.js&lt;/li&gt;
&lt;li&gt;build/beer-admin.js will be created from src/beer-admin.js&lt;/li&gt;
&lt;li&gt;build/beer-list.css will be created from src/beer-list.css&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From there, you would need to enqueue the styles and scripts for each item just like you did in the example above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;@wordpress/scripts simplifies working with Webpack, and makes it possible to create &lt;a href="https://www.wpdev.academy/wordpress-tutorials/create-a-custom-gutenberg-block-plugin-with-underpin/"&gt;custom Gutenberg blocks&lt;/a&gt;, allow you to utilize core WordPress libraries like &lt;a href="https://www.wpdev.academy/concepts/api-fetch-the-wordpress-library-you-didnt-know-you-needed/"&gt;the awesome ApiFetch library&lt;/a&gt;. It can be extended, manipulated, and changed to suit your needs, and it can ensure that your scripts don’t conflict with other scripts. Once you get the hang of it, you’ll never want to go back to a world where you don’t have this tool at your disposal. Underpin has &lt;a href="https://github.com/Underpin-WP/underpin-theme-boilerplate"&gt;theme&lt;/a&gt; and &lt;a href="https://github.com/Underpin-WP/underpin-plugin-boilerplate"&gt;plugin&lt;/a&gt; boilerplates that includes this library, and sets everything up to extend this compilation tool quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join &lt;a href="http://community.wpdev.academy"&gt;WP Dev Academy’s Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webpack</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>API Fetch – The WordPress Library You Didn’t Know You Needed</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Sun, 04 Jul 2021 14:14:27 +0000</pubDate>
      <link>https://dev.to/alexstandiford/api-fetch-the-wordpress-library-you-didn-t-know-you-needed-5hjp</link>
      <guid>https://dev.to/alexstandiford/api-fetch-the-wordpress-library-you-didn-t-know-you-needed-5hjp</guid>
      <description>&lt;p&gt;One of my favorite WordPress packages that nobody seems to talk about right now is @wordpress/apiFetch. This library works kind-of like your traditional fetch library, only it’s built into WordPress, plays nice with backwards compatibility, and is capable of using middleware to transform some, or all of the REST API calls that it receives.&lt;/p&gt;

&lt;p&gt;There are many people who go fully headless with GraphQL, Gatsby and the like, but &lt;a href="https://www.wpdev.academy/concepts/headless-wordpress-is-overrated-a-case-for-the-nearly-headless-web-app/"&gt;I prefer a semi-headless approach&lt;/a&gt; to WordPress. This allows me to use WordPress plugins to their full potential, but still make use of the REST API when I can. One of the key pieces of this approach is making extensive use of the apiFetch library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up api-fetch
&lt;/h2&gt;

&lt;p&gt;api-fetch is a &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/"&gt;built-in package&lt;/a&gt; that you can install just like any other NPM package, but it’s best to instead require the packaged up API Fetch library as a dependency. This will minimize conflicts with other plugins that are also using api-fetch.&lt;/p&gt;

&lt;p&gt;To use it, you just need to add it to your list of dependencies on your registered script. If you’re using &lt;a href="https://www.wpdev.academy/concepts/introducing-underpin-a-modern-wordpress-framework/"&gt;Underpin&lt;/a&gt;, that would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'path/to/script/src'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'The description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'deps'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'wp-api-fetch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wp-polyfill'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Script'&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you wanted to-do this &lt;em&gt;without&lt;/em&gt; Underpin, it would look more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'path/to/script/src'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'wp-api-fetch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wp-polyfill'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;em&gt;okay&lt;/em&gt;, but it’s way better If you’re extending &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/"&gt;wordpress/scripts&lt;/a&gt;‘ Webpack configuration. This is because you can actually use &lt;code&gt;import&lt;/code&gt; statements inside your Javascript for any &lt;code&gt;@wordpress&lt;/code&gt; package, just like as-if you had installed the NPM package. &lt;/p&gt;

&lt;p&gt;WordPress has a truly magical Webpack loader that will actually &lt;em&gt;extract the WordPress-specific dependencies, and generate the dependencies array as a PHP file.&lt;/em&gt; You can then require this file, and it will automatically set your script dependencies for you. That way you can just write your JS as you were, and it will automatically update the dependency array for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is way easier&lt;/strong&gt; once you get it set-up. You can see how Underpin sets up a Webpack config for these sorts of things in &lt;a href="https://github.com/Underpin-WP/underpin-plugin-boilerplate"&gt;Underpin’s plugin boilerplate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you get that all-set, you can instead use this file to set dependencies. With Underpin, that would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'path/to/script/src'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'The description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'deps'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;plugin_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/test.asset.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// path to dependency file generated by webpack&lt;/span&gt;
        &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Script'&lt;/span&gt; &lt;span class="c1"&gt;// Enqueue the script on the front-end&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without Underpin, it can be done but requires a bit of extra logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check to see if the file exists.&lt;/span&gt;
&lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'path/to/file'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Set default fallback to dependencies array&lt;/span&gt;
&lt;span class="nv"&gt;$deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// If the file can be found, use it to set the dependencies array.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;file_exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$deps_file&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$deps&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'dependencies'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Register script&lt;/span&gt;
&lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'path/to/script/src'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deps&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Enqueue the script later-on&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using api-fetch
&lt;/h2&gt;

&lt;p&gt;Like I mentioned earlier, API fetch works &lt;em&gt;a lot&lt;/em&gt; like the regular fetch API, but the syntax is a little different. Let’s say you wanted to get a list of posts via REST. It would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;apiFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'/wp/v2/posts'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would return a&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Javascript Promise&lt;/a&gt;, much like how the normal Fetch API, however, unlike Fetch, this will return a parsed object instead of a response object after the promise resolves.&lt;/p&gt;

&lt;p&gt;The example above doesn’t do anything with the object, however. There’s two primary methods to use the response for this. One is with the &lt;code&gt;then&lt;/code&gt; statement, or &lt;code&gt;async/await&lt;/code&gt;. Then is a little simpler to set up, but I find that async/await is easier to read.&lt;/p&gt;

&lt;p&gt;Using then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;apiFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/wp/v2/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/** do something with the posts **/&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using async/await:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/wp/v2/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Do something with your posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Middleware
&lt;/h2&gt;

&lt;p&gt;Okay, so this is all fine and dandy, but what’s the point of using this library when you can just use fetch and accomplish the same thing? The answer? Middleware.&lt;/p&gt;

&lt;p&gt;apiFetch middleware allows you to automatically run a callback function, and mutate the request on &lt;em&gt;every&lt;/em&gt; apiFetch call. This allows you to do some really cool things. api-fetch comes with three “built-in” middlewares that you can set up using a method.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;createRootURLMiddleware&lt;/strong&gt; will Automatically set the root REST API url so you only have to pass the relative path in your requests. This is important because the REST API URL can differ from WordPress install to WordPress install (don’t get me started with multisite)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;createNonceMiddleware&lt;/strong&gt; allows you to automatically authenticate a logged in user with a nonce.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;createPreloadingMiddleware&lt;/strong&gt; will allow you to preload REST API requests so that they load instantly instead of making another request on the server when that request is called on the front-end. (More on this later)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These can be set up inside Javascript directly, but it is common to load these as an inline script just after the script is enqueued using &lt;code&gt;wp_add_inline_script&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add a nonce to all requests&lt;/span&gt;
&lt;span class="nf"&gt;wp_add_inline_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="c1"&gt;// Script handle to add after&lt;/span&gt;
    &lt;span class="s1"&gt;'apiFetch.use( apiFetch.createNonceMiddleware( '&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;wp_create_nonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_rest'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' ) )'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Set the root URL for all requests&lt;/span&gt;
&lt;span class="nf"&gt;wp_add_inline_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="c1"&gt;// Script handle to add after&lt;/span&gt;
    &lt;span class="s1"&gt;'apiFetch.use( apiFetch.createRootURLMiddleware( '&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;get_rest_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' ) )'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preloading Middleware
&lt;/h2&gt;

&lt;p&gt;The first two built-in middlewares have a fairly obvious use-case – most REST requests need at least one of these in-order for the site to work consistently. Prior to middleware, this was done using &lt;code&gt;wp_localize_script&lt;/code&gt;, and even at that time it was necessary to pass one, or both of these things in every request.&lt;/p&gt;

&lt;p&gt;Preloading, however, is a bit different, and is defionitely not something that should be used in most cases, but there are times where it makes &lt;em&gt;a lot&lt;/em&gt; of sense to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does Preloading Middleware do?
&lt;/h3&gt;

&lt;p&gt;Preloading middleware accepts a Javascript object where the object keys are specific WordPress REST API endpoints, and the values are the corresponding result of that particular endpoint. When this middleware is set, apiFetch will automatically check to see if the provided REST endpoint has been pre-loaded. If it has, it will use the preloaded response &lt;em&gt;instead of making another fetch request.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When Should This Be Used?
&lt;/h3&gt;

&lt;p&gt;I find the best time to use this feature is when you have had to retrieve the same data on the server as what will need to be retrieved on the actual site. For example – a WordPress request on the front-end &lt;em&gt;always&lt;/em&gt; runs the page’s query when loading the content. If you’re using a semi-headless approach, and are ultimately using REST to fetch the post content after the site is loaded, you’re essentially forcing WordPress to fetch that data from the database twice. That kind-of sucks!&lt;/p&gt;

&lt;p&gt;But you can, however, use a method similar to what we did above to preload the REST API data that has already been retrieved by the database. Something like this will take advantage of the object cache, and allow your web app to instantly have access to the posts instead of waiting for another request to complete. It’s a win-win because you’re taking advantage of data that has already been retrieved from the database, and reducing your requests on page load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// Preload the fetch data&lt;/span&gt;

    &lt;span class="nv"&gt;$current_page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_query_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'paged'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Get the current page, and use page 1 as the default.&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;WP_REST_Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Instantiate a fake REST request.&lt;/span&gt;
    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set_query_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'page'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$current_page&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Set current page&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rest_do_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Run the REST request.&lt;/span&gt;

    &lt;span class="nv"&gt;$preloaded_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'wp/v2/posts?page='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$current_page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nf"&gt;wp_add_inline_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'test'&lt;/span&gt; &lt;span class="c1"&gt;// Script handle to add after&lt;/span&gt;
        &lt;span class="s1"&gt;'apiFetch.use( apiFetch.createPreloadingMiddleware( '&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;wp_json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$preloaded_data&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;' ) )'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Javascript to inject after the script. This will add the nonce header to all rest requests on the apiFetch object.&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make Your Own Middleware
&lt;/h2&gt;

&lt;p&gt;The sky is the limit with this. In some plugins, I’ve loaded in my own custom cache solution that automatically caches the REST API response, passed-in required params for different authentication methods, all kinds of things. Creating your own middleware is as simple as doing something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;apiFetch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do things, like manipulate the provided options&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could be done either in your Javascript, or inside of PHP using the method detailed with other middlewares above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;api-fetch is an awesome little fetch utility that I use all the time when working in a RESTful manner with WordPress. It’s a powerful utility that has been tried and tested on tons and tons of sites, on different browsers, and in different contexts. When used correctly, it can make working with WordPress’s REST API a lot easier. When I look at this method, and reflect on the times before the REST API, I can’t help but chuckle. It’s so much easier now, provided you know how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join WP Dev Academy’s &lt;a href="http://community.wpdev.academy"&gt;Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
      <category>php</category>
      <category>react</category>
    </item>
    <item>
      <title>
Create a Custom Gutenberg Block Plugin with Underpin</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Fri, 02 Jul 2021 14:23:40 +0000</pubDate>
      <link>https://dev.to/alexstandiford/create-a-custom-gutenberg-block-plugin-with-underpin-58g5</link>
      <guid>https://dev.to/alexstandiford/create-a-custom-gutenberg-block-plugin-with-underpin-58g5</guid>
      <description>&lt;p&gt;This tutorial covers how to set up a custom Gutenberg block plugin in WordPress using Underpin. This plugin can hold all of your site’s custom Gutenberg blocks, and allows you to bring your custom blocks with you when you change themes in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Underpin
&lt;/h2&gt;

&lt;p&gt;If you haven’t already, the first thing you need to-do is actually &lt;em&gt;install Underpin&lt;/em&gt; on your site. Underpin uses Composer to handle its dependencies, and it can be loaded in as a &lt;a href="https://wordpress.org/support/article/must-use-plugins/"&gt;Must-Use plugin&lt;/a&gt;, or directly inside of your plugin. It really just depends on where you have Composer set up on your site. I like to install it as a mu-plugin for personal sites, and directly inside the plugin when I’m building a plugin that is intended to be distributed. In this lesson, I’ll assume you’re going to use it as a must-use plugin.&lt;/p&gt;

&lt;p&gt;If you already have Underpin installed, you can skip this entirely.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;wp-content&lt;/code&gt; directory, create a new directory called mu-plugins. If &lt;code&gt;mu-plugins&lt;/code&gt; already exists, then skip this step.&lt;/p&gt;

&lt;p&gt;Now create a &lt;code&gt;composer.json&lt;/code&gt; file inside the &lt;code&gt;mu-plugins&lt;/code&gt; directory. Most of this is the default settings for a &lt;code&gt;composer.json&lt;/code&gt; file, the key difference is that &lt;code&gt;extra.installer-paths&lt;/code&gt; is tweaked to force &lt;code&gt;wordpress-muplugins&lt;/code&gt; to simply be installed directly in the &lt;code&gt;vendor&lt;/code&gt; directory. This is necessary because Underpin is considered a &lt;code&gt;mu-plugin&lt;/code&gt; by Composer, and will install in an improper directory, otherwise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wpda/muplugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"installer-paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"vendor/{$vendor}/{$name}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type:wordpress-muplugin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new PHP file inside the &lt;code&gt;mu-plugins&lt;/code&gt; directory. It can named be whatever you want it to be. WordPress will automatically include this file, and run it on every page load. This happens &lt;em&gt;really early&lt;/em&gt; in WordPress’s runtime so there are some limitations to this, but for our needs it’s perfect.&lt;/p&gt;

&lt;p&gt;Use this code to include composer’s autoloader. This will automatically install and set up Underpin so you can use it anywhere else. This includes any plugins custom to this site, or your theme. Essentially, this makes Underpin as close to core functionality as possible. The only caveat is you have to &lt;em&gt;remember to upload the mu-plugins directory to your live site.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Load Underpin, and its dependencies.&lt;/span&gt;
&lt;span class="nv"&gt;$autoload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$autoload&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s install Underpin. Open up your command-line, navigate to your site’s &lt;code&gt;mu-plugins&lt;/code&gt; directory, and then run this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require underpin/underpin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Booya! You now have Underpin installed. Now, let’s use it in a plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up The Gutenberg Block Plugin
&lt;/h2&gt;

&lt;p&gt;A fundamental practice of WordPress is to create WordPress plugins for each piece of custom functionality for your site. This allows you to enable/disable these plugins as-needed, and potentially re-use the plugin on other sites in the future.&lt;/p&gt;

&lt;p&gt;We’re going to use &lt;a href="https://github.com/Underpin-WP/underpin-plugin-boilerplate"&gt;Underpin’s plugin boilerplate&lt;/a&gt; to help set up this plugin quickly. This plugin does a few key things for us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It sets up our Underpin plugin instance&lt;/li&gt;
&lt;li&gt; It comes with a WordPress-ready Webpack config&lt;/li&gt;
&lt;li&gt; It sets up the file headers that WordPress needs in-order to recognize the file as a plugin&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To use this, navigate to &lt;code&gt;wp-content/plugins&lt;/code&gt; and clone the boilerplate. Then you’ll need to-do a few find/replaces in the boilerplate.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Replace &lt;code&gt;plugin-name-replace-me&lt;/code&gt; with &lt;code&gt;custom-blocks&lt;/code&gt; (it can be whatever you want, just make sure spaces use dashes, and it’s all lowercase)&lt;/li&gt;
&lt;li&gt; Replace &lt;code&gt;Plugin Name Replace Me&lt;/code&gt; with &lt;code&gt;Custom Blocks&lt;/code&gt; (Again, whatever you want just has to use spaces and Title Case)&lt;/li&gt;
&lt;li&gt; Replace &lt;code&gt;plugin_name_replace_me&lt;/code&gt; with &lt;code&gt;custom_blocks&lt;/code&gt; (Same thing applies here, but you should use snake_case)&lt;/li&gt;
&lt;li&gt; Replace &lt;code&gt;Plugin_Name_Replace_Me&lt;/code&gt; with &lt;code&gt;Custom_Blocks&lt;/code&gt; (using Upper_Snake_Case)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, your plugin’s &lt;code&gt;bootstrap.php&lt;/code&gt; file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Underpin\Abstracts\Underpin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Underpin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'root_namespace'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom_Blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text_domain'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_php_version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'7.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_wp_version'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'5.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'version'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lock and load.&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set Up The Loaders
&lt;/h2&gt;

&lt;p&gt;With Underpin, everything is registered using an Underpin loader. These loaders will handle actually &lt;em&gt;loading&lt;/em&gt; all of the things you need to register. Everything from scripts, to blocks, even admin pages, can all be added directly using Underpin’s loaders. Loaders make it so that everything uses an identical pattern to add items to WordPress. With this system, all of these things use nearly &lt;em&gt;exact&lt;/em&gt; same set of steps to register.&lt;/p&gt;

&lt;p&gt;To build a Gutenberg block, we need to add &lt;em&gt;at least&lt;/em&gt; two loaders, but you usually end up needing three.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A block loader&lt;/li&gt;
&lt;li&gt; A script loader&lt;/li&gt;
&lt;li&gt; A style loader (optional)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create the Gutenberg Block Loader
&lt;/h3&gt;

&lt;p&gt;First things first, install the &lt;a href="https://github.com/Underpin-WP/underpin-block-loader"&gt;block loader&lt;/a&gt;. In your command line, navigate to your &lt;code&gt;mu-plugins&lt;/code&gt; directory and run this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require underpin/block-loader&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will install the loader necessary to register blocks in Underpin. Now that it’s installed, you can &lt;em&gt;register your block&lt;/em&gt; by chaining &lt;code&gt;custom_blocks&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;                                      &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what’s going on above.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;custom_blocks()&lt;/code&gt; actually retrieves this plugin’s instance of Underpin&lt;/li&gt;
&lt;li&gt; &lt;code&gt;blocks()&lt;/code&gt; Retrieves the loader registry for this instance of Underpin&lt;/li&gt;
&lt;li&gt; &lt;code&gt;add()&lt;/code&gt; actually &lt;em&gt;adds&lt;/em&gt; this block to the registry&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Behind the scenes, Underpin will automatically create an instance of &lt;a href="https://github.com/Underpin-WP/underpin-block-loader/blob/master/Block.php"&gt;Block&lt;/a&gt;, which then automatically runs &lt;code&gt;register_block_type&lt;/code&gt; using the provided &lt;code&gt;args&lt;/code&gt; and &lt;code&gt;type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, your plugin’s &lt;code&gt;bootstrap.php&lt;/code&gt; will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Underpin\Abstracts\Underpin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Underpin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'root_namespace'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom_Blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text_domain'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_php_version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'7.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_wp_version'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'5.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'version'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lock and load.&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;                                      &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Gutenberg Block Script
&lt;/h3&gt;

&lt;p&gt;Next up, install the &lt;a href="https://github.com/Underpin-WP/script-loader"&gt;script loader.&lt;/a&gt; In your command line, navigate to your &lt;code&gt;mu-plugins&lt;/code&gt; directory and run this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require underpin/script-loader&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Exactly like blocks, this will install the loader necessary to register scripts in Underpin. With it, you can register scripts like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                &lt;span class="c1"&gt;// Script Handle used in wp_*_script&lt;/span&gt;
    &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Src used in wp_register_script&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom Blocks Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;// Names your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Script that loads in the custom blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Describes your script. Used for debugging.&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what’s going on above.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;custom_blocks()&lt;/code&gt; actually retrieves this plugin’s instance of Underpin&lt;/li&gt;
&lt;li&gt; &lt;code&gt;scripts()&lt;/code&gt; Retrieves the loader registry for this instance of Underpin&lt;/li&gt;
&lt;li&gt; &lt;code&gt;add()&lt;/code&gt; actually &lt;em&gt;adds&lt;/em&gt; this script to the registry&lt;/li&gt;
&lt;li&gt; &lt;code&gt;custom_blocks()-&amp;gt;js_url()&lt;/code&gt; is a helper function that automatically gets the javascript url for this plugin. This is configured in the &lt;code&gt;custom_blocks&lt;/code&gt; function directly, and defaults to &lt;code&gt;build&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Behind the scenes, Underpin will automatically create an instance of &lt;a href="https://github.com/Underpin-WP/underpin-block-loader/blob/master/Block.php"&gt;Script&lt;/a&gt;, which then automatically runs &lt;code&gt;wp_register_script&lt;/code&gt; using the arguments passed into the registry.&lt;/p&gt;

&lt;h4&gt;
  
  
  Enqueuing the Script
&lt;/h4&gt;

&lt;p&gt;Now that the script is registered, you actually have to &lt;em&gt;enqueue&lt;/em&gt; the script as well. We could manually enqueue the script, but instead we’re going to use Underpin’s &lt;a href="https://github.com/underpin-WP/underpin#middleware"&gt;Middleware&lt;/a&gt; functionality to automatically enqueue this script in the admin area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                &lt;span class="c1"&gt;// Script Handle used in wp_*_script&lt;/span&gt;
    &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Src used in wp_register_script&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom Blocks Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;// Names your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Script that loads in the custom blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Describes your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Admin_Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// Enqueues the script in the admin area&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;bootstrap.php&lt;/code&gt; file should now look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cm"&gt;/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Underpin\Abstracts\Underpin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Underpin&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'root_namespace'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom_Blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text_domain'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_php_version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'7.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'minimum_wp_version'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'5.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'version'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lock and load.&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'underpin/custom-block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;                                      &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                &lt;span class="c1"&gt;// Script Handle used in wp_*_script&lt;/span&gt;
    &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Src used in wp_register_script&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom Blocks Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;// Names your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Script that loads in the custom blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Describes your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Admin_Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// Enqueues the script in the admin area&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Blocks Javascript File
&lt;/h2&gt;

&lt;p&gt;First, you need to modify your &lt;code&gt;webpack.config.js&lt;/code&gt; to create a new entry file. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * WordPress Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/scripts/config/webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * External Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...{&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-blocks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-blocks.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Create "custom-blocks.js" file in "build" directory&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs Webpack to take a JS file located in your plugin’s &lt;code&gt;src&lt;/code&gt; directory, and compile it into &lt;code&gt;build/custom-blocks.js&lt;/code&gt;. From here, we need to create a new file in the &lt;code&gt;src&lt;/code&gt; directory called &lt;code&gt;custom-blocks.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we have to register the block in our Javascript, as well. This will allow us to customize how this block behaves in the Gutenberg editor. In this lesson, we’re going to just create a &lt;em&gt;very simple&lt;/em&gt; “Hello World” block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Imports the function to register a block type.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerBlockType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/blocks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Imports the translation function&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;__&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Registers our block type to the Gutenberg editor.&lt;/span&gt;
&lt;span class="nx"&gt;registerBlockType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-blocks/hello-world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display Hello World text on the site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello-world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello-world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, so what’s going on here?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We’re importing &lt;code&gt;registerBlockType&lt;/code&gt; so we can use it in this file&lt;/li&gt;
&lt;li&gt; We’re also importing &lt;code&gt;__&lt;/code&gt; so we can make translate-able strings&lt;/li&gt;
&lt;li&gt; We are running &lt;code&gt;registerBlockType&lt;/code&gt; to register our “Hello World” block to the editor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now run &lt;code&gt;npm install&lt;/code&gt; and &lt;code&gt;npm run start&lt;/code&gt;. This will create two files in your &lt;code&gt;build&lt;/code&gt; directory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;custom-blocks.js&lt;/strong&gt; – This is your compiled Javascript file that gets enqueued by Underpin’s script loader.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;custom-blocks-asset.php&lt;/strong&gt; – This asset file tells WordPress what additional scripts need enqueued in-order for this script to work properly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will notice that we did &lt;em&gt;not&lt;/em&gt; install &lt;code&gt;@wordpress/blocks&lt;/code&gt; or &lt;code&gt;@wordpress/i18n&lt;/code&gt;. That’s not a mistake. Since these are internal WordPress scripts, we need to tell WordPress to enqueue those scripts &lt;em&gt;before&lt;/em&gt; our script. Fortunately, WordPress and Underpin make this pretty easy to-do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Underpin Script to include
&lt;/h3&gt;

&lt;p&gt;Back in &lt;code&gt;bootstrap.php&lt;/code&gt;, update your script’s &lt;code&gt;add&lt;/code&gt; function to include a &lt;code&gt;deps&lt;/code&gt; argument. Since this argument is a path, it will automatically require the file, and use it to tell WordPress which scripts need enqueued. Since Webpack automatically generates this file for us, we no-longer need to worry about adding dependencies every time we want to use a WordPress library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'custom_blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                          &lt;span class="c1"&gt;// Script Handle used in wp_*_script&lt;/span&gt;
    &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;js_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks.js'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// Src used in wp_register_script&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom Blocks Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                   &lt;span class="c1"&gt;// Names your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Script that loads in the custom blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// Describes your script. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'deps'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'build/custom-blocks.asset.php'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Load these scripts first.&lt;/span&gt;
    &lt;span class="s1"&gt;'middlewares'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'Underpin_Scripts\Factories\Enqueue_Admin_Script'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Enqueues the script in the admin area&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From your admin screen, if you navigate to &lt;strong&gt;posts&amp;gt;&amp;gt;Add New&lt;/strong&gt;, you will find that you can use a new block called “Hello World”, which will simply display “Hello World” In gigantic letters on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pu_OKVwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_18-35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pu_OKVwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_18-35.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this script, you can create as many blocks as you need by simply creating another &lt;code&gt;registerBlockType&lt;/code&gt; call, and registering the block though Underpin using &lt;code&gt;custom_blocks()-&amp;gt;blocks()-&amp;gt;add&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Gutenberg Block Stylesheet (Optional)
&lt;/h2&gt;

&lt;p&gt;Stylesheets need a bit of extra thought in-order for them to work-as expected. Normally, you would simply enqueue the script much like you enqueue a script. The catch is that this stylesheet also needs to be used in the block editor in-order to accurately display the block output. Let’s get into how to set that up.&lt;/p&gt;

&lt;p&gt;Just like &lt;em&gt;everything else&lt;/em&gt; with Underpin, the first step is to install the appropriate loader, the register the style.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;mu-plugins&lt;/code&gt; directory, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require underpin/style-loader&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From there, register a style in your &lt;code&gt;bootstrap.php&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'custom_block_styles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'handle'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                        &lt;span class="c1"&gt;// handle used in wp_register_style&lt;/span&gt;
    &lt;span class="s1"&gt;'src'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;css_url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'custom-block-styles.css'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// src used in wp_register_style&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Custom Blocks Style'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                  &lt;span class="c1"&gt;// Names your style. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Styles for custom Gutenberg blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;// Describes your style. Used for debugging&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update &lt;code&gt;webpack.config.js&lt;/code&gt; to include &lt;code&gt;custom-block-styles.css&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * WordPress Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@wordpress/scripts/config/webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * External Dependencies
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...{&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-blocks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-blocks.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Create "custom-blocks.js" file in "build" directory&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-block-styles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-block-styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, update your registered block to use the style to specify the stylesheet to be used with this block like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                                        &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
        &lt;span class="s1"&gt;'style'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="c1"&gt;// Stylesheet handle to use in the block&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will update your block to enqueue the stylesheet in the block editor automatically, and will reflect the styles in the stylesheet. This will work both on the actual site and the block editor.&lt;/p&gt;

&lt;p&gt;With the style set as-such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.hello-world&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;rebeccapurple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll get this in the block editor, and the front end:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_gURBT_7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_20-50.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_gURBT_7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_20-50.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Server-Side Rendering (Optional)
&lt;/h2&gt;

&lt;p&gt;This is all fine and dandy, but there’s one problem with how this is built – what happens if a theme needs to change the markup of our block? Or, what if, for some reason, it makes more sense to use PHP to render this block instead of Javascript?&lt;/p&gt;

&lt;p&gt;A fundamental problem with blocks is that it will hardcode the saved block result inside of the WordPress content. In my opinion, it’s better to render using server-side rendering. This tells WordPress that, instead of saving the HTML output, to instead create a &lt;em&gt;placeholder&lt;/em&gt; for the block, and just before the content is rendered, WordPress will inject the content from a PHP callback. This allows you to update blocks across your site quickly just by updating a PHP callback whenever you want.&lt;/p&gt;

&lt;p&gt;Call me old fashioned, but I think that’s &lt;em&gt;a lot&lt;/em&gt; more maintain-able, and thankfully it’s pretty easy to-do.&lt;/p&gt;

&lt;p&gt;First, update your registered block so that &lt;code&gt;save&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt;. This instructs the editor to simply &lt;em&gt;not save HTML, and just put a placeholder there instead.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registers our block type to the Gutenberg editor.&lt;/span&gt;
&lt;span class="nx"&gt;registerBlockType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-blocks/hello-world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display Hello World text on the site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello-world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you specify a &lt;code&gt;render_callback&lt;/code&gt; in your registered block arguments, it will use the callback instead of what was originally in the &lt;code&gt;save&lt;/code&gt; callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                                        &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
        &lt;span class="s1"&gt;'style'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                             &lt;span class="c1"&gt;// Stylesheet handle to use in the block&lt;/span&gt;
        &lt;span class="s1"&gt;'render_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;h1 class="hello-world"&amp;gt;Hey, this is a custom callback!&amp;lt;/h1&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you look in your editor, you’ll still see “Hello World”, because that’s what the Javascript’s &lt;code&gt;edit&lt;/code&gt; method returns, however, if you save and look at the actual post, you’ll find that the actual post will show “Hey, this is a custom callback” instead. This is because it’s using PHP to render the output on the fly. Now, if you change the content of the &lt;code&gt;render_callback&lt;/code&gt;, it will automatically render this output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Going Further – Use Underpin’s Template System
&lt;/h3&gt;

&lt;p&gt;What happens if you have a WordPress theme, and you want to actually &lt;em&gt;override&lt;/em&gt; the render callback? A good way to approach this is to use &lt;a href="https://github.com/underpin-WP/underpin#template-system-trait"&gt;Underpin’s built-in Template loader system&lt;/a&gt;. This system allows you to specify file locations for PHP templates that render content, and also has baked-in support for template overriding by themes.&lt;/p&gt;

&lt;p&gt;Underpin’s template system is a &lt;a href="https://www.php.net/manual/en/language.oop5.traits.php"&gt;PHP trait&lt;/a&gt;. It can be applied to &lt;em&gt;any&lt;/em&gt; class that needs to output HTML content. The tricky part is, we haven’t made a class yet, have we?&lt;/p&gt;

&lt;p&gt;…Have we?&lt;/p&gt;

&lt;p&gt;Well, actually, we have. Every time we run the &lt;code&gt;add&lt;/code&gt; method in WordPress, it automatically creates &lt;em&gt;an instance&lt;/em&gt; of a class, and it uses the array of arguments to construct our class for us. However, now, we need to actually make the class ourselves so we can apply the Template trait to the class, and render our template. So, next up we’re going to take our registered block, and move it into it’s own PHP class, and then instruct Underpin to use that class directly instead of making it for us.&lt;/p&gt;

&lt;p&gt;First up, create a directory called &lt;code&gt;lib&lt;/code&gt; inside your plugin directory, and then inside &lt;code&gt;lib&lt;/code&gt; create another directory called &lt;code&gt;blocks&lt;/code&gt;. Inside that, create a new PHP file called &lt;code&gt;Hello_World.php&lt;/code&gt;. Underpin comes with an autoloader, so the naming convention matters here.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── lib
│   └── blocks
│       └── Hello_World.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Inside your newly created PHP file, create a new PHP class called &lt;code&gt;Hello_World&lt;/code&gt; that extends &lt;code&gt;Block&lt;/code&gt;, then move all of your array arguments used in your &lt;code&gt;add&lt;/code&gt; method as parameters inside the class, like so:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Custom_Blocks\Blocks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nf"&gt;Underpin_Blocks&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Abstracts\Block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hello_World&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                                        &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
            &lt;span class="s1"&gt;'style'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                 &lt;span class="c1"&gt;// Stylesheet handle to use in the block&lt;/span&gt;
            &lt;span class="s1"&gt;'render_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;h1 class="hello-world"&amp;gt;Hey, this is a custom callback!&amp;lt;/h1&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, replace the array of arguments in your &lt;code&gt;add&lt;/code&gt; callback with a string that references the class you just created, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registers block&lt;/span&gt;
&lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Custom_Blocks\Blocks\Hello_World'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, you have instructed Underpin to &lt;em&gt;use your PHP class&lt;/em&gt; instead of creating one from the array of arguments. Now that we have a full-fledged PHP class in-place, we can do a lot of things to clean this up a bit, and use that template Trait I mentioned before.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;use \Underpin\Traits\Templates&lt;/code&gt; to the top of your PHP class, and add the required methods to the trait as well, like so:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Custom_Blocks\Blocks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nf"&gt;Underpin_Blocks&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Abstracts\Block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hello_World&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Underpin\Traits\Templates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Block'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                       &lt;span class="c1"&gt;// Names your block. Used for debugging.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'A custom block specific to this site.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Describes your block. Used for debugging&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/hello-world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                                        &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
            &lt;span class="s1"&gt;'style'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                 &lt;span class="c1"&gt;// Stylesheet handle to use in the block&lt;/span&gt;
            &lt;span class="s1"&gt;'render_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;h1 class="hello-world"&amp;gt;Hey, this is a custom callback!&amp;lt;/h1&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_templates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: Implement get_templates() method.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_template_group&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: Implement get_template_group() method.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_template_root_path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: Implement get_template_root_path() method.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we’re going to fill out each of these functions. &lt;code&gt;get_templates&lt;/code&gt; should return an array of template file names with an array declaring if that template can be manipulated by a theme, or not, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_templates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'wrapper'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'override_visibility'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt; &lt;span class="p"&gt;]];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_template_group&lt;/code&gt; should return a string, that indicates what the template sub directory should be called. In our case, we’re going to make it &lt;code&gt;hello-world&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_template_group&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'hello-world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_template_root_path&lt;/code&gt; should simply return &lt;code&gt;custom_blocks()-&amp;gt;template_dir()&lt;/code&gt;, as we don’t need to use a custom template directory or anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_template_root_path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;custom_blocks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;template_dir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we have the &lt;em&gt;option&lt;/em&gt; to override the template override directory name into something specific to our own plugin. Let’s do that, too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get_override_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks/'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these three items in-place, you can now create a new file in &lt;code&gt;templates/hello-world&lt;/code&gt; called &lt;code&gt;wrapper.php&lt;/code&gt;. Inside your theme, this template can be completely overridden by adding a file in &lt;code&gt;custom-blocks/hello-world&lt;/code&gt; called &lt;code&gt;wrapper.php&lt;/code&gt;. Let’s start by adding our template in the plugin file.&lt;/p&gt;

&lt;p&gt;The first thing your template needs is a header that checks to make sure the template was loaded legitimately. You don’t want people to load this template outside of the intended way, so you must add a check at the top level to make sure it was loaded properly, like so:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Custom_Blocks\Blocks\Hello_World&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underpin automatically creates a new variable called &lt;code&gt;$template&lt;/code&gt; and assigns it to the class that renders the actual template. So inside your template file &lt;code&gt;$template&lt;/code&gt; will always be the instance of your registered block. This allows you to create custom methods inside the block for rendering purposes if you want, but it also gives you access to rendering other sub-templates using &lt;code&gt;$template-&amp;gt;get_template()&lt;/code&gt;, plus a lot of other handy things that come with the &lt;code&gt;Template&lt;/code&gt; trait. As you can see above, this also provides you with a handy way to validate that the required file is legitimate.&lt;/p&gt;

&lt;p&gt;Now, simply add the HTML output at the bottom, like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Custom_Blocks\Blocks\Hello_World&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hello-world"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hey, this is a custom callback, and it is inside my template!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, go back into your &lt;code&gt;Hello_World&lt;/code&gt; class, and update the render callback to use your template. This is done using &lt;code&gt;get_template&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                         &lt;span class="c1"&gt;// See register_block_type&lt;/span&gt;
            &lt;span class="s1"&gt;'style'&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'custom-blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Stylesheet handle to use in the block&lt;/span&gt;
            &lt;span class="s1"&gt;'render_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wrapper'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs the &lt;code&gt;render_callback&lt;/code&gt; to use &lt;code&gt;get_template&lt;/code&gt;, which will then retrieve the template file you created. If you look at your template’s output, you’ll notice that your h1 tag changed to read “Hey, this is a custom callback, and it is inside my template!”.&lt;/p&gt;

&lt;p&gt;Now, go into your current theme, create a php file inside &lt;code&gt;custom-blocks/hello-world&lt;/code&gt; called &lt;code&gt;wrapper.php&lt;/code&gt;. Copy the contents of your original &lt;code&gt;wrapper.php&lt;/code&gt; file, and paste them in. Finally, change the output a little bit. When you do this, the template will automatically be overridden by your theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now that you have one block set-up, it’s just a matter of registering new blocks using Underpin, and inside your Javascript using &lt;code&gt;registerBlockType&lt;/code&gt;. If necessary, you can create a block class for each block, and use the template system to render the content.&lt;/p&gt;

&lt;p&gt;This post barely scratches the surface of what can be done with Underpin, the template loader, and Gutenberg. From here, you could really flesh out your block into something more than a trivial example. If you want to go deeper on these subjects, check out my &lt;a href="https://www.wpdev.academy/course/beer-lister-plugin/"&gt;WordPress plugin development course&lt;/a&gt;, where we create a block much like how I describe it here, and then build out a fully-functional Gutenberg block, as well as many other things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join WP &lt;a href="https://discord.gg/ArqzZmqfYZ"&gt;Dev Academy’s Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>gutenberg</category>
      <category>javascript</category>
      <category>php</category>
    </item>
    <item>
      <title>Headless WordPress is Overrated: A Case for The Nearly-Headless Web App</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Fri, 02 Jul 2021 14:05:26 +0000</pubDate>
      <link>https://dev.to/alexstandiford/headless-wordpress-is-overrated-a-case-for-the-nearly-headless-web-app-4914</link>
      <guid>https://dev.to/alexstandiford/headless-wordpress-is-overrated-a-case-for-the-nearly-headless-web-app-4914</guid>
      <description>&lt;p&gt;Over the last few years, I’ve built a number of fully headless WordPress websites with the REST API and React. I loved how fast the end result is, and if done correctly, how powerful and extend-able you could eventually make page creation. Plus it just &lt;em&gt;feels nice.&lt;/em&gt; Loading transitions, and the general behavior just makes your website feel fresh, and modern. It provides a polish that even the fastest non-headless sites can’t quite achieve.&lt;/p&gt;

&lt;p&gt;I avoid creating headless WordPress websites. It creates a lot of extra overhead, which creates more bugs, and ultimately ends up making the site much harder to maintain. I typically stick to the basics – fast hosting, and aggressive caching.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Headless WordPress kind-of sucks. You give up &lt;em&gt;a lot&lt;/em&gt; of help from WordPress in-search of an app experience. I have found myself frequently dreaming &lt;em&gt;“what if I could have it both ways?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem With Headless
&lt;/h2&gt;

&lt;p&gt;One problem with fully-headless WordPress is routing. Behind the scenes, WordPress has a lot of logic built-in to handle routing, and with a headless approach you have to build something to handle that on the front end. Ultimately, you’re re-inventing the wheel, and it takes a lot of extra time to build.&lt;/p&gt;

&lt;p&gt;Another problem with headless WordPress quickly becomes apparent the moment you try to use most WordPress plugins. The ugly truth is that you usually have to re-invent a lot of things just to get the plugin working properly. For example, try to create and use a &lt;a href="https://www.wpdev.academy/recommends/gravity-forms/" rel="noopener noreferrer"&gt;Gravity Forms&lt;/a&gt; form, and then use it in a React app. You can’t really do it without re-building the form’s render, validation, and submission logic on the front-end. That’s &lt;em&gt;a lot&lt;/em&gt; of extra overhead to maintain. This example is something as simple as &lt;em&gt;adding a form to a website&lt;/em&gt;. Imagine how complex things get when you look at things like integrating e-commerce tools, like Easy Digital Downloads, or WooCommerce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-Thinking “Headless WordPress”
&lt;/h2&gt;

&lt;p&gt;I knew when I committed to upgrading my personal theme, I wanted it to be a fast, and have an app feel, but I didn’t want to completely sacrifice all of the natural capabilities that WordPress plugins offer. This site, for example, uses &lt;a href="https://www.wpdev.academy/recommends/lifterlms/" rel="noopener noreferrer"&gt;LifterLMS&lt;/a&gt; for its courses, and it would have taken a lot of extra time to rewrite all of those course templates from-scratch.&lt;/p&gt;

&lt;p&gt;I also wanted to &lt;em&gt;use&lt;/em&gt; this tech for our premium clients at &lt;a href="https://www.wpdev.academy/recommends/designframe/" rel="noopener noreferrer"&gt;DesignFrame&lt;/a&gt;. Because of that, we needed a way to maximize compatibility with WordPress’s native features, and ensure that plugins remain compatible with whatever we build.&lt;/p&gt;

&lt;p&gt;So, with that, here’s the key parameters of this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The theme had to be compatible with other plugins &lt;em&gt;without re-building a bunch of custom logic in the process&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt; Other developers should be able to pick up and work with the theme with a minimal learning curve.&lt;/li&gt;
&lt;li&gt; The site needs to just &lt;em&gt;work&lt;/em&gt; with WordPress’s block editor without needing any changes to the theme.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These parameters &lt;em&gt;immediately&lt;/em&gt; revealed a couple technical truths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The HTML markup needs to be rendered inside WordPress. This ensures that plugins can render their output in the same way they do with any other theme.&lt;/li&gt;
&lt;li&gt; The app needs to rely on WordPress for routing, and needs to handle any custom page from any plugin without fail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither of these things fit the description of headless WordPress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Our Nearly Headless Web App
&lt;/h2&gt;

&lt;p&gt;I like to call this a &lt;em&gt;nearly headless app.&lt;/em&gt; Partially because it makes sense – the app still relies on the server to get started, but once the server provides the initial load, the app can usually take it from there. But let’s be real, I just wanted an excuse to put John Cleese in my blog post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.wpdev.academy%2Fwp-content%2Fuploads%2F2021%2F07%2Ftumblr_inline_nb93aw3mNs1rmc5y8540.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.wpdev.academy%2Fwp-content%2Fuploads%2F2021%2F07%2Ftumblr_inline_nb93aw3mNs1rmc5y8540.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the gist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The system uses &lt;a href="https://github.com/alpinejs/alpine" rel="noopener noreferrer"&gt;AlpineJS&lt;/a&gt; for rendering. It’s light, fairly easy to understand, and it plays exceptionally nice with PHP server-side rendering.&lt;/li&gt;
&lt;li&gt; Most of the theme is is loaded around HTML &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template" rel="noopener noreferrer"&gt;template&lt;/a&gt; tags. These tags get populated by WordPress’s REST responses for post content.&lt;/li&gt;
&lt;li&gt; The system makes judicious use of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage" rel="noopener noreferrer"&gt;session storage&lt;/a&gt;. This drastically reduces the number of REST API calls, and keeps the site running fast.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How Nearly Headless WordPress Works
&lt;/h3&gt;

&lt;p&gt;The site is loaded just like any regular ol’ WordPress site. The key difference is “the loop” is replaced by a template tag, which uses Alpine’s &lt;code&gt;x:forEach&lt;/code&gt; loop to actually render the loop. It looks something like this:&lt;/p&gt;

&lt;pre&gt; `&lt;span&gt;&lt;span&gt;
&lt;span&gt;/**
 * Index Loop Template
 *
 * @author: Alex Standiford
 * @date  : 12/21/19
 * &lt;a class="mentioned-user" href="https://dev.to/var"&gt;@var&lt;/a&gt; $template \Theme\Templates\Index
 */&lt;/span&gt;

&lt;span&gt;if&lt;/span&gt; &lt;span&gt;(&lt;/span&gt; &lt;span&gt;!&lt;/span&gt; &lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;templates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;is_valid_template&lt;/span&gt;&lt;span&gt;(&lt;/span&gt; &lt;span&gt;$template&lt;/span&gt; &lt;span&gt;)&lt;/span&gt; &lt;span&gt;)&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;

&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;loop&lt;span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span&gt;x-for&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;( post, index ) in posts&lt;span&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span&gt;:key&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;index&lt;span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span&gt;x-if&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;post&lt;span&gt;'&lt;/span&gt; === getParam(index, &lt;span&gt;'&lt;/span&gt;type&lt;span&gt;'&lt;/span&gt;)&lt;span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span&gt;&lt;span&gt;=&lt;/span&gt; &lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;templates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;get_template&lt;/span&gt;&lt;span&gt;(&lt;/span&gt; &lt;span&gt;'post'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;'post'&lt;/span&gt; &lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt; &lt;span&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;
                &lt;span&gt;x-if&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;page&lt;span&gt;'&lt;/span&gt; === getParam(index, &lt;span&gt;'&lt;/span&gt;type&lt;span&gt;'&lt;/span&gt;) || &lt;span&gt;'&lt;/span&gt;course&lt;span&gt;'&lt;/span&gt; === getParam(index, &lt;span&gt;'&lt;/span&gt;type&lt;span&gt;'&lt;/span&gt;) || &lt;span&gt;'&lt;/span&gt;lesson&lt;span&gt;'&lt;/span&gt; === getParam(index, &lt;span&gt;'&lt;/span&gt;type&lt;span&gt;'&lt;/span&gt;)&lt;span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
                &lt;span&gt;&lt;span&gt;=&lt;/span&gt; &lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;templates&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;get_template&lt;/span&gt;&lt;span&gt;(&lt;/span&gt; &lt;span&gt;'page'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;'page'&lt;/span&gt; &lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt; &lt;span&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;`
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;I’m using &lt;a href="https://github.com/Underpin-WP/underpin#template-system-trait" rel="noopener noreferrer"&gt;Underpin’s template system&lt;/a&gt; in the example above, but you could just as easily do this with &lt;code&gt;get_template_part()&lt;/code&gt; instead of &lt;code&gt;get_template()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the page loads, AlpineJS fires up, and renders the content using the REST API. Since the initial endpoint is preloaded, it grabs the data from the cache, loops through the content, and renders the result. The REST response is also saved in session storage (more on that later).&lt;/p&gt;

&lt;p&gt;Behind the scenes, the app scans the entire rendered page for internal site links, gathers them up, and sends them to a custom REST endpoint. This endpoint takes the URLs, retrieves the post object associated with each one, and returns them to the app. The app takes these objects and puts them in session storage for later use.&lt;/p&gt;

&lt;p&gt;When a link is clicked, the app intercepts the event, and checks to see if the post for that link is stored in session storage. If it is, it re-renders the page using the data from session storage, and pushes the URL to the browser’s history. If the page content isn’t stored in session storage, it simply loads the link using the default behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Your Website Works Less
&lt;/h3&gt;

&lt;p&gt;Because most of the content ultimately gets loaded from the session storage, the app has all of the information needed to render content &lt;em&gt;without contacting the server&lt;/em&gt;. This takes a lot of strain off of the server by reducing the number of requests a visitor makes when exploring your website. Not only does this mean your site will run faster, it also means your site will be able to handle more concurrent visitors without slowing down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster Experience On Slow Networks
&lt;/h3&gt;

&lt;p&gt;Another benefit of the nearly headless WordPress app is how much better this app performs on a slow network. I spend &lt;a href="https://casualweirdness.life/" rel="noopener noreferrer"&gt;a lot of time in the boonies&lt;/a&gt;, so I’m painfully aware of how much an optimized website can improve a person’s experience. The initial load won’t be any better than a normal site, but when it loads, the rest of the content is fetched in another request. Once that loads, the site will load instantly, even though the network is slow.&lt;/p&gt;

&lt;p&gt;In fact, in testing, I was able to load the initial page, turn on my iPhone’s airplane mode, and still navigate most of the site as-if I had a lightning fast connection.&lt;/p&gt;

&lt;p&gt;&amp;gt; .&lt;a href="https://twitter.com/DFS_Web?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@DFS_Web&lt;/a&gt;’s website redesign will make it possible to visit any page without an internet connection shortly after the first page is loaded. This makes this site FAST even if your internet connection is slow. &lt;a href="https://t.co/keOxyMU8cq" rel="noopener noreferrer"&gt;pic.twitter.com/keOxyMU8cq&lt;/a&gt;&lt;br&gt;
&amp;gt; &lt;br&gt;
&amp;gt; — Alex Standiford (&lt;a class="mentioned-user" href="https://dev.to/alexstandiford"&gt;@alexstandiford&lt;/a&gt;) &lt;a href="https://twitter.com/AlexStandiford/status/1336468966160683017?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;December 9, 2020&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem Pages Can Bypass the App
&lt;/h3&gt;

&lt;p&gt;The app only instant-loads if the content is in the session storage. This means that you can “disable” the app on pages that, for whatever reason, need to run through a WordPress request when visited. This theme includes settings page that makes it possible to add a list of pages to explicitly force to load in this manner.&lt;/p&gt;

&lt;p&gt;This makes it possible to fallback to a more-traditional theme load on pages that somehow conflict with the app. That gives us a way to quickly fix pages that are behaving unexpectedly without needing to make any immediate changes to the theme.&lt;/p&gt;

&lt;p&gt;This allows me to quickly put a quick-fix to problems that crop up, and then implement the necessary upgrades to the theme to fix the conflicts and re-activate the app on that page.&lt;/p&gt;

&lt;p&gt;This also allows us to completely disable headless WordPress when it’s convenient from a technical standpoint. Some pages would require &lt;em&gt;a lot&lt;/em&gt; of extra work to re-build using REST. For example, a cart page on a website that uses an e-commerce solution would require a significant rewrite of the template because these plugins expect a traditional request to occur when the page is visited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;This system avoids &lt;em&gt;most&lt;/em&gt; problems that headless apps create, however, in its current form, it has a few gotchas. Fortunately, these issues have been relatively easy to fix, and can often be avoided altogether by simply disabling the app for the pages that are impacted.&lt;/p&gt;

&lt;p&gt;Scripts and styles are probably the biggest headache that comes with any method that renders with Javascript, and this system is no exception. Any plugin that enqueues a custom script or style on the front end &lt;em&gt;will not work&lt;/em&gt; if the page is loaded with the cache. This is because most plugins only load scripts and styles on pages that &lt;em&gt;need&lt;/em&gt; the scripts. This can usually be avoided by forcing any page that utilizes these plugins to load without the cache. That will force the site to load the site normally, which usually makes everything work as-expected.&lt;/p&gt;

&lt;p&gt;In my build, Gravity Forms &lt;em&gt;still&lt;/em&gt; didn’t work, even when the page with the form was loaded normally. This was because Gravity Form’s script fired &lt;em&gt;before&lt;/em&gt; Alpine rendered the content. This caused Gravity Form to fail.&lt;/p&gt;

&lt;p&gt;To get around this, I had two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Force all pages that have a Gravity Form to load without Alpine, using a traditional loop. Easy, but not as nice.&lt;/li&gt;
&lt;li&gt; Modify how Gravity Forms renders its forms to use Alpine + Gravity Forms REST API. Harder, but nicer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this specific problem, I opted to spend a few hours getting Gravity Forms working with Alpine. Since I’m still using WordPress to do my rendering, I didn’t have to re-do the rendering portion, just had to modify it a little to use Alpine’s event handles. This ended up being significantly easier than what I’ve had to-do in React, where I had to also re-create the forms in JSX. I didn’t have to re-invent the rendering, just had to get &lt;em&gt;submissions working&lt;/em&gt;, and it took a lot less effort to achieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When I started working on this, I knew that there would be a little more overhead than a basic theme, and a lot less overhead than a site builder. The goal, however, was to minimize the overhead enough to make it a plausible option for our premium clients. This approach offers a &lt;em&gt;lot&lt;/em&gt; of performance gains without adding a lot of extra overhead in the process, and most-importantly, provides a traditional-loading safety net for pages that are being naughty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join WP &lt;a href="https://discord.gg/ArqzZmqfYZ" rel="noopener noreferrer"&gt;Dev Academy’s Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>alpinejs</category>
      <category>javascript</category>
      <category>php</category>
    </item>
    <item>
      <title>Introducing Underpin - A Modern WordPress Framework</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Thu, 20 May 2021 21:28:04 +0000</pubDate>
      <link>https://dev.to/alexstandiford/introducing-underpin-a-modern-wordpress-framework-4804</link>
      <guid>https://dev.to/alexstandiford/introducing-underpin-a-modern-wordpress-framework-4804</guid>
      <description>&lt;p&gt;Over the years, I have built dozens and dozens of WordPress plugins. Some have been GPL plugins shared publicly, but most have been custom plugins for clients. I started out much like most other WordPress developers, building ever-improving homemade boilerplates. It started simple enough - nothing more than a collection of handy functions that I always wanted in my plugins. Over time, it expanded into a bootstrap file that sets up the plugin, set up constants, and all of the other things that you will find in pretty much any major WordPress plugin today.&lt;/p&gt;

&lt;p&gt;Over time, I tinkered, built, re-built, and re-imagined my little plugin boilerplate into &lt;a href="//github.com/underpin-WP/underpin"&gt;a full-blown WordPress framework&lt;/a&gt;, that I use on all of my client sites, and all of my distributed plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  WordPress Isn't a Framework
&lt;/h2&gt;

&lt;p&gt;WordPress 'aint perfect. It does a lot of things "old skool", and wasn't really designed to make building web applications easy. Instead, it has always been focused on making its core as extendable as possible, and simply stays out of your way so you can go on to build what you want.&lt;/p&gt;

&lt;p&gt;In my opinion, I think this is great. I think WordPress's staying power has come from the fact that it just &lt;em&gt;does not give a damn&lt;/em&gt; about how you build your stuff. If you hook into the right action, it'll do whatever you want.&lt;/p&gt;

&lt;p&gt;But this does &lt;em&gt;not&lt;/em&gt; make plugin development particularly efficient in WordPress. If you're starting from scratch, you're literally starting with a blank PHP file in a plugin directory. There's a lot of extra setup that comes with doing pretty much everything in WordPress that you don't find in actual frameworks.&lt;/p&gt;

&lt;p&gt;Over time, you will also find that WordPress is missing some key things that most plugins will need as they mature, such as plugin upgrade routines.&lt;/p&gt;

&lt;p&gt;In fact, WordPress has a lot of shortcomings that many plugins have to eventually overcome:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Troubleshooting their plugin on a live customer site&lt;/li&gt;
&lt;li&gt;Building robust, extendable admin pages&lt;/li&gt;
&lt;li&gt;Making your plugin extendable without taking on tons of technical debt in the process&lt;/li&gt;
&lt;li&gt;Using consistent code practices across add-ons&lt;/li&gt;
&lt;li&gt;Handling upgrade routines, and database upgrades&lt;/li&gt;
&lt;li&gt;Keeping technical debt managed, and under-control&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are a few of the challenges that I have had to overcome in my own plugins, and Underpin tackles each one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Underpin
&lt;/h2&gt;

&lt;p&gt;Underpin takes &lt;em&gt;everything&lt;/em&gt; that is done in WordPress, and converts it into a consistent syntax. In other words, the process to create a widget is pretty much identical to how you would register a custom post type. Things as seemingly different as creating scripts, and creating admin pages, even use the same exact steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the appropriate loader via Composer&lt;/li&gt;
&lt;li&gt;Register the item using the loader's &lt;code&gt;add&lt;/code&gt; function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Registering a custom post type could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register custom Post Type&lt;/span&gt;
&lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;custom_post_types&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'example_type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'example-type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// see register_post_type&lt;/span&gt;
    &lt;span class="s1"&gt;'args'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&gt;/*...*/&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// see register_post_type&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registering a widget could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register widget&lt;/span&gt;
&lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;widgets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'hello-world-widget'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Hello World Widget'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;                               &lt;span class="c1"&gt;// Required. The name of the widget.&lt;/span&gt;
    &lt;span class="s1"&gt;'id_base'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'widget_name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                                        &lt;span class="c1"&gt;// Required. The ID.&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Displays hello to a specified name on your site.'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Widget description.&lt;/span&gt;
    &lt;span class="s1"&gt;'widget_options'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;                                                                     &lt;span class="c1"&gt;// Options to pass to widget. See wp_register_sidebar_widget&lt;/span&gt;
        &lt;span class="s1"&gt;'classname'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test_widget'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'get_fields_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;WP_Widget&lt;/span&gt; &lt;span class="nv"&gt;$widget&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                            &lt;span class="c1"&gt;// Fetch, and set settings fields.&lt;/span&gt;
        &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;esc_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Underpin\Factories\Settings_Fields\Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$widget&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_field_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// See WP_Widget get_field_name&lt;/span&gt;
                &lt;span class="s1"&gt;'id'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$widget&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_field_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// See WP_Widget get_field_id&lt;/span&gt;
                &lt;span class="s1"&gt;'setting_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                            &lt;span class="c1"&gt;// Must match field name and field ID&lt;/span&gt;
                &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Optional. Specify the person to say hello to. Default "world".'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'label'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s1"&gt;'render_callback'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                      &lt;span class="c1"&gt;// Render output&lt;/span&gt;
        &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;esc_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Hello, %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, each of these items do all of the things necessary to register their respective items. It uses all of the information you provide to build when the time is right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging With Underpin
&lt;/h2&gt;

&lt;p&gt;Underpin comes with a &lt;a href="https://github.com/Underpin-WP/logger-loader"&gt;PSR3 compatible logging utility&lt;/a&gt;. This utility will write error logs to a file, or you can extend it to do something else if your plugin needs something else.&lt;/p&gt;

&lt;p&gt;This logger is used throughout Underpin, and is something that you could use as you build your own plugin, as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugin_name_replace_me&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// Error type.&lt;/span&gt;
    &lt;span class="s1"&gt;'unique_error_code'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Error code&lt;/span&gt;
    &lt;span class="s1"&gt;'Human readable error message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'arbitrary'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'that'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'is relevant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ref'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only does this optionally save certain events to the log, it also &lt;a href="https://dev.tointegrates%20seamlessly"&gt;https://github.com/Underpin-WP/debug-bar-extension&lt;/a&gt; with &lt;a href="https://wordpress.org/plugins/query-monitor/"&gt;Query Monitor&lt;/a&gt; and &lt;a href="https://wordpress.org/plugins/debug-bar/"&gt;Debug Bar&lt;/a&gt; debug plugins. All Underpin events automatically get added to these logs. This allows you to troubleshoot problems on a live site, and begin to make sense of what's going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Settings Fields
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://make.wordpress.org/design/handbook/focuses/full-site-editing/"&gt;Full-Site editing&lt;/a&gt; is coming, but for now we still need a way to create admin pages using WordPress's HTML markup. If you've ever worked with this system, you know - there be dragons.&lt;/p&gt;

&lt;p&gt;To help make this easier, Underpin has a set of HTML input classes that make it possible to sanitize, save, and render HTML on admin pages. These fields are capable of natively using all of the markup that WordPress expects, or a bare-minimum set of markup for places that do not work inside of a table.&lt;/p&gt;

&lt;p&gt;A basic text field would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$text_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Underpin\Factories\Settings_Fields\Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$widget&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_field_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// See WP_Widget get_field_name&lt;/span&gt;
    &lt;span class="s1"&gt;'id'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$widget&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_field_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// See WP_Widget get_field_id&lt;/span&gt;
    &lt;span class="s1"&gt;'setting_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                            &lt;span class="c1"&gt;// Must match field name and field ID&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Human Readable Description'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'label'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Field Name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Render the field&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$text_field&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;place&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these fields, you can create entire settings pages using the exact same &lt;code&gt;add&lt;/code&gt; syntax discussed above. The example below would render an admin page in WordPress, complete with support for saving the field to an option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Register the option to use on the settings page. See Underpin_Options\Abstracts\Option&lt;/span&gt;
&lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'example_admin_options'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'key'&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'example_option'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// required&lt;/span&gt;
    &lt;span class="s1"&gt;'default_value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'test_setting'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Hello world'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Example Admin Page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Settings manged by Example Admin Page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register the admin page&lt;/span&gt;
&lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;admin_pages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'example-admin-page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'page_title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Example Admin Page'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'menu_title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Example'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'capability'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'administrator'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'menu_slug'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'example-admin-page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'icon'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'dashicons-admin-site-alt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'position'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'sections'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'id'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'primary-section'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Primary Section'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'options_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'example_admin_options'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'fields'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'test_setting'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Underpin\Factories\Settings_Fields\Text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'args'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'example_admin_options'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'test_setting'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="s1"&gt;'name'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'test_setting'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Optional. Specify the person to say hello to. Default "world".'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="s1"&gt;'label'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'Name'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plugin Extend-ability
&lt;/h2&gt;

&lt;p&gt;The loader pattern provided by Underpin makes it possible to extend WordPress core, and you can use the exact same technology to make your own plugin extendable. This can be done by registering your own custom loader registries to your WordPress plugin.&lt;/p&gt;

&lt;p&gt;In fact, the process to &lt;em&gt;add&lt;/em&gt; a loader uses the exact same syntax as adding literally anything else. The only difference here is that you have to also create the &lt;code&gt;Loader_Registry&lt;/code&gt; class to reference.&lt;/p&gt;

&lt;p&gt;I'm not going to get into this too deep in this post, but you can see how it all works in the &lt;a href="https://github.com/Underpin-WP/custom-post-type-loader"&gt;custom post type loader source code&lt;/a&gt;. This particular bit of functionality is really the heart and soul of Underpin, and an entire post could be easily written on it.&lt;/p&gt;

&lt;p&gt;Here's an example of how a loader is registered in an upcoming course I'm working on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
        &lt;span class="cd"&gt;/**
         * Setup Colors
         */&lt;/span&gt;
        &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'colors'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'registry'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Beer_List\Loaders\Colors'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if I wanted to add a new color to the registry, I would do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'color_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cd"&gt;/** Args to register the color **/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, this would create a new instance of your &lt;code&gt;Color&lt;/code&gt; class, and store it for later use.&lt;/p&gt;

&lt;p&gt;That color class could be accessed at any time with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'color_id'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could also be done by anyone extending the plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade Routines &amp;amp; Batch Tasks
&lt;/h2&gt;

&lt;p&gt;Like anything else, Underpin has a custom loader that handles the admin interface, script, and batch actions needed to make an upgrade routine work. Like everything else, this is a &lt;a href="https://github.com/Underpin-WP/batch-task-loader"&gt;pre-built loader&lt;/a&gt; that can be installed and used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Underpin\underpin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;batch_tasks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'example-batch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'description'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'A batch task that does nothing 20 times'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'name'&lt;/span&gt;                    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Batch Task Example'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'tasks_per_request'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'stop_on_error'&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'total_items'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'notice_message'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Run the most pointless batch task ever made.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'button_text'&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'LETS GO.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'capability'&lt;/span&gt;              &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'administrator'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'batch_id'&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'example-batch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'task_callback'&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'__return_null'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The callback that iterates on every task&lt;/span&gt;
    &lt;span class="s1"&gt;'finish_process_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'__return_null'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The callback that runs after everything is finished&lt;/span&gt;
    &lt;span class="s1"&gt;'prepare_task_callback'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'__return_null'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The callback that runs before each task&lt;/span&gt;
    &lt;span class="s1"&gt;'finish_task_callback'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'__return_null'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The callback that runs after each task&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Distributing Underpin
&lt;/h2&gt;

&lt;p&gt;One big thing that stops most developers from using dependencies in their WordPress plugin is that there's usually no easy way to prevent plugin conflicts with other plugins that also use the system. Underpin resolves that problem with a &lt;a href="https://github.com/Underpin-WP/compiler"&gt;rename compiler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This compiler is a basic find/replace script that replaces all mention of the word &lt;code&gt;underpin&lt;/code&gt; throughout the plugin with a word of your choice. Everything in Underpin that can cause a conflict has been intentionally prefixed with &lt;code&gt;underpin&lt;/code&gt; to make it possible to do this rename.&lt;/p&gt;

&lt;p&gt;This means that you can use Underpin in its entirety in your WordPress plugin, and run the script to compile Underpin so it is safe to be distributed with other plugins that also use Underpin.&lt;/p&gt;

&lt;p&gt;If you don't need to distribute your plugin, and are using Underpin on a single website, it usually makes sense to install it as a mu-plugin. This removes the need to do any compilation, and also ties all of your plugins to a single installation of underpin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There is so much more about Underpin that hasn't been discussed here, and I expect I'll be publishing more content over time. In the meantime, if you think you'd like to learn more about Underpin, check out the &lt;a href="//github.com/underpin-WP/"&gt;GitHub organization&lt;/a&gt; containing all of the current loaders, as well as Underpin's core.&lt;/p&gt;

&lt;p&gt;PR's, discussions, and questions are always welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join &lt;a href="https://discord.gg/ArqzZmqfYZ"&gt;WP Dev Academy’s Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>composer</category>
    </item>
    <item>
      <title>Create Your Own WordPress Menu API</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Wed, 06 Jan 2021 19:45:09 +0000</pubDate>
      <link>https://dev.to/alexstandiford/create-your-own-wordpress-menu-api-4198</link>
      <guid>https://dev.to/alexstandiford/create-your-own-wordpress-menu-api-4198</guid>
      <description>&lt;h2&gt;
  
  
  What We’ll Be Doing
&lt;/h2&gt;

&lt;p&gt;We’re going to create 3 REST API endpoints, designed to allow us to get our menus in a couple of different ways.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Get a single menu by its ID&lt;/li&gt;
&lt;li&gt; Get a single menu by its name (slug)&lt;/li&gt;
&lt;li&gt; Get a single menu by its theme location
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’re going to create a single class, &lt;code&gt;NavMenu&lt;/code&gt;, to handle these functions.&lt;/p&gt;

&lt;p&gt;I should mention that &lt;strong&gt;one limitation this build has&lt;/strong&gt; is that it doesn’t provide a way to filter results by the&lt;br&gt;
parent. I didn’t use submenus in my personal build of my site, so I didn’t bother adding this functionality. Children&lt;br&gt;
menu items &lt;em&gt;will show&lt;/em&gt; up with this build, but you will have to filter out the results with Javascript’s &lt;code&gt;array.filter()&lt;/code&gt;&lt;br&gt;
method, or just build that directly into the endpoint via an extra argument, or something like that. I’m sure there’s a&lt;br&gt;
better database query that can be run to only get the results where the parent is X, but that’s outside of the scope of&lt;br&gt;
this lesson.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build Your NavMenu Class
&lt;/h2&gt;

&lt;p&gt;In my experience, it’s generally easier to create the class that will handle the functionality first, and then build the static methods that will be used as the api callbacks later. Create a new file called &lt;code&gt;NavMenu.php&lt;/code&gt; and drop it in your plugin. Be sure to include it in your core plugin file!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Gets a nav menu
 * @author: Alex Standiford
 * @date : 1/13/18
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;asmenus\lib\app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NavMenu&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_get_nav_menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll start simple, and grab the list of menu items using &lt;code&gt;wp_get_nav_menu_items()﻿&lt;/code&gt; Luckily, this function will work with&lt;br&gt;
either a menu name, or a menu ID, which makes our constructor pretty simple for now.&lt;/p&gt;

&lt;p&gt;Also note that we are using a namespace called &lt;code&gt;asmenus\lib\app&lt;/code&gt;. This helps to reduce the probability of name collisions&lt;br&gt;
with our code and other plugins, and is why I didn’t name the class something like &lt;code&gt;ASNavMenu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Right now, if we run &lt;code&gt;var_dump(new asmenus\lib\app\NavMenu($menu_name_or_id));&lt;/code&gt; where you replace &lt;code&gt;$menu_name_or_id&lt;/code&gt;&lt;br&gt;
with a valid menu name or ID, it will dump out all of the relevant information related to the items in that menu.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a Way To Get a Menu By its Location
&lt;/h3&gt;

&lt;p&gt;Now that we have a way to get the menu by the menu ID and the menu slug, we need to create a way to get a menu by its&lt;br&gt;
location. With a little digging, I discovered that the menu locations and their correlated menu IDs are stored in the&lt;br&gt;
database as a theme mod. To access that, we just need to add a method to our class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Gets a nav menu
 * @author: Alex Standiford
 * @date : 1/13/18
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;asmenus\lib\app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NavMenu&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_get_nav_menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets the menu ID from the specified location
 *
 * @param $location
 *
 * @return mixed
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_theme_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nav_menu_locations'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we added a new method, &lt;code&gt;getMenuIdFromLocation&lt;/code&gt;. This method uses &lt;code&gt;get_theme_mod﻿&lt;/code&gt; returns an associative array, with&lt;br&gt;
the menu IDs keyed by the menu location. So, by passing a &lt;code&gt;$location&lt;/code&gt; parameter, we can get the desired menu ID.&lt;/p&gt;

&lt;p&gt;Now, we just need to use this function in our constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Gets a nav menu
 * @author: Alex Standiford
 * @date : 1/13/18
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;asmenus\lib\app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NavMenu&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'menu'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_get_nav_menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets the menu ID from the specified location
 *
 * @param $location
 *
 * @return mixed
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_theme_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nav_menu_locations'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On our constructor, we add an optional second argument, &lt;code&gt;$type&lt;/code&gt;, which if the type is set to &lt;code&gt;'location'&lt;/code&gt;, our&lt;br&gt;
constructor will use the &lt;code&gt;getMenuIdFromLocation&lt;/code&gt; to get the correct menu ID based on the provided location in our first&lt;br&gt;
argument, &lt;code&gt;$menu_name_or_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Right now, if we run &lt;code&gt;var_dump(new asmenus\lib\app\NavMenu($menu_location,'location'));&lt;/code&gt; where you replace&lt;br&gt;
&lt;code&gt;$menu_location&lt;/code&gt; with a valid menu location, it will dump out all of the relevant information related to the items in&lt;br&gt;
that menu.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create the REST API Callback Functions
&lt;/h3&gt;

&lt;p&gt;Now that our class is capable of getting our menu details, let’s create our functions that will be used by our rest&lt;br&gt;
endpoints when our endpoint is visited.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Gets a nav menu
 * @author: Alex Standiford
 * @date : 1/13/18
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;asmenus\lib\app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NavMenu&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'menu'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_get_nav_menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets the menu ID from the specified location
 *
 * @param $location
 *
 * @return mixed
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_theme_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nav_menu_locations'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="cd"&gt;/**
 * Callback function to get the menu by name/id via the REST API
 *
 * @param \WP_REST_Request $request
 *
 * @return array|\WP_Error
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuFromApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;WP_REST_Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$name_or_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll start with the first method, which will handle getting the menu regardless of if it is a name or an id. As you can&lt;br&gt;
see, this is a static method (in other words, we can access it without instantiating our class first), and the first&lt;br&gt;
parameter is &lt;a href="http://php.net/manual/en/language.oop5.typehinting.php"&gt;type-hinted&lt;/a&gt; with &lt;code&gt;\WP_REST_Request&lt;/code&gt;. If you’re&lt;br&gt;
using a namespace, be sure to remember to add the &lt;code&gt;\&lt;/code&gt; beforehand, otherwise it won’t work.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$name_or_id&lt;/code&gt; is set with a ternary operator – which allows us to get the argument regardless of if it is a name or an&lt;br&gt;
id. You &lt;em&gt;could&lt;/em&gt;﻿ have built this out as two separate methods, but I felt this was cleaner.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our Completed Class
&lt;/h2&gt;

&lt;p&gt;Now, we just need to repeat this process with a second static method that will get the menu by location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * Gets a nav menu
 * @author: Alex Standiford
 * @date : 1/13/18
 */&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;asmenus\lib\app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NavMenu&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'menu'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_get_nav_menu_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$menu_name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets the menu ID from the specified location
 *
 * @param $location
 *
 * @return mixed
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuIdFromLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_theme_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nav_menu_locations'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="cd"&gt;/**
 * Callback function to get the menu by slug/id via the REST API
 *
 * @param \WP_REST_Request $request
 *
 * @return array|\WP_Error
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuFromApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;WP_REST_Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$name_or_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name_or_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="cd"&gt;/**
 * Callback function to get the menu by location via the REST API
 *
 * @param \WP_REST_Request $request
 *
 * @return array|\WP_Error
 */&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getMenuByLocationFromApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;WP_REST_Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;$location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;menus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference here is that when we instantiated the class, we set the second parameter to &lt;code&gt;'location'&lt;/code&gt;. This tells&lt;br&gt;
our constructor to look up the ID based on the location provided&lt;/p&gt;
&lt;h2&gt;
  
  
  Register Our Endpoints
&lt;/h2&gt;

&lt;p&gt;Now that our class is built out, we just need to register our endpoints. This should not be added to &lt;code&gt;NavMenu.php&lt;/code&gt;,&lt;br&gt;
instead this should be either placed in the core plugin file, or in a separate file that is included afterward. In the&lt;br&gt;
example below, I put them in the core plugin file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
Plugin Name: WordPress Menu REST API Endpoints
Description: Creates RESTful endpoints for WordPress menus
Version: 1.0
Author: Alex Standiford
Author URI: http://www.alexstandiford.com
**/&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;plugin_dir_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'NavMenu.php'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="cd"&gt;/**
 * Registers our api endpoints
 */&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'rest_api_init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
 &lt;span class="cd"&gt;/**
 * Gets a single menu by ID
 */&lt;/span&gt;
 &lt;span class="nf"&gt;register_rest_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'asmenus/v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/menu/(?P&amp;lt;id&amp;gt;[\d]+)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'methods'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'asmenus\lib\app\NavMenu::getMenuFromApi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;]);&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets a single menu by slug
 */&lt;/span&gt;
 &lt;span class="nf"&gt;register_rest_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'asmenus/v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/menu/(?P&amp;lt;name&amp;gt;[\w-_]+)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'methods'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'asmenus\lib\app\NavMenu::getMenuFromApi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;]);&lt;/span&gt;

 &lt;span class="cd"&gt;/**
 * Gets a single menu by its theme location
 */&lt;/span&gt;
 &lt;span class="nf"&gt;register_rest_route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'asmenus/v1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/menu/location/(?P&amp;lt;location&amp;gt;[\w-_]+)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'methods'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'asmenus\lib\app\NavMenu::getMenuByLocationFromApi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To register our endpoints, we hook into &lt;code&gt;'rest_api_init'&lt;/code&gt;, and register our routes in the callback, using&lt;br&gt;
&lt;code&gt;register_rest_route&lt;/code&gt;. Notice that our callbacks have the namespace prepended to them, and that we are setting the&lt;br&gt;
method for these endpoints to &lt;code&gt;GET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Voila! When you visit any of the endpoints, you should now get an array of objects containing all of the information&lt;br&gt;
about each menu item. From here, you could build out some React/Angular components that loop through and build your menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for more WordPress Resources?
&lt;/h2&gt;

&lt;p&gt;Join &lt;a href="https://discord.gg/ArqzZmqfYZ"&gt;WP Dev Academy’s Discord server&lt;/a&gt;, and become a part of a growing community of WordPress developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>rest</category>
    </item>
    <item>
      <title>Batch Video Editing With Node.JS</title>
      <dc:creator>Alex Standiford</dc:creator>
      <pubDate>Wed, 06 Jan 2021 19:29:10 +0000</pubDate>
      <link>https://dev.to/alexstandiford/batch-video-editing-with-node-js-5ada</link>
      <guid>https://dev.to/alexstandiford/batch-video-editing-with-node-js-5ada</guid>
      <description>&lt;p&gt;Over at DesignFrame, one of my clients hosts videos on their own site. In order to ensure that these videos will play correctly on all devices, I have been manually converting these videos using &lt;a href="https://cloudconvert.com/"&gt;Cloudconvert&lt;/a&gt;. It's a very handy tool, but the process can be tedious when you have a lot of files to deal with, and it doesn't (at least to my knowledge) handle generating screenshots of your videos for you.&lt;/p&gt;

&lt;p&gt;So, in-order to upload the videos to their website, my (admittedly awful) workflow looked something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Take each video, and use cloudconvert to create ogv, webm, and mp4 versions of each video&lt;/li&gt;
&lt;li&gt; Open the video and save a screenshot at a good place&lt;/li&gt;
&lt;li&gt; Upload each version of each video to their server&lt;/li&gt;
&lt;li&gt; Publish the video with the screenshot&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This wasn't &lt;em&gt;too&lt;/em&gt;﻿ bad, but as a programmer, doing manual, repetitive tasks makes my skin crawl, so I started looking into ways to automate this. I've been playing with creating small CLI applications with Node.js using commander lately, and decided that this would be an excellent place to start.&lt;/p&gt;

&lt;p&gt;What's nice about starting with a CLI-based solution is that it allows me to spend most of my time focusing on the back-end instead of building out some kind of interface. If you build correctly, it should be easy to set up what you've built with an interface.&lt;/p&gt;

&lt;p&gt;Here's what the script does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Add 3 commands accessible from my terminal's command line: &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;screenshots&lt;/code&gt;, and &lt;code&gt;videos&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Take all of the files in a specified directory, and convert the videos to ogv, webm, and mp4&lt;/li&gt;
&lt;li&gt; Automatically generate 6 screenshots of each video at different intervals throughout.&lt;/li&gt;
&lt;li&gt; Save the results of each video in a converted files directory, with each video title as the sub directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The nice thing about setting it up with Node is that, if the conversion job warrants it, you can spin up a cpu-optimized droplet on DigitalOcean, upload the files, and make the conversion quickly, and then destroy the droplet. This is &lt;em&gt;way&lt;/em&gt; faster than doing it on your local machine, and since the droplet is usually destroyed in 1-2 hours you're going to spend very little money to get the job done. This isn't a requirement, of course; The script runs perfectly fine on a local machine - the conversion will just take longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Completed Project Files
&lt;/h2&gt;

&lt;p&gt;You can get the completed project files &lt;a href="https://github.com/alexstandiford/video-conversion-script"&gt;here.﻿&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;I set the project up to use 3 files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;index.js&lt;/code&gt; - The entry point for our program. This is where we configure our CLI commands&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;FileConverter.js&lt;/code&gt; - Handles the actual conversion of a single file.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;MultiFileConverter.js&lt;/code&gt; - Gathers up videos from a directory, creates instances of &lt;code&gt;FileConverter&lt;/code&gt;, and runs the conversion.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Your Project
&lt;/h2&gt;

&lt;p&gt;Here is the resulting &lt;code&gt;package.json&lt;/code&gt; file that I'm using for this project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {
      "name": "video-converstion-script",
      "version": "1.0.0",
      "description": "Converts Videos",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1"
      },
      "bin": {
        "asconvert": "./index.js"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "@ffmpeg-installer/ffmpeg": "^1.0.15",
        "@ffprobe-installer/ffprobe": "^1.0.9",
        "commander": "^2.16.0",
        "fluent-ffmpeg": "^2.1.2",
        "junk": "^2.1.0"
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a list of each dependency and a brief description of their role in this project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@ffmpeg-installer/ffmpeg&lt;/code&gt; - sets up the binaries needed to convert the videos and create screenshots&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ffprobe-installer/ffprobe&lt;/code&gt; - sets up the binaries needed to convert the videos and create screenshots&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commander&lt;/code&gt; - Super awesome tool that allows us to build out a CLI from our Node.js application.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fluent-ffmpeg&lt;/code&gt; - Allows us to interface with ffmpeg using Node&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;junk&lt;/code&gt; - A nice little library that makes it easy to filter out junk files from our directory. This will keep us from trying to convert a .DS_Store file, or something like that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that we also have set the bin object. This allows us to associate our CLI command &lt;code&gt;asconvert&lt;/code&gt; with our &lt;code&gt;index.js&lt;/code&gt; file. You can change &lt;code&gt;asconvert&lt;/code&gt; to whatever you want, just keep in mind that you will need to use whatever you call &lt;code&gt;asconvert&lt;/code&gt; instead of what I call it in this post.&lt;/p&gt;

&lt;p&gt;Place JSON above into your &lt;code&gt;package.json&lt;/code&gt; file, and run &lt;code&gt;npm install&lt;/code&gt;. Once you do that, you'll also need to run &lt;code&gt;npm link&lt;/code&gt;. This will connect the bin configuration to your terminal so you can run your commands directly from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up our Index file
&lt;/h2&gt;

&lt;p&gt;Before we can start messing with our system, we need to set up some commander commands. This will allow us to test, debug, and tinker with our javascript from the terminal. We will be adding multiple commands later, but for now, let's simply add the &lt;code&gt;run&lt;/code&gt; command. The code below is a basic example, and should respond with "hello world!' in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node

/**
 * Allows us to run this script as a cli command
 */
const program = require('commander');

/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

/**
 The run command
 */
program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{
 console.log('hello world!');
//We will put our actual command here.
 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you add this, you should be able to run &lt;code&gt;asconvert run&lt;/code&gt; from your terminal and you should get "hello world!" back.  Superkewl!&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up the MultiFileConverter Class
&lt;/h2&gt;

&lt;p&gt;Now that we've got some simple command line things set up, let's start working on the good stuff.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;MultiFileConverter.js&lt;/code&gt; and add 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;/**
 * Parses file names
 */
const path = require('path');

/**
 * converts files from a directory
 */
class MultiFileConverter{
 constructor(args = {}){
 //Set the argument object
 const defaults = {
 directory: false,
 formats: false
 };
 this.args = Object.assign(args, defaults);

 //Construct from the args object
 this.formats = this.args.formats;
 this.directory = this.args.directory === false ? `${path.dirname(require.main.filename)}/files-to-convert/` : this.args.directory;
 }
}

module.exports = MultiFileConverter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic setup will allow us to pass an object of arguments to our constructor, which will merge with default arguments and build everything we'll need to complete the conversions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect The Converter to the CLI
&lt;/h3&gt;

&lt;p&gt;Once you do this, we need to set up our CLI command to use this object. Go back to your index.js file and create an instance of this class, 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;#!/usr/bin/env node
/**
 * Allows us to run this script as a cli command
 */
const program = require('commander');

const MultiFileConverter = require('./lib/MultiFileConverter');

/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

/**
 The run command
 */
program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{
 const converter = new MultiFileConverter();
 console.log(converter);
 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run the command now, the converter object should be displayed in the terminal. &lt;/p&gt;

&lt;p&gt;I personally organize my js files inside a &lt;code&gt;lib&lt;/code&gt; directory. You can put your files wherever you want, just make sure your include paths are correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the List of FileConverter objects
&lt;/h3&gt;

&lt;p&gt;The primary purpose of the &lt;code&gt;MultiFileConverter&lt;/code&gt; class is to batch-convert files in the directory. In order to do that, we are going to loop through the files in the directory and construct an array of &lt;code&gt;FileConverter&lt;/code&gt; objects from each file. We'll let the &lt;code&gt;FileConverter&lt;/code&gt; object handle the actual conversion and other file-specific things.&lt;/p&gt;

&lt;p&gt;I like to delay processes that have the potential to be time-consuming until I absolutely need them. That way I can construct the class without going through the time-consuming bits every time. To do this, I often create a getter method, 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;/**
 * Constructs the files object
 * @returns {*}
 */
getFiles(){
 if(this.files) return this.files;
 this.files = [];
 const files = fs.readdirSync(this.directory, {});
 //Loop through and construct the files from the specified directory
 files.filter(junk.not).forEach((file) =&amp;gt;{
 this.files.push(new FileConverter(this.directory + file, false, this.formats));
 });

 return this.files;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice the first line checks to see if the class already has a files array set. If it does, it simply returns that array. Otherwise, it goes through and builds this array. This allows us to use &lt;code&gt;getFiles()&lt;/code&gt; throughout the class without re-building the array every time.&lt;/p&gt;

&lt;p&gt;A lot is happening in this method. Let's break it down.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Check to see if the files array exists. If it does, it returns the value&lt;/li&gt;
&lt;li&gt; Reads the specified directory and returns an array of files&lt;/li&gt;
&lt;li&gt; Filters out junk files, and then loops through the filtered array.&lt;/li&gt;
&lt;li&gt; Inside the loop, we push a new instance of &lt;code&gt;FileConverter&lt;/code&gt; and pass the arguments into the the files array.&lt;/li&gt;
&lt;li&gt; Return the files in the object&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Update your &lt;code&gt;MultiFileConverter&lt;/code&gt; class to include a couple of required libraries, and add the &lt;code&gt;getFiles()&lt;/code&gt; class. You should end up with 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;/**
 * Node File system
 */
const fs = require('fs');

/**
 * Parses file names
 */
const path = require('path');

/**
 * Allows us to filter out junk files in our results
 */
const junk = require('junk');

/**
 * Handles the actual file conversion of individual files
 * @type {FileConverter}
 */
const FileConverter = require('./FileConverter');

/**
 * converts files from a directory
 */
class MultiFileConverter{
 constructor(args = {}){
 //Set the argument object
 const defaults = {
 directory: false,
 formats: false
 };
 this.args = Object.assign(args, defaults);

 //Construct from the args object
 this.formats = this.args.formats;
 this.directory = this.args.directory === false ? `${path.dirname(require.main.filename)}/files-to-convert/` : this.args.directory;
 }

 /**
 * Constructs the files object
 * @returns {*}
 */
 getFiles(){
 if(this.files) return this.files;
 this.files = [];
 const files = fs.readdirSync(this.directory, {});
 //Loop through and construct the files from the specified directory
 files.filter(junk.not).forEach((file) =&amp;gt;{
 this.files.push(new FileConverter(this.directory + file, false, this.formats));
 });

 return this.files;
 }
}

module.exports = MultiFileConverter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set Up the FileConverter Class
&lt;/h2&gt;

&lt;p&gt;Now that we are looping through our files, it's time to build a basic instance of the FileConverter class so our files array builds properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
 * Parses file names
 */
const path = require('path');

/**
 * Node File system
 */
const fs = require('fs');

/**
 * Handles the actual file conversion
 */
const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
const ffprobePath = require('@ffprobe-installer/ffprobe').path;
const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
ffmpeg.setFfprobePath(ffprobePath);

/**
 * Converts files and takes screenshots
 */
class FileConverter{

 constructor(inputPath, outputPath = false, formats = false){
 this.formats = formats === false ? ['ogv', 'webm', 'mp4'] : formats.split(',');
 this.file = path.basename(inputPath);
 this.format = path.extname(this.file);
 this.fileName = path.parse(this.file).name;
 this.conversion = ffmpeg(inputPath);
 this.outputPath = outputPath === false ? `${path.dirname(require.main.filename)}/converted-files/${this.fileName}` : `${outputPath}/${this.fileName}`;
 }
}

module.exports = FileConverter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that we are constructing some useful data related to the file and its impending conversion, but we don't actually do the conversion step yet. This simply sets the file up. We'll add the actual conversion in a separate method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test It Out
&lt;/h3&gt;

&lt;p&gt;We now have all 3 of our files all set up and connected. We haven't started the actual conversion process yet, but if we make a change to our command action we can check to make sure everything is working as-expected.&lt;/p&gt;

&lt;p&gt;If you haven't yet, now would be a good time to create 2 directories in the root of your project. &lt;code&gt;converted-files&lt;/code&gt; and &lt;code&gt;files-to-convert&lt;/code&gt;. Add a few video files in your &lt;code&gt;files-to-convert&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Modify your commander action in your &lt;code&gt;index.js&lt;/code&gt; file so that it logs the result of the &lt;code&gt;getFiles()&lt;/code&gt; method. If all went well, you should get a big ol' array of objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env node
/**
 * Allows us to run this script as a cli command
 */
const program = require('commander');

const MultiFileConverter = require('./lib/MultiFileConverter');

/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

/**
 The run command
 */
program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{
 const converter = new MultiFileConverter();
 console.log(converter.getFiles());
 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Convert Videos
&lt;/h2&gt;

&lt;p&gt;Whew. All this effort and we haven't even started converting videos yet. Let's change that.&lt;/p&gt;

&lt;p&gt;Add a new method, called &lt;code&gt;getVideos()&lt;/code&gt; to your &lt;code&gt;MultiFileConverter.js&lt;/code&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;/**
 * Loops through and converts files
 */
getVideos(){
 return this.getFiles().forEach(file =&amp;gt; file.convert());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This iddy biddy method simply loops through our files array and runs the &lt;code&gt;convert&lt;/code&gt; method on each &lt;code&gt;FileConverter&lt;/code&gt; object. Of course, we have to actually &lt;em&gt;create&lt;/em&gt; the convert method on the &lt;code&gt;FileConverter&lt;/code&gt; object for this to work, so let's do that now.&lt;/p&gt;

&lt;p&gt;Add a new method, called &lt;code&gt;convert()&lt;/code&gt; to your &lt;code&gt;FileConverter.js&lt;/code&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;/**
 * Converts the file into the specified formats
 */
convert(){
 fs.mkdir(this.outputPath,() =&amp;gt;{

 //Loop through file formats
 this.formats.forEach((fileFormat) =&amp;gt;{
 //Check to see if the current file format matches the given file's format
 if(`.${fileFormat}` !== this.format){
 //Start the conversion
 this.conversion.output(`${this.outputPath}/${this.fileName}.${fileFormat}`)
 .on('end', () =&amp;gt; console.log(`${this.file} has been converted to a ${fileFormat}`))
 .on('start', () =&amp;gt;{
 console.log(`${this.fileName}.${fileFormat} conversion started`);
 })
 }

 //If the file format matches the file's format, skip it and let us know.
 else{
 console.log(`Skipping ${this.fileName} conversion to ${fileFormat} as this file is already in the ${fileFormat} format.`);
 }
 });

 this.conversion.run();
 });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the real meat and potatoes of the build. A lot is happening here, so let's break it down.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Creates a directory named after the original video we're converting. This will hold all files generated for this video.&lt;/li&gt;
&lt;li&gt; Loops through each file format specified for this conversion.&lt;/li&gt;
&lt;li&gt; In the loop, we check to see if the current file format matches the format of the video we're converting. If they match, the converter skips that conversion and moves on to the next format. This keeps us from needlessly converting an .mp4 to another .mp4.&lt;/li&gt;
&lt;li&gt; If the formats are different, we queue up the converter using the specified format.&lt;/li&gt;
&lt;li&gt; Once we've looped through all of the formats we're converting to, we run the actual converter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test It Out
&lt;/h3&gt;

&lt;p&gt;We have now set up the actual converter. Let's see if it works as expected.&lt;/p&gt;

&lt;p&gt;Modify your commander action in your &lt;code&gt;index.js&lt;/code&gt; file to use the &lt;code&gt;getVideos()﻿&lt;/code&gt; method, 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;#!/usr/bin/env node
/**
 * Allows us to run this script as a cli command
 */
const program = require('commander');

const MultiFileConverter = require('./lib/MultiFileConverter');

/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

/**
 The run command
 */
program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{

 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a message for each video, stating that the conversion started for each format. It will also let you know if it skipped one of the conversions, and why. This will take a long time to convert, and since we're just testing, cancel the command (CTRL+C on a Mac) after about 20 seconds. Check your &lt;code&gt;converted-files&lt;/code&gt; directory and see if the video conversion started to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate Screenshots
&lt;/h2&gt;

&lt;p&gt;Sweet! Now that we have videos converting, let's get generate some screenshots while we're at it. The process of adding screenshots is very similar.&lt;/p&gt;

&lt;p&gt;Add a new method, called &lt;code&gt;getScreenshots()&lt;/code&gt; to your &lt;code&gt;MultiFileConverter.js&lt;/code&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;/**
 * Loops through and generates screenshots
 */
getScreenshots(){
 return this.getFiles().forEach(file =&amp;gt; file.getScreenshots());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works just like &lt;code&gt;getVideos()&lt;/code&gt;, only it runs &lt;code&gt;getScreenshots&lt;/code&gt; method on each &lt;code&gt;FileConverter&lt;/code&gt; object instead. Again, we need to create the convert method on the &lt;code&gt;FileConverter&lt;/code&gt; object for this to work.&lt;/p&gt;

&lt;p&gt;Add a new method, called &lt;code&gt;getScreenshots()&lt;/code&gt; to your &lt;code&gt;FileConverter.js&lt;/code&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;/**
 * Creates 6 screenshots taken throughout the video
 */
getScreenshots(){
 this.conversion
 .on('filenames', filenames =&amp;gt; console.log(`\n ${this.fileName} Will generate 6 screenshots, ${filenames.join('\n ')}`))
 .on('end', () =&amp;gt;{
 console.log(`\n Screenshots for ${this.fileName} complete.\n`)
 })
 .screenshots({
 count: 6,
 timestamps: [2, 5, '20%', '40%', '60%', '80%'],
 folder: this.outputPath,
 filename: `${this.fileName}-%s.png`
 })

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is a bit simpler than &lt;code&gt;getVideos()&lt;/code&gt;. We simply chain the &lt;code&gt;screenshots()&lt;/code&gt; method (included in our ffmpeg library) and pass some arguments. Our arguments instruct ffmpeg to create 6 screenshots at 2 seconds, 5 seconds, and at 20%, 40%, 60%, and 80% of the video. Each file is saved inside the same directory as our converted videos are saved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test It Out
&lt;/h3&gt;

&lt;p&gt;Let's make sure that we can generate screenshots.&lt;/p&gt;

&lt;p&gt;Modify your commander action in your &lt;code&gt;index.js&lt;/code&gt; file to use the &lt;code&gt;getScreenshots()﻿&lt;/code&gt; method, 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;#!/usr/bin/env node
/**
 * Allows us to run this script as a cli command
 */
const program = require('commander');

const MultiFileConverter = require('./lib/MultiFileConverter');

/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

/**
 The run command
 */
program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{
const converter = new MultiFileConverter();
return converter.getScreenshots();
 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a message for each video, listing off the screenshots that will be created. This will take a some time to convert, and since we're just testing, cancel the command (CTRL+C on a Mac) after about 20 seconds. Check your &lt;code&gt;converted-files&lt;/code&gt; directory and see if the screenshots started to generate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate Everything
&lt;/h2&gt;

&lt;p&gt;Now that we have a way to generate screenshots and convert our videos, we need to make one more method in our &lt;code&gt;MultiFileConverter.js&lt;/code&gt; file. This method will run both the &lt;code&gt;convert()&lt;/code&gt; method and the &lt;code&gt;getScreenshots()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;We are creating a third method to do both of these because it allows us to loop through the files once, instead of twice, and as such is more efficient than running &lt;code&gt;getVideos()&lt;/code&gt; and then &lt;code&gt;getScreenshots()&lt;/code&gt; separately.&lt;/p&gt;

&lt;p&gt;Add this method to your &lt;code&gt;MultiFileConverter&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Runs the complete converter, converting files and getting screenshots
 */
runConverter(){
 return this.getFiles().forEach((file) =&amp;gt;{
 file.convert();
 file.getScreenshots();
 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Commands
&lt;/h2&gt;

&lt;p&gt;Now that we have everything needed, let's create our 3 commands we talked about earlier - &lt;code&gt;asconvert videos&lt;/code&gt;, &lt;code&gt;asconvert screenshots&lt;/code&gt;, and &lt;code&gt;asconvert run&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Sets up the command to run from the cli
 */
program
 .version('0.1.0')
 .description('Convert Video Files From a Directory');

program
 .command('run')
 .description('Converts the files in the files-to-convert directory of this project')
 .action(() =&amp;gt;{
 const converter = new MultiFileConverter();
 return converter.runConverter();
 });

/**
 * Sets up the command to run from the cli
 */
program
 .command('screenshots')
 .description('Gets a screenshot of each video')
 .action(() =&amp;gt;{
 const converter = new MultiFileConverter();
 return converter.getScreenshots();
 });

/**
 * Sets up the command to run from the cli
 */
program
 .command('videos')
 .description('Gets conversions of each video')
 .action(() =&amp;gt;{
 const converter = new MultiFileConverter();
 return converter.getVideos();
 });

program.parse(process.argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now run any of those 3 commands, and convert videos, create screenshots, or do both at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Remarks
&lt;/h2&gt;

&lt;p&gt;There are a couple of things that could improve this tool.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; I'm sure someone who knows Docker better than I could put it in some kind of container to make this EZPZ to set up/tear down on a server&lt;/li&gt;
&lt;li&gt; The directory that houses the videos is a part of the project. With further configuration you could set this up so that the videos are pulled directly from Google Drive, or something like that. I didn't have a need for that, but it would be pretty slick.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All in all, it was a fun little build, and I'm sure it will save me some time in the future.&lt;/p&gt;

&lt;p&gt;If you are using this, I'd love to hear about how it worked for you, and why you needed it. Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>ffmpeg</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
