This repository demonstrates conditional code execution patterns in the WordPress Block Editor, with complete E2E tests validating each approach. All examples are executable, testable, and based on real-world patterns from WordPress core.
This project provides working examples and tests for conditionally executing code in different WordPress Block Editor contexts:
- β
Specific Post Types - Only run code for certain post types (pages, products, etc.)
- Component:
SpecificPostTypeComponent.js- Renders only for 'page' and 'product' post types
- Component:
- β
Exclude Post Types - Prevent code from running on specific post types
- Component:
ExcludePostTypesComponent.js- Excludes 'attachment' and 'wp_block' post types
- Component:
- β
Viewable Post Types - Only execute for public-facing content (excludes Site Editor)
- Component:
ViewablePostTypeComponent.js- UsespostTypeObject.viewableto exclude Site Editor contexts
- Component:
- β
Site Editor Context - Detect and exclude Site Editor contexts specifically
- Component:
SiteEditorContextComponent.js- Only renders in Post Editor by checking viewable post types
- Component:
- β
Post Status - Conditional execution based on draft/publish status
- Component:
PostStatusComponent.js- Only renders for draft posts (including auto-draft)
- Component:
- β
Page Templates - Detect when specific page templates are active
- Component:
PageTemplateComponent.js- Only renders when 'full-width' template is selected on pages
- Component:
- β
User Capabilities - Restrict features based on user permissions
- Component:
UserCapabilityComponent.js- Only renders for users who can publish posts
- Component:
- β
Exclude Design Post Types - Skip Site Editor contexts (templates, navigation, etc.)
- Component:
ExcludeDesignPostTypesComponent.js- Excludes wp_template, wp_template_part, wp_block, and wp_navigation
- Component:
- β
Editor Mode - Detect current editor mode (visual vs code)
- Component:
EditorModeComponent.js- Only renders when editor is in visual mode
- Component:
- β
Selected Block Type - Detect the currently selected block
- Component:
SelectedBlockTypeComponent.js- Only renders when a paragraph block is selected
- Component:
- β
Sidebar State - Check if document settings sidebar is open
- Component:
SidebarStateComponent.js- Only renders when the sidebar is open
- Component:
- β
Combined Conditions - Multiple conditions working together
- Component:
CombinedConditionsComponent.js- Requires post type = 'page', status = 'draft', and user has edit permissions
- Component:
- β
Block Editor Detection -
cre_is_block_editor()- Check if block editor is active - β
Post Type Filtering -
cre_is_post_type()- Server-side post type checks (single or array) - β
Post Type Exclusion -
cre_is_not_post_type()- Exclude specific post types - β
Site Editor Exclusion -
cre_is_site_editor()- Detect and exclude Site Editor before JS loads - β
Post Edit Screen -
cre_is_post_edit_screen()- Check if on post edit screen - β
Viewable Post Types -
cre_is_viewable_post_type()- Check if post type is publicly queryable - β
Design Post Types -
cre_is_design_post_type()- Check if post type is a Site Editor design type - β
User Capabilities - Multiple functions for permission verification:
cre_user_can_publish_posts()- Check if user can publish postscre_user_can_edit_posts()- Check if user can edit postscre_user_can_edit_pages()- Check if user can edit pages
- β
Post Status -
cre_is_post_status()- Check if post has specific status - β
Template Detection -
cre_is_template()- Check if post uses specific template - β
Combined Conditions -
cre_check_conditions()- Check multiple conditions with AND logic - β
Any Condition -
cre_check_any_condition()- Check if any condition passes with OR logic - β Performance Optimization - Prevent unnecessary script loading by checking conditions server-side
When building WordPress Block Editor plugins, you often need to:
- Register SlotFills that only appear in the Post Editor or Site Editor
- Add editor commands available only in certain contexts
- Display custom UI based on post status or user capabilities
- Prevent functionality from running where it doesn't belong
This repo shows you how to do it correctly, with zero mocks and real tests.
- Node.js 18+
- PHP 7.4+ (for running tests locally)
- Composer (for PHP dependencies)
- Docker (for @wordpress/env)
- npm or yarn
git clone https://github.com/juanma-wp/detect-site-post-editor.git
cd detect-site-post-editor
npm run setupThis single command will:
- Install Node.js dependencies
- Install PHP dependencies (Composer)
- Build the plugin
- Start WordPress environment
-
Clone the repository:
git clone https://github.com/juanma-wp/detect-site-post-editor.git cd detect-site-post-editor -
Install Node.js dependencies:
npm install
-
Install PHP dependencies:
composer install
-
Build the plugin:
npm run build
-
Start the WordPress environment:
npm run wp-env:start
This will start WordPress at http://localhost:8888
- Username: admin
- Password: password
-
Run the tests:
# Run E2E tests npm run test:e2e # Run PHP unit tests npm run test:php # Run all tests npm test
.
βββ plugin/ # WordPress plugin
β βββ plugin.php # Main plugin file
β βββ includes/
β β βββ server-side-detection.php # Server-side detection functions
β βββ resources/js/
β βββ index.js # Plugin entry point
β βββ components/ # React components demonstrating patterns
β β βββ SpecificPostTypeComponent.js
β β βββ ExcludePostTypesComponent.js
β β βββ ViewablePostTypeComponent.js
β β βββ SiteEditorContextComponent.js
β β βββ PostStatusComponent.js
β β βββ PageTemplateComponent.js
β β βββ UserCapabilityComponent.js
β β βββ ExcludeDesignPostTypesComponent.js
β β βββ EditorModeComponent.js
β β βββ SelectedBlockTypeComponent.js
β β βββ SidebarStateComponent.js
β β βββ CombinedConditionsComponent.js
β βββ server-conditionals/ # Server-side conditional examples
β βββ editor-only.js
β βββ page-only.js
β βββ publisher-only.js
β βββ no-site-editor.js
β βββ post-edit-only.js
βββ tests/
β βββ e2e/ # Playwright E2E tests
β β βββ utils.js # Test utilities
β β βββ specific-post-type.spec.js
β β βββ exclude-post-types.spec.js
β β βββ viewable-post-type.spec.js
β β βββ site-editor-context.spec.js
β β βββ post-status.spec.js
β β βββ page-template.spec.js
β β βββ user-capability.spec.js
β β βββ exclude-design-post-types.spec.js
β β βββ editor-mode.spec.js
β β βββ selected-block-type.spec.js
β β βββ sidebar-state.spec.js
β β βββ combined-conditions.spec.js
β βββ php/ # PHP unit tests
β βββ bootstrap.php
β βββ ServerSideDetectionTest.php
βββ .wp-env.json # WordPress environment config
βββ playwright.config.js # Playwright configuration
βββ phpunit.xml.dist # PHPUnit configuration
βββ package.json # Dependencies and scripts
This project uses End-to-End (E2E) tests with Playwright because:
- β No Mocks - Tests run against a real WordPress instance
- β Real Behavior - Validates actual editor store data and React rendering
- β WordPress Core Pattern - Same approach used by WordPress/Gutenberg
- β Confidence - Proves the patterns work in production-like environments
# Run all tests
npm run test:e2e
# Run tests in headed mode (see the browser)
npm run test:e2e:headed
# Debug a specific test
npm run test:e2e:debugEach test file validates specific conditional rendering patterns:
| Test File | What It Verifies |
|---|---|
specific-post-type.spec.js |
Components render only for allowed post types (page, product) |
exclude-post-types.spec.js |
Components exclude specific post types (attachment, wp_block) |
viewable-post-type.spec.js |
Components exclude Site Editor contexts |
site-editor-context.spec.js |
Site Editor context detection works correctly (excludes templates, navigation, etc.) |
post-status.spec.js |
Components respect draft/publish status |
page-template.spec.js |
Template-specific rendering works (full-width template) |
user-capability.spec.js |
Components render based on user permissions (publish_posts capability) |
exclude-design-post-types.spec.js |
Design post types are properly excluded (wp_template, wp_template_part, wp_block, wp_navigation) |
editor-mode.spec.js |
Components render based on editor mode (visual vs code) |
selected-block-type.spec.js |
Components detect currently selected block type (paragraph block) |
sidebar-state.spec.js |
Components render based on sidebar open/closed state |
combined-conditions.spec.js |
Multiple conditions work together (page + draft + can edit) |
# Start WordPress environment
npm run wp-env:start
# Start development build (watch mode)
npm run start
# Visit http://localhost:8888/wp-admin
# Login with admin/password- Log into WordPress (http://localhost:8888/wp-admin)
- Create a new Post or Page
- Open the Document Settings sidebar (top right)
- Look for the "Conditional Rendering Examples" panel
- Components will appear/disappear based on the current context
npm run wp-env:stopnpm run wp-env:destroyimport { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
const MyComponent = () => {
const { postTypeName } = useSelect((select) => ({
postTypeName: select(editorStore).getCurrentPostType(),
}), []);
const allowedPostTypes = ['page', 'product'];
if (!allowedPostTypes.includes(postTypeName)) {
return null;
}
return <div>Only visible for pages and products!</div>;
};import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
const MyComponent = () => {
const { isViewable } = useSelect((select) => {
const postType = select(editorStore).getCurrentPostType();
const postTypeObject = select(coreStore).getPostType(postType);
return {
isViewable: postTypeObject?.viewable,
};
}, []);
if (!isViewable) {
return null; // Excludes Site Editor
}
return <div>Only in Post Editor!</div>;
};import { useSelect } from '@wordpress/data';
import { store as editPostStore } from '@wordpress/edit-post';
const MyComponent = () => {
const { editorMode } = useSelect((select) => ({
editorMode: select(editPostStore).getEditorMode(),
}), []);
// Only show in visual editor mode
if (editorMode !== 'visual') {
return null;
}
return <div>Only visible in visual mode!</div>;
};import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
const MyComponent = () => {
const selectedBlockName = useSelect((select) => {
const selectedBlock = select(blockEditorStore).getSelectedBlock();
return selectedBlock?.name;
}, []);
// Only show when a paragraph block is selected
if (selectedBlockName !== 'core/paragraph') {
return null;
}
return <div>Paragraph block selected!</div>;
};import { useSelect } from '@wordpress/data';
import { store as editPostStore } from '@wordpress/edit-post';
const MyComponent = () => {
const isSidebarOpened = useSelect((select) => {
return select(editPostStore)?.isEditorSidebarOpened();
}, []);
// Only show when sidebar is open
if (!isSidebarOpened) {
return null;
}
return <div>Sidebar is open!</div>;
};const MyComponent = () => {
const { postTypeName, postStatus, canEdit } = useSelect((select) => {
const postType = select(editorStore).getCurrentPostType();
return {
postTypeName: postType,
postStatus: select(editorStore).getCurrentPostAttribute('status'),
canEdit: select(coreStore).canUser('update', postType),
};
}, []);
// Only run for draft pages that the user can edit
if (postTypeName !== 'page' || postStatus !== 'draft' || !canEdit) {
return null;
}
return <div>Draft page editor only!</div>;
};function my_plugin_enqueue_page_script() {
// Only load for page post type
if ( cre_is_post_type( 'page' ) ) {
wp_enqueue_script(
'my-page-script',
plugins_url( 'page-script.js', __FILE__ ),
array( 'wp-blocks', 'wp-element' ),
'1.0.0'
);
}
}
add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_page_script' );function my_plugin_enqueue_post_editor_only() {
// Load everywhere except Site Editor
if ( cre_is_block_editor() && ! cre_is_site_editor() ) {
wp_enqueue_script(
'my-post-editor-script',
plugins_url( 'post-editor.js', __FILE__ ),
array( 'wp-blocks' ),
'1.0.0'
);
}
}
add_action( 'enqueue_block_editor_assets', 'my_plugin_enqueue_post_editor_only' );function my_plugin_enqueue_for_publishers() {
// Only load for users who can publish posts
if ( cre_user_can_publish_posts() && cre_is_block_editor() ) {
wp_enqueue_script(
'my-publisher-features',
plugins_url( 'publisher.js', __FILE__ ),
array( 'wp-editor' ),
'1.0.0'
);
}
}
add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_for_publishers' );function my_plugin_enqueue_draft_pages() {
// Only load for draft pages
$conditions = array(
fn() => cre_is_post_type( 'page' ),
fn() => cre_is_post_status( array( 'draft', 'auto-draft' ) ),
fn() => cre_user_can_edit_pages(),
);
if ( cre_check_conditions( $conditions ) ) {
wp_enqueue_script(
'my-draft-page-script',
plugins_url( 'draft-page.js', __FILE__ ),
array( 'wp-editor' ),
'1.0.0'
);
}
}
add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_draft_pages' );function my_plugin_enqueue_content_only() {
// Don't load in Site Editor design contexts
if ( ! cre_is_design_post_type() ) {
wp_enqueue_script(
'my-content-script',
plugins_url( 'content-only.js', __FILE__ ),
array( 'wp-blocks' ),
'1.0.0'
);
}
}
add_action( 'enqueue_block_editor_assets', 'my_plugin_enqueue_content_only' );Found a bug or want to add more examples? Contributions are welcome!
- Fork the repository
- Create a feature branch
- Add tests for new patterns
- Submit a pull request
MIT
All patterns are based on real-world usage in WordPress/Gutenberg core.
Questions? Open an issue or reach out on Twitter.