TIL how to convert a shortcode to a WP block
POSTED ON:
Today I learned how to create a WP block that used to be a shortcode.
You can read the whole blog here: https://developer.wordpress.org/news/2023/03/converting-your-shortcodes-to-blocks/
It does use React. And I know in my job, we're using ACF-powered Blocks, so it's more traditional PHP.
Traditionally, shortcodes were a way that plugin developers could provide users the ability to add specific plugin functionality anwhere on their site. But shortcodes are not very user friendly, nor was hunting down the relevant data you needed to render the correct data for the shortcode. Converting existing shortcodes to blocks provides a much greater user experience in all aspects.
[time relative]Tuesday 07:00 UTC[/time]
Should it be a shortcode? #
- If the shortcode just does one thing and does it well, it can probably be left as a shortcode. The time shortcode above is a good example of this.
-
Shortcodes that have loads of attributes, or combinations of attributes that render information differently, make the best targets for block support.
-
If it renders complicated HTML, that will make a huge difference to the user if they can see it in the editor.
Making the conversion #
- Loading your block using the block.json
before, the author had to use a enqueue_script class.
Now, he can just use the block.json
.
The block definition allows code sharing between JavaScript, PHP, and other languages when processing block types stored as JSON, and registering blocks with the block.json metadata file provides multiple benefits on top of it.
From a performance perspective, when themes support lazy loading assets, blocks registered with block.json will have their asset enqueuing optimized out of the box. The frontend CSS and JavaScript assets listed in the style or script properties will only be enqueued when the block is present on the page, resulting in reduced page sizes.
Via the https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/
- Creating the react component to render the content
The full code is here: https://github.com/CastosHQ/Seriously-Simple-Podcasting/blob/master/src/components/CastosPlayer.js
// components/CastroPlayer.js
import {Component} from '@wordpress/element';
import classnames from "classnames";
import CastosPlayerPanels from "./CastosPlayerPanels";
class CastosPlayer extends Component {
render() {
try {
// Getting the props
const {className, episodeId, episodeImage, episodeTitle, episodeFileUrl, episodeDuration, episodeData} = this.props;
const {playerMode} = episodeData ;
const playerClassNames = classnames(
className,
'castos-player',
playerMode + '-mode',
);
return (
<div className={playerClassNames} data-episode={episodeId}>
<!-- ... removed the extra markup -->
<CastosPlayerPanels
className={className}
episodeId={episodeId}
episodeTitle={episodeTitle}
episodeFileUrl={episodeFileUrl}
episodeData={episodeData}
/>
</div>
);
} catch (error) {
console.log('Error:', error);
this.state = ({error: error});
}
}
}
export default CastosPlayer;
- Create react component to edit the block in WordPress
The full code is here: https://github.com/CastosHQ/Seriously-Simple-Podcasting/blob/master/src/components/EditPlayer.js
import {__} from '@wordpress/i18n';
import {Component} from '@wordpress/element';
import {BlockControls} from '@wordpress/block-editor';
import {Button, Toolbar} from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
import EpisodeSelector from "./EpisodeSelector";
import AudioPlayer from "./AudioPlayer";
class EditPlayer extends Component {
constructor({attributes, setAttributes, className}) {
super(...arguments);
this.episodeRef = React.createRef();
let editing = true;
if (attributes.audio_player){
editing = false;
}
const episode = {
audioPlayer: attributes.audio_player || "",
}
this.state = {
className,
editing: editing,
episode: episode,
episodes: [],
setAttributes: setAttributes
}
}
// this makes a fetch request to wordpress
componentDidMount() {
let fetchPost = 'ssp/v1/episodes';
apiFetch({path: fetchPost}).then(posts => {
let episodes = []
Object.keys(posts).map(function (key) {
let episode = {
id: posts[key].id,
title: posts[key].title.rendered
}
episodes.push(episode);
});
this.setState({
episodes: episodes,
});
});
}
// This is to show the block controls on the Block Editor
render() {
const {editing, episodes, episode, className, setAttributes} = this.state;
const switchToEditing = () => {
this.setState({editing: true});
};
const activateEpisode = () => {
const episodeId = this.episodeRef.current.value;
const fetchAudioPlayer = 'ssp/v1/audio_player?ssp_podcast_id='+episodeId;
apiFetch({path: fetchAudioPlayer}).then(response => {
const episode = {
episodeId: episodeId,
audioPlayer: response.audio_player
}
this.setState({
episode: episode,
editing: false
});
setAttributes({
id: episodeId,
audio_player: episode.audioPlayer
});
});
};
const controls = (
<BlockControls key="controls">
<Toolbar>
<Button
className="components-icon-button components-toolbar__control"
label={__('Select Podcast', 'seriously-simple-podcasting')}
onClick={switchToEditing}
icon="edit"
/>
</Toolbar>
</BlockControls>
);
if (editing) {
return (
<EpisodeSelector
className={className}
episodeRef={this.episodeRef}
episodes={episodes}
activateEpisode={activateEpisode}
/>
);
} else {
return [
controls, (
<AudioPlayer className={className} audioPlayer={episode.audioPlayer}/>
)];
}
}
}
export default EditPlayer;
- Fetching data
In the code above -- Notice the apiFetch
? More here: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/
Apparently, you can also use the core-data
course to explore.
How to replicate the get_users()
function but using JS and core-data
import {useSelect} from "@wordpress/data";
const users = useSelect( ( select ) => {
return select( 'core' ).getUsers();
}, [] );
The online course: https://learn.wordpress.org/course/using-the-wordpress-data-layer/
Related TILs
Tagged: block