Skip to content

Instantly share code, notes, and snippets.

@georgestephanis
Last active March 6, 2022 02:14
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save georgestephanis/f790524891e1a6e1b457b2e91a794589 to your computer and use it in GitHub Desktop.
Save georgestephanis/f790524891e1a6e1b457b2e91a794589 to your computer and use it in GitHub Desktop.

Your First Block

To make things simple, we're going to make a semi-structured way to display a mailing address.

We're going to start with a file tree that looks like this:

address-block
  ├ blocks
  | ├ address.jsx
  | └ address.scss
  ├ address-block.php
  └ package.json

One PHP file, one style file, one javascript file, and one file for npm to run the build tools off of.

package.json

We're running a fairly clean implementation here -- not too much happening, so let's not overclutter our package.json

{
  "name": "address-block",
  "scripts": {
    "build": "parcel build blocks/address.jsx --out-dir blocks --public-url ."
  },
  "devDependencies": {
    "parcel-bundler": "^1.10.1",
    "sass": "^1.15.1"
  }
}

blocks/address.scss

For the sake of something here that we can populate later, we're going to add a simple snippet of sass code. This will make sure that new lines on our output will stay as newlines when rendered in html.

.address-block-address {
  p {
    white-space: pre-line;
  }
}

blocks/address.jsx

Now, we start building our block javascript. This is, at the moment, a very stripped down version, but that's okay. We'll add to it later.

const {
	registerBlockType
} = wp.blocks;

const {
	TextareaControl
} = wp.components;

import './address.scss'

registerBlockType( 'address-block/address', {
	title: 'Address',
	icon: 'sticky',
	category: 'common',

	attributes: {
		address: {
			type: 'string',
			default: '',
			source: 'text',
			selector: 'p',
		}
	},

	edit: function( props ) {
		return (
			<div>
				<TextareaControl
					label="Address"
					value={ props.attributes.address }
					onChange={ address => props.setAttributes( { address } ) }
				/>
			</div>
		);
	},

	save: function( props ) {
		return (
			<div className="address-block-address">
				<p>{ props.attributes.address }</p>
			</div>
		);
	}
} );

BUILD!

Okay, so let's build it, using npm install && npm run build

You should now have three new files, generated by the build script:

address-block
  └ blocks
    ├ address.js
    ├ address.map
    └ address.css

These are the files that we'll be enqueueing to WordPress.

address-block.php

Now, we finally make our main plugin file. We're going to be registering (not enqueueing) our generated style and script files -- and then registering our block type, listing the script and style as dependencies of it, so that they'll get pulled in.

This also means that if some other plugin wants to deregister our block, the scripts and styles won't be loaded. Which is good!

<?php

// Plugin name: Address Block

add_action( 'init', 'register_address_block' );
function register_address_block() {
	if ( ! function_exists( 'register_block_type' ) ) {
		return;
	}

	wp_register_style( 'address-block', plugins_url( 'blocks/address.css', __FILE__ ) );
	wp_register_script(
		'address-block',
		plugins_url( 'blocks/address.js', __FILE__ ),
		array(
			'wp-blocks',
			'wp-components',
		)
	);

	register_block_type( 'address-block/address', array(
		'editor_script' => 'address-block',
		'style' => 'address-block',
	) );
}

Congratulations!

Your first block is done. Build tools are set up, code is transpiling, all is well!

There's lots more we can do, such as translating the "Address" string in the script, adding a second line for the place name, deep linking to Google Maps to provide directions to the address, dynamically rendering the content -- but for now this is the bulk of the basics.

Not as scary as you thought, right?

@topher1kenobe
Copy link

I've done this. Now I have a folder with lots of this in it. Now what?

@cagrimmett
Copy link

Heads up for anyone using this in 2022 and beyond: parcel-bundler has been deprecated. New dependency:

"devDependencies": {
   "parcel": "^2.0.0",
    "sass": "^1.15.1"
  }

Also, the syntax for Parcel's output directory has changed. --out-dir has been replaced with --dist-dir, so you'll need to update the build command accordingly.

@cagrimmett
Copy link

This was very helpful, @georgestephanis! Thank you. I used it to create my first blocks: https://github.com/cagrimmett/meta-dates-blocks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment