Tag: Blocks

Rendering External API Data in WordPress Blocks on the Back End

This is a continuation of my last article about “Rendering External API Data in WordPress Blocks on the Front End”. In that last one, we learned how to take an external API and integrate it with a block that renders the fetched data on the front end of a WordPress site.

The thing is, we accomplished this in a way that prevents us from seeing the data in the WordPress Block Editor. In other words, we can insert the block on a page but we get no preview of it. We only get to see the block when it’s published.

Let’s revisit the example block plugin we made in the last article. Only this time, we’re going to make use of the JavaScript and React ecosystem of WordPress to fetch and render that data in the back-end Block Editor as well.

Where we left off

As we kick this off, here’s a demo where we landed in the last article that you can reference. You may have noticed that I used a render_callback method in the last article so that I can make use of the attributes in the PHP file and render the content.

Well, that may be useful in situations where you might have to use some native WordPress or PHP function to create dynamic blocks. But if you want to make use of just the JavaScript and React (JSX, specifically) ecosystem of WordPress to render the static HTML along with the attributes stored in the database, you only need to focus on the Edit and Save functions of the block plugin.

  • The Edit function renders the content based on what you want to see in the Block Editor. You can have interactive React components here.
  • The Save function renders the content based on what you want to see on the front end. You cannot have the the regular React components or the hooks here. It is used to return the static HTML that is saved into your database along with the attributes.

The Save function is where we’re hanging out today. We can create interactive components on the front-end, but for that we need to manually include and access them outside the Save function in a file like we did in the last article.

So, I am going to cover the same ground we did in the last article, but this time you can see the preview in the Block Editor before you publish it to the front end.

The block props

I intentionally left out any explanations about the edit function’s props in the last article because that would have taken the focus off of the main point, the rendering.

If you are coming from a React background, you will likely understand what is that I am talking about, but if you are new to this, I would recommend checking out components and props in the React documentation.

If we log the props object to the console, it returns a list of WordPress functions and variables related to our block:

Console log of the block properties.

We only need the attributes object and the setAttributes function which I am going to destructure from the props object in my code. In the last article, I had modified RapidAPI’s code so that I can store the API data through setAttributes(). Props are only readable, so we are unable to modify them directly.

Block props are similar to state variables and setState in React, but React works on the client side and setAttributes() is used to store the attributes permanently in the WordPress database after saving the post. So, what we need to do is save them to attributes.data and then call that as the initial value for the useState() variable.

The edit function

I am going to copy-paste the HTML code that we used in football-rankings.php in the last article and edit it a little to shift to the JavaScript background. Remember how we created two additional files in the last article for the front end styling and scripts? With the way we’re approaching things today, there’s no need to create those files. Instead, we can move all of it to the Edit function.

Full code
import { useState } from "@wordpress/element"; export default function Edit(props) {   const { attributes, setAttributes } = props;   const [apiData, setApiData] = useState(null);     function fetchData() {       const options = {         method: "GET",         headers: {           "X-RapidAPI-Key": "Your Rapid API key",           "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",         },       };       fetch(         "https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39",           options       )       .then((response) => response.json())       .then((response) => {         let newData = { ...response }; // Deep clone the response data         setAttributes({ data: newData }); // Store the data in WordPress attributes         setApiData(newData); // Modify the state with the new data       })       .catch((err) => console.error(err));     }     return (       <div {...useBlockProps()}>         <button onClick={() => getData()}>Fetch data</button>         {apiData && (           <>           <div id="league-standings">             <div               className="header"               style={{                 backgroundImage: `url($ {apiData.response[0].league.logo})`,               }}             >               <div className="position">Rank</div>               <div className="team-logo">Logo</div>               <div className="team-name">Team name</div>               <div className="stats">                 <div className="games-played">GP</div>                 <div className="games-won">GW</div>                 <div className="games-drawn">GD</div>                 <div className="games-lost">GL</div>                 <div className="goals-for">GF</div>                 <div className="goals-against">GA</div>                 <div className="points">Pts</div>               </div>               <div className="form-history">Form history</div>             </div>             <div className="league-table">               {/* Usage of [0] might be weird but that is how the API structure is. */}               {apiData.response[0].league.standings[0].map((el) => {                                  {/* Destructure the required data from all */}                 const { played, win, draw, lose, goals } = el.all;                   return (                     <>                     <div className="team">                       <div class="position">{el.rank}</div>                       <div className="team-logo">                         <img src={el.team.logo} />                       </div>                       <div className="team-name">{el.team.name}</div>                       <div className="stats">                         <div className="games-played">{played}</div>                         <div className="games-won">{win}</div>                         <div className="games-drawn">{draw}</div>                         <div className="games-lost">{lose}</div>                         <div className="goals-for">{goals.for}</div>                         <div className="goals-against">{goals.against}</div>                         <div className="points">{el.points}</div>                       </div>                       <div className="form-history">                         {el.form.split("").map((result) => {                           return (                             <div className={`result-$ {result}`}>{result}</div>                           );                         })}                       </div>                     </div>                     </>                   );                 }               )}             </div>           </div>         </>       )}     </div>   ); }

I have included the React hook useState() from @wordpress/element rather than using it from the React library. That is because if I were to load the regular way, it would download React for every block that I am using. But if I am using @wordpress/element it loads from a single source, i.e., the WordPress layer on top of React.

This time, I have also not wrapped the code inside useEffect() but inside a function that is called only when clicking on a button so that we have a live preview of the fetched data. I have used a state variable called apiData to render the league table conditionally. So, once the button is clicked and the data is fetched, I am setting apiData to the new data inside the fetchData() and there is a rerender with the HTML of the football rankings table available.

You will notice that once the post is saved and the page is refreshed, the league table is gone. That is because we are using an empty state (null) for apiData‘s initial value. When the post saves, the attributes are saved to the attributes.data object and we call it as the initial value for the useState() variable like this:

const [apiData, setApiData] = useState(attributes.data);

The save function

We are going to do almost the same exact thing with the save function, but modify it a little bit. For example, there’s no need for the “Fetch data” button on the front end, and the apiData state variable is also unnecessary because we are already checking it in the edit function. But we do need a random apiData variable that checks for attributes.data to conditionally render the JSX or else it will throw undefined errors and the Block Editor UI will go blank.

Full code
export default function save(props) {   const { attributes, setAttributes } = props;   let apiData = attributes.data;   return (     <>       {/* Only render if apiData is available */}       {apiData && (         <div {...useBlockProps.save()}>         <div id="league-standings">           <div             className="header"             style={{               backgroundImage: `url($ {apiData.response[0].league.logo})`,             }}           >             <div className="position">Rank</div>             <div className="team-logo">Logo</div>             <div className="team-name">Team name</div>             <div className="stats">               <div className="games-played">GP</div>               <div className="games-won">GW</div>               <div className="games-drawn">GD</div>               <div className="games-lost">GL</div>               <div className="goals-for">GF</div>               <div className="goals-against">GA</div>               <div className="points">Pts</div>             </div>             <div className="form-history">Form history</div>           </div>           <div className="league-table">             {/* Usage of [0] might be weird but that is how the API structure is. */}             {apiData.response[0].league.standings[0].map((el) => {               const { played, win, draw, lose, goals } = el.all;                 return (                   <>                   <div className="team">                     <div className="position">{el.rank}</div>                       <div className="team-logo">                         <img src={el.team.logo} />                       </div>                       <div className="team-name">{el.team.name}</div>                       <div className="stats">                         <div className="games-played">{played}</div>                         <div className="games-won">{win}</div>                         <div className="games-drawn">{draw}</div>                         <div className="games-lost">{lose}</div>                         <div className="goals-for">{goals.for}</div>                         <div className="goals-against">{goals.against}</div>                         <div className="points">{el.points}</div>                       </div>                       <div className="form-history">                         {el.form.split("").map((result) => {                           return (                             <div className={`result-$ {result}`}>{result}</div>                           );                         })}                       </div>                     </div>                   </>                 );               })}             </div>           </div>         </div>       )}     </>   ); }

If you are modifying the save function after a block is already present in the Block Editor, it would show an error like this:

The football rankings block in the WordPress block Editor with an error message that the block contains an unexpected error.

That is because the markup in the saved content is different from the markup in our new save function. Since we are in development mode, it is easier to remove the bock from the current page and re-insert it as a new block — that way, the updated code is used instead and things are back in sync.

This situation of removing it and adding it again can be avoided if we had used the render_callback method since the output is dynamic and controlled by PHP instead of the save function. So each method has it’s own advantages and disadvantages.

Tom Nowell provides a thorough explanation on what not to do in a save function in this Stack Overflow answer.

Styling the block in the editor and the front end

Regarding the styling, it is going to be almost the same thing we looked at in the last article, but with some minor changes which I have explained in the comments. I’m merely providing the full styles here since this is only a proof of concept rather than something you want to copy-paste (unless you really do need a block for showing football rankings styled just like this). And note that I’m still using SCSS that compiles to CSS on build.

Editor styles
/* Target all the blocks with the data-title="Football Rankings" */ .block-editor-block-list__layout  .block-editor-block-list__block.wp-block[data-title="Football Rankings"] {   /* By default, the blocks are constrained within 650px max-width plus other design specific code */   max-width: unset;   background: linear-gradient(to right, #8f94fb, #4e54c8);   display: grid;   place-items: center;   padding: 60px 0;    /* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties really not needed :) */   button.fetch-data {     align-items: center;     background-color: #ffffff;     border: 1px solid rgb(0 0 0 / 0.1);     border-radius: 0.25rem;     box-shadow: rgb(0 0 0 / 0.02) 0 1px 3px 0;     box-sizing: border-box;     color: rgb(0 0 0 / 0.85);     cursor: pointer;     display: inline-flex;     font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;     font-size: 16px;     font-weight: 600;     justify-content: center;     line-height: 1.25;     margin: 0;     min-height: 3rem;     padding: calc(0.875rem - 1px) calc(1.5rem - 1px);     position: relative;     text-decoration: none;     transition: all 250ms;     user-select: none;     -webkit-user-select: none;     touch-action: manipulation;     vertical-align: baseline;     width: auto;     &:hover,     &:focus {       border-color: rgb(0, 0, 0, 0.15);       box-shadow: rgb(0 0 0 / 0.1) 0 4px 12px;       color: rgb(0, 0, 0, 0.65);     }     &:hover {       transform: translateY(-1px);     }     &:active {       background-color: #f0f0f1;       border-color: rgb(0 0 0 / 0.15);       box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;       color: rgb(0 0 0 / 0.65);       transform: translateY(0);     }   } }
Front-end styles
/* Front-end block styles */ .wp-block-post-content .wp-block-football-rankings-league-table {   background: linear-gradient(to right, #8f94fb, #4e54c8);   max-width: unset;   display: grid;   place-items: center; }  #league-standings {   width: 900px;   margin: 60px 0;   max-width: unset;   font-size: 16px;   .header {     display: grid;     gap: 1em;     padding: 10px;     grid-template-columns: 1fr 1fr 3fr 4fr 3fr;     align-items: center;     color: white;     font-size: 16px;     font-weight: 600;     background-color: transparent;     background-repeat: no-repeat;     background-size: contain;     background-position: right;      .stats {       display: flex;       gap: 15px;       &amp; &gt; div {         width: 30px;       }     }   } } .league-table {   background: white;   box-shadow:     rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,     rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;   padding: 1em;   .position {     width: 20px;   }   .team {     display: grid;     gap: 1em;     padding: 10px 0;     grid-template-columns: 1fr 1fr 3fr 4fr 3fr;     align-items: center;   }   .team:not(:last-child) {     border-bottom: 1px solid lightgray;   }   .team-logo img {     width: 30px;     top: 3px;     position: relative;   }   .stats {     display: flex;     gap: 15px;     &amp; &gt; div {       width: 30px;       text-align: center;     }   }   .last-5-games {     display: flex;     gap: 5px;     &amp; &gt; div {       width: 25px;       height: 25px;       text-align: center;       border-radius: 3px;       font-size: 15px;     &amp; .result-W {       background: #347d39;       color: white;     }     &amp; .result-D {       background: gray;       color: white;     }     &amp; .result-L {       background: lightcoral;       color: white;     }   } } 

We add this to src/style.scss which takes care of the styling in both the editor and the frontend. I will not be able to share the demo URL since it would require editor access but I have a video recorded for you to see the demo:


Pretty neat, right? Now we have a fully functioning block that not only renders on the front end, but also fetches API data and renders right there in the Block Editor — with a refresh button to boot!

But if we want to take full advantage of the WordPress Block Editor, we ought to consider mapping some of the block’s UI elements to block controls for things like setting color, typography, and spacing. That’s a nice next step in the block development learning journey.


Rendering External API Data in WordPress Blocks on the Back End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , , ,

Rendering External API Data in WordPress Blocks on the Front End

There’ve been some new tutorials popping here on CSS-Tricks for working with WordPress blocks. One of them is an introduction to WordPress block development and it’s a good place to learn what blocks are and to register them in WordPress for use in pages and posts.

While the block basics are nicely covered in that post, I want to take it another step forward. You see, in that article, we learned the difference between rendering blocks in the back-end WordPress Block Editor and rendering them on the front-end theme. The example was a simple Pullquote Block that rendered different content and styles on each end.

Let’s go further and look at using dynamic content in a WordPress block. More specifically, let’s fetch data from an external API and render it on the front end when a particular block is dropped into the Block Editor.

We’re going to build a block that outputs data that shows soccer (er, football) rankings pulled from Api-Football.

An ordered set of football team rankings showing team logos, names, and game results.
This is what we’re working for together.

There’s more than one way to integrate an API with a WordPress block! Since the article on block basics has already walked through the process of making a block from scratch, we’re going to simplify things by using the @wordpress/create-block package to bootstrap our work and structure our project.

Initializing our block plugin

First things first: let’s spin up a new project from the command line:

npx @wordpress/create-block football-rankings

I normally would kick a project like this off by making the files from scratch, but kudos to the WordPress Core team for this handy utility!

Once the project folder has been created by the command, we technically have a fully-functional WordPress block registered as a plugin. So, let’s go ahead and drop the project folder into the wp-content/plugins directory where you have WordPress installed (probably best to be working in a local environment), then log into the WordPress admin and activate it from the Plugins screen.

Now that our block is initialized, installed, and activated, go ahead and open up the project folder from at /wp-content/plugins/football-rankings. You’re going to want to cd there from the command line as well to make sure we can continue development.

These are the only files we need to concentrate on at the moment:

  • edit.js
  • index.js
  • football-rankings.php

The other files in the project are important, of course, but are inessential at this point.

Reviewing the API source

We already know that we’re using Api-Football which comes to us courtesy of RapidAPI. Fortunately, RapidAPI has a dashboard that automatically generates the required scripts we need to fetch the API data for the 2021 Premier League Standings.

A dashboard interface with three columns showing code and data from an API source.
The RapidAPI dashboard

If you want to have a look on the JSON structure, you can generate visual representation with JSONCrack.

Fetching data from the edit.js file

I am going to wrap the RapidAPI code inside a React useEffect() hook with an empty dependency array so that it runs only once when the page is loaded. This way, we prevent WordPress from calling the API each time the Block Editor re-renders. You can check that using wp.data.subscribe() if you care to.

Here’s the code where I am importing useEffect(), then wrapping it around the fetch() code that RapidAPI provided:

/** * The edit function describes the structure of your block in the context of the * editor. This represents what the editor will render when the block is used. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit * * @return {WPElement} Element to render. */  import { useEffect } from "@wordpress/element";  export default function Edit(props) {   const { attributes, setAttributes } = props;    useEffect(() => {     const options = {       method: "GET",       headers: {         "X-RapidAPI-Key": "Your Rapid API key",         "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",       },     };      fetch("https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39", options)       .then( ( response ) => response.json() )       .then( ( response ) => {         let newData = { ...response };         setAttributes( { data: newData } );         console.log( "Attributes", attributes );       })       .catch((err) => console.error(err)); }, []);    return (     <p { ...useBlockProps() }>       { __( "Standings loaded on the front end", "external-api-gutenberg" ) }     </p>   ); }

Notice that I have left the return function pretty much intact, but have included a note that confirms the football standings are rendered on the front end. Again, we’re only going to focus on the front end in this article — we could render the data in the Block Editor as well, but we’ll leave that for another article to keep things focused.

Storing API data in WordPress

Now that we are fetching data, we need to store it somewhere in WordPress. This is where the attributes.data object comes in handy. We are defining the data.type as an object since the data is fetched and formatted as JSON. Make sure you don’t have any other type or else WordPress won’t save the data, nor does it throw any error for you to debug.

We define all this in our index.js file:

registerBlockType( metadata.name, {   edit: Edit,   attributes: {     data: {       type: "object",     },   },   save, } );

OK, so WordPress now knows that the RapidAPI data we’re fetching is an object. If we open a new post draft in the WordPress Block Editor and save the post, the data is now stored in the database. In fact, if we can see it in the wp_posts.post_content field if we open the site’s database in phpMyAdmin, Sequel Pro, Adminer, or whatever tool you use.

Showing a large string of JSON output in a database table.
API output stored in the WordPress database

Outputting JSON data in the front end

There are multiple ways to output the data on the front end. The way I’m going to show you takes the attributes that are stored in the database and passes them as a parameter through the render_callback function in our football-rankings.php file.

I like keeping a separation of concerns, so how I do this is to add two new files to the block plugin’s build folder: frontend.js and frontend.css (you can create a frontend.scss file in the src directory which compiled to CSS in the build directory). This way, the back-end and front-end codes are separate and the football-rankings.php file is a little easier to read.

/explanation Referring back to the introduction to WordPress block development, there are editor.css and style.css files for back-end and shared styles between the front and back end, respectively. By adding frontend.scss (which compiles to frontend.css, I can isolate styles that are only intended for the front end.

Before we worry about those new files, here’s how we call them in football-rankings.php:

/** * Registers the block using the metadata loaded from the `block.json` file. * Behind the scenes, it registers also all assets so they can be enqueued * through the block editor in the corresponding context. * * @see https://developer.wordpress.org/reference/functions/register_block_type/ */ function create_block_football_rankings_block_init() {   register_block_type( __DIR__ . '/build', array(     'render_callback' => 'render_frontend'   )); } add_action( 'init', 'create_block_football_rankings_block_init' );  function render_frontend($ attributes) {   if( !is_admin() ) {     wp_enqueue_script( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.js');     wp_enqueue_style( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.css' ); // HIGHLIGHT 15,16,17,18   }      ob_start(); ?>    <div class="football-rankings-frontend" id="league-standings">     <div class="data">       <pre>         <?php echo wp_json_encode( $ attributes ) ?>       </pre>     </div>     <div class="header">       <div class="position">Rank</div>       <div class="team-logo">Logo</div>       <div class="team-name">Team name</div>       <div class="stats">         <div class="games-played">GP</div>         <div class="games-won">GW</div>         <div class="games-drawn">GD</div>         <div class="games-lost">GL</div>         <div class="goals-for">GF</div>         <div class="goals-against">GA</div>         <div class="points">Pts</div>       </div>       <div class="form-history">Last 5 games</div>     </div>     <div class="league-table"></div>   </div>    <?php return ob_get_clean(); }

Since I am using the render_callback() method for the attributes, I am going to handle the enqueue manually just like the Block Editor Handbook suggests. That’s contained in the !is_admin() condition, and is enqueueing the two files so that we avoid enqueuing them while using the editor screen.

Now that we have two new files we’re calling, we’ve gotta make sure we are telling npm to compile them. So, do that in package.json, in the scripts section:

"scripts": {   "build": "wp-scripts build src/index.js src/frontend.js",   "format": "wp-scripts format",   "lint:css": "wp-scripts lint-style",   "lint:js": "wp-scripts lint-js",   "packages-update": "wp-scripts packages-update",   "plugin-zip": "wp-scripts plugin-zip",   "start": "wp-scripts start src/index.js src/frontend.js" },

Another way to include the files is to define them in the block metadata contained in our block.json file, as noted in the introduction to block development:

"viewScript": [ "file:./frontend.js", "example-shared-view-script" ], "style": [ "file:./frontend.css", "example-shared-style" ],

The only reason I’m going with the package.json method is because I am already making use of the render_callback() method.

Rendering the JSON data

In the rendering part, I am concentrating only on a single block. Generally speaking, you would want to target multiple blocks on the front end. In that case, you need to make use of document.querySelectorAll() with the block’s specific ID.

I’m basically going to wait for the window to load and grab data for a few key objects from JSON and apply them to some markup that renders them on the front end. I am also going to convert the attributes data to a JSON object so that it is easier to read through the JavaScript and set the details from JSON to HTML for things like the football league logo, team logos, and stats.

The “Last 5 games” column shows the result of a team’s last five matches. I have to manually alter the data for it since the API data is in a string format. Converting it to an array can help make use of it in HTML as a separate element for each of a team’s last five matches.

import "./frontend.scss";  // Wait for the window to load window.addEventListener( "load", () => {   // The code output   const dataEl = document.querySelector( ".data pre" ).innerHTML;   // The parent rankings element   const tableEl = document.querySelector( ".league-table" );   // The table headers   const tableHeaderEl = document.querySelector( "#league-standings .header" );   // Parse JSON for the code output   const dataJSON = JSON.parse( dataEl );   // Print a little note in the console   console.log( "Data from the front end", dataJSON );      // All the teams    let teams = dataJSON.data.response[ 0 ].league.standings[ 0 ];   // The league logo   let leagueLogoURL = dataJSON.data.response[ 0 ].league.logo;   // Apply the league logo as a background image inline style   tableHeaderEl.style.backgroundImage = `url( $ { leagueLogoURL } )`;      // Loop through the teams   teams.forEach( ( team, index ) => {     // Make a div for each team     const teamDiv = document.createElement( "div" );     // Set up the columns for match results     const { played, win, draw, lose, goals } = team.all;      // Add a class to the parent rankings element     teamDiv.classList.add( "team" );     // Insert the following markup and data in the parent element     teamDiv.innerHTML = `       <div class="position">         $ { index + 1 }       </div>       <div class="team-logo">         <img src="$ { team.team.logo }" />       </div>       <div class="team-name">$ { team.team.name }</div>       <div class="stats">         <div class="games-played">$ { played }</div>         <div class="games-won">$ { win }</div>         <div class="games-drawn">$ { draw }</div>         <div class="games-lost">$ { lose }</div>         <div class="goals-for">$ { goals.for }</div>         <div class="goals-against">$ { goals.against }</div>         <div class="points">$ { team.points }</div>       </div>       <div class="form-history"></div>     `;          // Stringify the last five match results for a team     const form = team.form.split( "" );          // Loop through the match results     form.forEach( ( result ) => {       // Make a div for each result       const resultEl = document.createElement( "div" );       // Add a class to the div       resultEl.classList.add( "result" );       // Evaluate the results       resultEl.innerText = result;       // If the result a win       if ( result === "W" ) {         resultEl.classList.add( "win" );       // If the result is a draw       } else if ( result === "D" ) {         resultEl.classList.add( "draw" );       // If the result is a loss       } else {         resultEl.classList.add( "lost" );       }       // Append the results to the column       teamDiv.querySelector( ".form-history" ).append( resultEl );     });      tableEl.append( teamDiv );   }); });

As far as styling goes, you’re free to do whatever you want! If you want something to work with, I have a full set of styles you can use as a starting point.

I styled things in SCSS since the @wordpress/create-block package supports it out of the box. Run npm run start in the command line to watch the SCSS files and compile them to CSS on save. Alternately, you can use npm run build on each save to compile the SCSS and build the rest of the plugin bundle.

View SCSS
body {   background: linear-gradient(to right, #8f94fb, #4e54c8); }  .data pre {   display: none; }  .header {   display: grid;   gap: 1em;   padding: 10px;   grid-template-columns: 1fr 1fr 3fr 4fr 3fr;   align-items: center;   color: white;   font-size: 16px;   font-weight: 600;   background-repeat: no-repeat;   background-size: contain;   background-position: right; }  .frontend#league-standings {   width: 900px;   margin: 60px 0;   max-width: unset;   font-size: 16px;    .header {     .stats {       display: flex;       gap: 15px;        &amp; &gt; div {         width: 30px;       }     }   } }  .league-table {   background: white;   box-shadow:     rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,     rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;   padding: 1em;    .position {     width: 20px;   }    .team {     display: grid;     gap: 1em;     padding: 10px 0;     grid-template-columns: 1fr 1fr 3fr 4fr 3fr;     align-items: center;   }    .team:not(:last-child) {     border-bottom: 1px solid lightgray;   }    .team-logo img {     width: 30px;   }    .stats {     display: flex;     gap: 15px;   }    .stats &gt; div {     width: 30px;     text-align: center;   }    .form-history {     display: flex;     gap: 5px;   }    .form-history &gt; div {     width: 25px;     height: 25px;     text-align: center;     border-radius: 3px;     font-size: 15px;   }    .form-history .win {     background: #347d39;     color: white;   }    .form-history .draw {     background: gray;     color: white;   }    .form-history .lost {     background: lightcoral;     color: white;   } }

Here’s the demo!

Check that out — we just made a block plugin that fetches data and renders it on the front end of a WordPress site.

We found an API, fetch()-ed data from it, saved it to the WordPress database, parsed it, and applied it to some HTML markup to display on the front end. Not bad for a single tutorial, right?

Again, we can do the same sort of thing so that the rankings render in the Block Editor in addition to the theme’s front end. But hopefully keeping this focused on the front end shows you how fetching data works in a WordPress block, and how the data can be structured and rendered for display.


Rendering External API Data in WordPress Blocks on the Front End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , , ,
[Top]

Code blocks, but better

Pedro Duarte made a wishlist for styled code blocks in blog posts and documentation, then hand-rolls a perfect solution for that wishlist. For example, a feature to be able to highlight certain lines or words within the code block. The line highlighter is unique in that it only syntax highlights the highlighted lines, leaving the rest gray, which is a neat way to draw focus. And the word highlighter works via a RegEx. Pedro notes this isn’t a tutorial, it’s just a showcase of all the features that were stitched together using an existing combination of custom code and existing libraries. It’s pretty specific to React + Next.js + MDX, but apparently the core of it is based on rehype, which is new to me.

The results are pretty darn nice, modern-looking code blocks if you ask me! At the same time, I think it’s equally notable what’s not there as opposed to what is. I’ve seen (and tried) really featured-packed code blocks in the past, with features like a copy to clipboard button, a view raw code button, and export to whatever services. Pedro’s code blocks don’t even have an option to show the language in use.

Everybody’s wishlist is different. One thing that isn’t on Pedro’s wishlist is server-side rendering, but you can see on the blog post itself that it totally works with that (it’s presumably just Next.js at work). I’m pretty envious of that. Even though we’re ultimately both using Prism.js as the syntax highlighter, I’m only using it client-side. It occurs to me now that I could perhaps pull all this off in a Cloudflare Worker by using the HTMLRewriter — meaning it would essentially look like it’s done server-side and I could rip off the client-side copy of Prism. Maybe even more ideally, though, is that I’d do it as a WordPress plugin. Basically a PHP port of Prism, which seems like a tall order.

My wishlist for code block plugin…

  • Syntax highlighting (both on the rendered site and while authoring)
  • Server-side rendered <span class="token-foo"> stuff for syntax highlighting
  • Works nicely with the native WordPress block editor code blocks (```). For example, pasting in a code block auto-detects and uses the correct block. Easy to convert code to and from this kind of block.
  • Optional line highlighter
  • Optional line numbers
  • Optional word highlighter
  • Optional language display (and the ability to override that label)
  • Copy and paste very cleanly
  • No need to escape code while authoring
  • Freedom to style however on the front end (for modes, themes, custom scrollbars, etc). Styling code blocks has a million things to consider, so smart defaults should probably come with the plugin, but easy to override.
  • Stretch goal: can it somehow help with inline code as well?

Direct Link to ArticlePermalink


The post Code blocks, but better appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

Styling Code In and Out of Blocks

We’ll get to that, but first, a long-winded introduction.

I’m still not in a confident place knowing a good time to use native web components. The templating isn’t particularly robust, so that doesn’t draw me in. There is no state management, and I like having standard ways of handling that. If I’m using another library for components anyway, seems like I would just stick with that. So, at the moment, my checklist is something like:

  • Not using any other JavaScript framework that has components
  • Templating needs aren’t particularly complex
  • Don’t need particularly performant re-rendering
  • Don’t need state management

I’m sure there is tooling that helps with these things and more (the devMode episode with some folks from Stencil was good), but if I’m going to get into tooling-land, I’d be extra tempted to go with a framework, and probably not framework plus another thing with a lot of overlap.

The reasons I am tempted to go with native web components are:

  • They are native. No downloads of frameworks.
  • The Shadow DOM is a true encapsulation in a way a framework can’t really do.
  • I get to build my own HTML element that I use in HTML, with my own API design.

It sorta seems like the sweet spot for native web components is design system components. You build out your own little API for the components in your system, and people can use them in a way that is a lot safer than just copy and paste this chunk of HTML. And I suppose if consumers of the system wanted to BYO framework, they could.

So you can use like <our-tabs active-tab="3"> rather than <div class="tabs"> ... <a href="#3" class="tab-is-active">. Refactoring the components certainly gets a lot easier as changes percolate everywhere.

I’ve used them here on CSS-Tricks for our <circle-text> component. It takes the radius as a parameter and the content via, uh, content, and outputs an <svg> that does the trick. It gave us a nice API for authoring that abstracted away the complexity.

So!

It occurred to me a “code block” might be a nice use-case for a web component.

  • The API would be nice for it, as you could have attributes control useful things, and the code itself as the content (which is a great fallback).
  • It doesn’t really need state.
  • Syntax highlighting is a big gnarly block of CSS, so it would be kinda cool to isolate that away in the Shadow DOM.
  • It could have useful functionality like a “click to copy” button that people might enjoy having.

Altogether, it might feel like a yeah, I could use this kinda component.

This probably isn’t really production ready (for one thing, it’s not on npm or anything yet), but here’s where I am so far:

Here’s a thought dump!

  • What do you do when a component depends on a third-party lib? The syntax highlighting here is done with Prism.js. To make it more isolated, I suppose you could copy and paste the whole lib in there somewhere, but that seems silly. Maybe you just document it?
  • Styling web components doesn’t feel like it has a great story yet, despite the fact that Shadow DOM is cool and useful.
  • Yanking in pre-formatted text to use in a template is super weird. I’m sure it’s possible to do without needing a <pre> tag inside the custom element, but it’s clearly much easier if you grab the content from the <pre>. Makes the API here just a smidge less friendly (because I’d prefer to use the <code-block> alone).
  • I wonder what a good practice is for passing along attributes that another library needs. Like is data-lang="CSS" OK to use (feels nicer), and then convert it to class="language-css" in the template because that’s what Prism wants? Or is it better practice to just pass along attributes as they are? (I went with the latter.)
  • People complain that there aren’t really “lifecycle methods” in native web components, but at least you have one: when the thing renders: connectedCallback. So, I suppose you should do all the manipulation of HTML and such before you do that final shadowRoot.appendChild(node);. I’m not doing that here, and instead am running Prism over the whole shadowRoot after it’s been appended. Just seemed to work that way. I imagine it’s probably better, and possible, to do it ahead of time rather than allow all the repainting caused by injecting spans and such.
  • The whole point of this is a nice API. Seems to me thing would be nicer if it was possible to drop un-escaped HTML in there to highlight and it could escape it for you. But that makes the fallback actually render that HTML which could be bad (or even theoretically insecure). What’s a good story for that? Maybe put the HTML in HTML comments and test if <!-- is the start of the content and handle that as a special situation?

Anyway, if you wanna fork it or do anything fancier with it, lemme know. Maybe we can eventually put it on npm or whatever. We’ll have to see how useful people think it could be.


The post Styling Code In and Out of Blocks appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

How to Create Custom WordPress Editor Blocks in 2020

Peter Tasker on creating blocks right now:

It’s fairly straightforward these days to get set up with the WP CLI ‘scaffold’ command. This command will set up a WordPress theme or plugin with a ‘blocks’ folder that contains the PHP and base CSS and JavaScript required to create a custom block. The only drawback that I noticed is that the JavaScript uses the old ES5 syntax rather than modern ESNext. Modern JavaScript allows us to write more concise code and use JSX in our custom block code.

You can also use the ‘create-guten-block’ tool by Ahmad Awais. It gives you a lot of the boilerplate stuff you need out of the box, like Webpack, ESNext support etc. Setting it up is fairly straightforward, and it’s similar to Create React App.

I’ve used create-guten-block for the handful of custom blocks I’ve made so far, and have found it a pretty nice experience.

But… I feel like I just sort of lucked into being comfortable with all this. I have one foot in WordPress development and just so happen to have one foot in React development. Building blocks with both technologies together feels decently natural to me. If blocks were Angular or something, I feel like I might not have even given it a shot.

I’ll echo this sentiment:

I also found it really annoying working on a block that’s actively changing in code. Every time you reload Gutenberg, you’ll get the “This block appears to have been modified externally…” message because the markup of the block has changed.

I get why it’s throwing the error, but it slows you down.

At the end, Peter mentions the approach of building blocks that Advanced Custom Fields has. It almost feels like a weird bizarro-reverso world. The ACF approach seems more like what WordPress would have done in a normal world (building blocks with just PHP and templating) and third-parties would be the ones adding all the fancy React stuff.

Direct Link to ArticlePermalink

The post How to Create Custom WordPress Editor Blocks in 2020 appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

When CSS Blocks

Tim Kadlec:

One particular pattern [for loading non-critical CSS] I’ve seen is the preload/polyfill pattern. With this approach, you load any stylesheets as preloads instead, and then use their onload events to change them back to a stylesheet once the browser has them ready.

So you’re trying to make your stylesheet more async, but it causes two big problems:

  1. You’ve kicked up the priority of the downloading higher than any other asset.
  2. You’ve blocked the HTML parser too (because of the polyfill as an inline script).

Firefox does something fancy to avoid problem #2 in this particular case, but it affects every other browser.

I’ve never had good luck with fancy techniques to trick the browser into theoretically better downloading/rendering patterns. I’m kind of a stylesheets in the head, scripts at the end of the body kinda guy, but I know the web is a complicated place. In fact, in a quick peek, I see that Jetpack is inserting an inline script into my <head>, so that would affect my loading too, except they load it with an obfuscated type until later scripts execute and change it, probably to avoid this exact problem.

Anyway, Tim’s advice:

• If you’re using loadCSS with the preload/polyfill pattern, switch to the print stylesheet pattern instead.

• If you have any external stylesheets that you’re loading normally (that is, as a regular stylesheet link), move any and all inline scripts that you can above it in the markup

• Inline your critical CSS for the fastest possible start render times.

The print pattern being:

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

Direct Link to ArticlePermalink

The post When CSS Blocks appeared first on CSS-Tricks.

CSS-Tricks

[Top]

Jetpack Gutenberg Blocks

I remember when Gutenberg was released into core, because I was at WordCamp US that day. A number of months have gone by now, so I imagine more and more of us on WordPress sites have dipped our toes into it. I just wrote about our first foray here on CSS-Tricks and using Gutenberg to power our newsletter.

Jetpack, of course, was ahead of the game. Jetpack adds a bunch of special, powerful blocks to Gutenberg that it’s easy to see how useful they can be.

Here they are, as of this writing:

Maps! Subscriptions! GIFs! There are so many good ones. Here’s a look at a few more:

The form widget, I hear, is the most popular.

You get a pretty powerful form builder right within your editor:

Instant Markdown Processing

Jetpack has always enabled Markdown support for WordPress, so it’s nice that there is a Markdown widget!

PayPal Selling Blocks

There is even basic eCommerce blocks, which I just love as you can imagine how empowering that could be for some folks.

You can read more about Jetpack-specific Gutenberg blocks in their releases that went out for 6.8 and 6.9. Here at CSS-Tricks, we use a bunch of Jetpack features.

The post Jetpack Gutenberg Blocks appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]