Building Custom Blocks

With v3.1, we introduced a complete block building system to WP Rig. This system comes with a handful of helper commands to make creating and building blocks a breeze.

With any of the below commands, if you are using Bun, you can replace the npm command with bun

Create a block (static)

(title auto-generated from slug)

npm run block:new -- hero --title="Hero"

Create a dynamic block

(title auto-generated from slug)

npm run block:new:dynamic testimonial

List blocks

npm run block:list

Remove a block

(prompts to confirm)

npm run block:remove wprig/hero

Promote to a plugin

(exports minimal plugin skeleton)

npm run block:promote-plugin wprig/hero

Once you have created your new block, you can run all of WP Rig

npm start

or we also have block specific commands to only watch and build the blocks

npm run start:blocks

Workflow and conventions

This block system is built on the @wordpress/create-block package, so you would develop these blocks the same as those. This means you can write JSX and follow all the other block dev conventions used with that package. This does mean you need to know some React to build these blocks.

Why build blocks in your theme?

While traditionally custom Gutenberg blocks are built as a plugin, there are some specific scenarios where you may want to build theme-specific blocks. It’s particularly useful for bespoke client sites or themes with unique visual patterns, as opposed to reusable blocks that warrant plugin distribution for broader compatibility.

Another thing to consider here is that WP Rig already has a robust build process built-in. Having a single build process to build your theme and blocks means that you only need one set of dependencies, one dev server running while developing, one build to run in your CI/CD pipeline, and one directory in your project repo.

Some great examples are blocks like: mega menu blocks, custom page navigation blocks,

Block ExampleDescriptionWhy Integrate into a Theme?
Hero Banner/CTA SectionA full-width banner with editable headline, subheadline, background image/video, and overlaid CTA button/link. Supports color palettes and alignment controls, often with nested inner blocks for columns or icons.Ties directly into the theme’s header/footer patterns and responsive grid; custom parallax or overlay effects match theme-specific animations without extra plugin overhead.
Testimonial SliderA carousel displaying rotating client quotes, photos, and ratings, with options for autoplay, navigation dots, and quote sourcing from a CPT.Enhances trust-building sections in marketing themes; styling (e.g., theme-matched typography and transitions) ensures seamless integration with site-wide sliders or grids.
Pricing TableResponsive cards comparing product/service plans, with features checklists, pricing toggles (monthly/yearly), and highlight states for premium options.Common in SaaS/e-commerce themes; leverages theme’s color scheme and button styles for consistent conversion funnels, avoiding plugin bloat for simple, non-dynamic pricing.
Mega MenusSome menu experience designs merit a very specific look and feel along with markupCustom menu walkers are commonly built into themes. So if the walk is in the theme, why not the menu block as well?
FAQ AccordionCollapsible panels for questions/answers, with search/filter options and expandable states; supports rich text and icons per item.Improves UX in documentation or service themes; theme-specific animations (e.g., easing curves) and icon libraries keep it lightweight and on-brand.
Dynamic Content GridA query loop pulling from CPTs (e.g., courses, products, or blog teasers) with customizable columns, filters, and card layouts (image, title, excerpt).Essential for content-heavy themes like education sites; integrates with theme’s query vars and pagination for efficient, site-tailored displays without external dependencies.
Custom Column LayoutAn advanced columns block with mobile breakpoint controls, drag-to-swap ordering, gap spacing, and background options per column.Addresses core block limitations in responsive themes; ensures uniform spacing and breakpoints across the site via theme’s SCSS variables.

Doc Categories:

Enqueuing Scripts in WP Rig

As of v2.0, rather than conditionally enqueuing scripts in functions.php, scripts are enqueued by the PHP components that use them.

To enqueue a script, find the component that is most closely related to your script, or create a new component (don’t forget to new it in Theme.php). “Add_action” is triggered in the component’s “initialize” method, which invokes a custom method on the same component class.

The custom method ( ex. public function enqueue_[component_name]_scripts() ) can enqueue in a few different ways:

Scripts Component – wp_rig_js_files hook

WP Rig makes use of some custom hooks to make adding custom JS files to the theme a little bit easier for devs. Using a specific associative array format, the Scripts component can handle the enqueueing of your scripts for you.

In your Initialize method:

add_filter( 'wp_rig_js_files', array( $this, 'enqueue_your_new_scripts' ) );

In your custom method described above:

public function enqueue_your_new_scripts( array $js_files ): array {
	global $template;
	$is_target_page = $this->is_course_page();
        //You can use conditional logic here to control what pages your scripts enqueue on if you want
	if ( basename( $template ) == 'custom-page-template' ) {
		$js_files['wp-rig-custom-page-scripts'] = array(
			'file'   => 'custom-page-scripts.min.js',
			'footer' => true,
			'global' => false,
		);
	}
	return $js_files;
}

Here is a list of all of the possible array keys that can be used when adding scripts to the js_files array:

  • ‘file’ (file path relative to ‘assets/js’ directory) – required
  • ‘global’ (whether the file should immediately be enqueued instead of just being registered)
  • ‘loading’ (whether the file should be loaded ‘async’ or ‘defer’)
  • ‘footer’ (whether the file should be loaded in the footer)
  • ‘deps’ (array of dependencies)
  • ‘localize’ (array of variables to inject with wp_localize_scripts)

“wp_enqueue_script” invocation

wp_enqueue_script(
	'wp-rig-navigation',
	get_theme_file_uri( '/assets/js/navigation.min.js' ),
	array(),
	wp_rig()->get_asset_version( get_theme_file_path( '/assets/js/navigation.min.js' ) ),
	false
		);

print_scripts method

Sometimes, it might be handy to only enqueue specific scripts that are already preregistered in specific template parts. In this scenario, the Scripts component exposes a hand method called print_scripts().

wp_rig()->print_scripts('my-script-handle');

Modern Script Loading

WP Rig allows you to declare each script you enqueue to be loaded asynchronously or deferred. This means that most scripts enqueued in WP Rig can and should be loaded in the header. If a script needs to be loaded after the rest of the page, instead of passing true as the fifth argument to wp_enqueue_script, always pass false. After enqueuing the script, add script data using “wp_add_script_data()”. Pass the script label as the first argument, then either ‘async’ or ‘defer’ (depending on your needs), and “true” as the third and final argument.

Example

wp_enqueue_script(
	'wp-rig-navigation',
	get_theme_file_uri( '/assets/js/navigation.min.js' ),
	array(),
	wp_rig()->get_asset_version( get_theme_file_path( '/assets/js/navigation.min.js' ) ),
	false
);
wp_script_add_data( 'wp-rig-navigation', 'async', true );
wp_script_add_data( 'wp-rig-navigation', 'precache', true );

Alternatively, we can also control these values when using the hook-based enqueue approach mentioned above as the array of args accepts a ‘loading’ argument that allows for this level of control.

Doc Categories: