Creating a Settings UI for a Custom WordPress Block | CSS-Tricks

Creating a Settings UI for a Custom WordPress Block | CSS-Tricks

Creating a Settings UI for a Custom WordPress Block
Nov 17, 2022
DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!
So far, we’ve covered how to work with data from an external API in a custom WordPress block. We walked through the process of fetching that data for use on the front end of a WordPress site, and how to render it directly in the WordPress Block Editor when placing the block in content. This time, we’re going to bridge those two articles by hooking into the block editor’s control panel to create a settings UI for the block we made.
Working With External APIs in WordPress Blocks
Creating a Custom Settings UI (you are here!)
Saving Custom Block Settings (coming soon)
You know the control panel I’m referring to, right? It’s that panel on the right that contains post and block settings in the block editor.
See that red highlighted area? That’s the control panel. A Paragraph block is currently selected and the settings for it are displayed in the panel. We can change styles, color, typography… a number of things!
Well, that’s exactly what we’re doing this time around. We’re going to create the controls for the settings of the Football Rankings block we worked on in the last two articles. Last time, we made a button in our block that fetches the external data for the football rankings. We already knew the URL and endpoints we needed. But what if we want to fetch ranking for a different country? Or maybe a different league? How about data from a different season?
We need form controls to do that. We could make use of interactive React components — like React-Select — to browse through the various API options that are available to parse that data. But there’s no need for that since WordPress ships with a bunch of core components that we hook right into!
The documentation for these components — called InspectorControls — is getting better in the WordPress Block Editor Handbook . That’ll get even better over time, but meanwhile, we also have the WordPress Gutenberg Storybook and WordPress Gutenberg Components sites for additional help.
The API architecture
Before we hook into anything, it’s a good idea to map out what it is we need in the first place. I’ve mapped out the structure of the RapidAPI data we’re fetching so we know what’s available to us:
Credit:  API-Football
Seasons and countries are two top-level endpoints that map to a leagues endpoint. From there, we have the rest of the data we’re already using to populate the rankings table. So, what we want to do is create settings in the WordPress Block Editor that filter the data by Season, Country, and League, then pass that filtered data into the rankings table. That gives us the ability to drop the block in any WordPress page or post and display variations of the data in the block.
In order to get the standings, we need to first get the leagues. And in order to get the leagues, we first need to get the countries and/or the seasons. You can view the various endpoints in the RapidAPI dashboard.
There are different combinations of data that we can use to populate the rankings, and you might have a preference for which data you want. For the sake of this article, we are going to create the following options in the block settings panel:
Choose Country
Choose League
Choose Season
Then we’ll have a button to submit those selections and fetch the relevant data and pass them into the rankings table.
Load and store a list of countries
We can’t select which country we want data for if we don’t have a list of countries to choose from. So, our first task is to grab a list of countries from RapidAPI.
The ideal thing is to fetch the list of countries when the block is actually used in the page or post content. There’s no need to fetch anything if the block isn’t in use. The approach is very similar to what we did in the first article , the difference being that we are using a different API endpoint and different attributes to store the list of returned countries. There are other WordPress ways to fetch data, like api-fetch , but that‘s outside the scope of what we’re doing here.
We can either include the country list manually after copying it from the API data, or we could use a separate API or library to populate the countries. But the API we’re using already has a list of countries, so I would just use one of its endpoints. Let’s make sure the initial country list loads when the block is inserted into the page or post content in the block editor:
// edit.js const [countriesList, setCountriesList] = useState(null); useEffect(() => , }; fetch("https://api-football-v1.p.rapidapi.com/v3/countries", countryOptions) .then( (response) => response.json() ) .then( (response) => ; console.log("Countries list", countriesArray.response); setCountriesList(countriesArray.response); }) .catch((err) => console.error(err)); }, []);
We have a state variable to store the list of countries. Next, we are going to import a component from the @wordpress/block-editor package called InspectorControls which is where all of the components we need to create our settings controls are located.
import from "@wordpress/block-editor";
The package’s GitHub repo does a good job explaining InspectorControls. In our example, we can use it to control the API data settings like Country, League, and Season. Here’s a preview so that you get an idea of the UI we’re making:
And once those selections are made in the block settings, we use them in the block’s Edit function :

Here, I am making sure that we are using conditional rendering so that the function only loads the component after the list of countries is loaded. If you’re wondering about that LeagueSettings component, it is a custom component I created in a separate components subfolder in the block so we can have a cleaner and more organized Edit function instead of hundreds of lines of country data to deal with in a single file.
We can import it into the edit.js file like this:
import from "./components/LeagueSettings";
Next, we’re passing the required props to the LeagueSettings component from the parent Edit component so that we can access the state variables and attributes from the LeagueSettings child component. We can also do that with other methods like the Context API to avoid prop drilling, but what we have right now is perfectly suitable for what we’re doing.
The other parts of the Edit function can also be converted into components. For example, the league standings code can be put inside a separate component — like maybe LeagueTable.js — and then imported just like we imported LeagueSettings into the Edit function.
Inside the LeagueSettings.js file
LeagueSettings is just like another React component from which we can destructure the props from the parent component. I am going to use three state variables and an additional leagueID state because we are going to extract the ID from the league object:
const [country, setCountry] = useState(null); const [league, setLeague] = useState(null); const [season, setSeason] = useState(null); const [leagueID, setLeagueID] = useState(null);
The first thing we’re going to do is import the PanelBody component from the @wordpress/block-editor package:
import from "@wordpress/block-editor";
…and include it in our return function:
There are other panel tags and attributes — it’s just my personal preference to use these ones. None of the others are required… but look at all the components we have available to make a settings panel! I like the simplicity of the PanelBody for our use case. It expands and collapses to reveal the dropdown settings for the block and that’s it.
Speaking of which, we have a choice to make for those selections. We could use the SelectControl component or a ComboBoxControl , which the docs describe as “an enhanced version of a SelectControl, with the addition of being able to search for options using a search input.” That’s nice for us because the list of countries could get pretty long and users will be able to either do a search query or select from a list.
Here’s an example of how a ComboboxControl could work for our country list:
handleCountryChange(value) } onInputChange=} />
The ComboboxControl is configurable in the sense that we can apply different sizing for the control’s label and values:
,
But our API data is not in this syntax, so we can convert the countriesList array that comes from the parent component when the block is included:
let setupCountrySelect; setupCountrySelect = countriesList.map((country) => ; });
When a country is selected from the ComboboxControl, the country value changes and we filter the data accordingly:
function handleCountryChange(value) , }; fetch(`https://api-football-v1.p.rapidapi.com/v3/leagues?country=$`, options) .then((response) => response.json()) .then((response) => ) .then((leagueOptions) => ; }); setFilteredLeagueOptions(setupLeagueSelect); }) .catch((err) => console.error(err)); }
Note that I am using another three state variables to handle changes when the country selection changes:
const [filteredCountryOptions, setFilteredCountryOptions] = useState(setupCountrySelect); const [filteredLeagueOptions, setFilteredLeagueOptions] = useState(null); const [filteredSeasonOptions, setFilteredSeasonOptions] = useState(null);
What about the other settings options?
I will show the code that I used for the other settings but all it does is take normal cases into account while defining errors for special cases. For example, there will be errors in some countries and leagues because:
there are no standings for some leagues, and
some leagues have standings but they are not in a single table.
This isn’t a JavaScript or React tutorial, so I will let you handle the special cases for the API that you plan to use:
function handleLeagueChange(value) }); if (selectedLeague) ; }); setFilteredSeasonOptions(setupSeasonSelect); } } else } function handleSeasonChange(value)
Submitting the settings selections
In the last article , we made a button in the block editor that fetches fresh data from the API. There’s no more need for it now that we have settings. Well, we do need it — just not where it currently is. Instead of having it directly in the block that’s rendered in the block editor, we’re going to move it to our PanelBody component to submit the settings selections.
So, back in LeagueSettings.js:
// When countriesList is loaded, show the country combo box onInputChange=} /> )} // When filteredLeagueOptions is set through handleCountryChange, show league combobox onInputChange=} /> )} // When filteredSeasonOptions is set through handleLeagueChange, show season combobox onInputChange= } /> // When season is set through handleSeasonChange, show the "Fetch data" button >Fetch data

Images Powered by Shutterstock