Block State Manager

React-style global state management for WordPress Gutenberg blocks

WordPress 6.0+ PHP 7.4+ MIT License
GitHub Get Started

Overview

BSM (Block State Manager) is a developer library that brings modern React-style state management to WordPress Gutenberg blocks. It solves the fundamental problem of cross-block communication.

🔗

Global State

One shared state accessible by all blocks on the page

⚡

Instant Sync

No REST API calls - state updates instantly across blocks

🎣

React Hooks

Familiar useState-like API for easy adoption

🖥️

Editor + Frontend

Works in Gutenberg editor and published pages

The Problem

Gutenberg blocks are isolated by design. They can't easily share data or communicate with each other. Traditional solutions require REST API calls (slow, server load) or complex custom event systems (error-prone).

The Solution

// Block A sets state
const [count, setCount] = useBlockState('app/counter', 0);
setCount(5);

// Block B (anywhere on the page) sees the same state immediately
const [count] = useBlockState('app/counter', 0);
// count === 5 - instantly, no API calls!

Getting Started

Installation

Terminal
cd wp-content/plugins
git clone https://github.com/etuperin99-sys-sys/block-state-manager-bsm.git block-state-manager
cd block-state-manager
npm install
npm run build

Then activate the plugin in WordPress admin.

Basic Usage

Import the hook in your custom block:

your-block/edit.js
import { useBlockState } from 'bsm-store';

const Edit = () => {
    const [count, setCount] = useBlockState('myapp/counter', 0);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(c => c + 1)}>+</button>
        </div>
    );
};

Add Dependency

Ensure your block loads after BSM:

block.json
{
    "editorScript": "file:./index.js",
    "editorDependencies": ["bsm-store"]
}
That's it!

Any block using the same state key will automatically stay in sync. When one updates, all others see the change immediately.

API Reference

useBlockState(key, initialValue)

Primary hook for reading and writing state. Similar to React's useState but global.

Parameter Type Description
key string Unique identifier (use namespace/name format)
initialValue any Default value if state doesn't exist

Returns: [value, setValue, helpers]

const [user, setUser, helpers] = useBlockState('app/user', { name: '' });

// Set value
setUser({ name: 'John' });

// Functional update
setUser(prev => ({ ...prev, age: 30 }));

// Helpers
helpers.reset();        // Reset to initial value
helpers.remove();       // Delete this key
helpers.update({...});  // Partial object merge
helpers.isInitial;      // Boolean

useBlockStateValue(key, defaultValue)

Read-only hook for observing state without modifying it.

const count = useBlockStateValue('app/counter', 0);

useBlockStates(keysWithDefaults)

Read multiple state values at once.

const state = useBlockStates({
    'form/name': '',
    'form/email': '',
    'form/step': 0
});
console.log(state['form/name']);

useBlockStateDispatch()

Direct access to dispatch functions.

const { setState, setMultiple, deleteKey, resetState } = useBlockStateDispatch();

setState('app/counter', 5);
setMultiple({ 'app/a': 1, 'app/b': 2 });

Direct Store Access

import { bsmSelect, bsmDispatch, bsmSubscribe } from 'bsm-store';

bsmSelect().getState('key');
bsmDispatch().setState('key', value);
bsmSubscribe(() => console.log('State changed'));

PHP Functions

// Set initial state for frontend
bsm_set_initial_state('shop/cart', $cart_data);

// Get state value
$value = bsm_get_state('key', $default);

Examples

Shopping Cart

// Product Block
const ProductBlock = ({ product }) => {
    const [cart, setCart] = useBlockState('shop/cart', []);

    return (
        <button onClick={() => setCart(c => [...c, product])}>
            Add to Cart
        </button>
    );
};

// Cart Block - reads same state
const CartBlock = () => {
    const [cart] = useBlockState('shop/cart', []);
    const total = cart.reduce((sum, item) => sum + item.price, 0);

    return (
        <div>
            <p>{cart.length} items</p>
            <p>Total: ${total}</p>
        </div>
    );
};

Search + Results

// Search Block
const SearchBlock = () => {
    const [filters, setFilters] = useBlockState('search/filters', {
        query: '',
        category: 'all'
    });

    return (
        <input
            value={filters.query}
            onChange={e => setFilters(f => ({...f, query: e.target.value}))}
        />
    );
};

// Results Block - automatically updates when filters change
const ResultsBlock = () => {
    const filters = useBlockStateValue('search/filters', {});
    // Render filtered results...
};

Multi-Step Form

// All form step blocks share this state
const [form, setForm] = useBlockState('form/application', {
    step: 1,
    name: '',
    email: '',
    message: ''
});

// Step 1: Name input
// Step 2: Email input
// Step 3: Message input
// Summary: Shows all collected data

Gallery + Lightbox

// Gallery Block
const GalleryBlock = ({ images }) => {
    const [, setLightbox] = useBlockState('gallery/lightbox', {
        open: false,
        index: 0
    });

    return images.map((img, i) => (
        <img
            src={img.thumb}
            onClick={() => setLightbox({ open: true, index: i })}
        />
    ));
};

// Lightbox Block - separate block
const LightboxBlock = ({ images }) => {
    const [lb, setLb] = useBlockState('gallery/lightbox', {
        open: false,
        index: 0
    });

    if (!lb.open) return null;

    return (
        <div className="lightbox">
            <img src={images[lb.index].full} />
            <button onClick={() => setLb(l => ({...l, open: false}))}>
                Close
            </button>
        </div>
    );
};

Demo Blocks

BSM includes three demo blocks to demonstrate the shared state functionality:

BSM Counter

Add multiple counter blocks to a page. They all share the same count - clicking + in one updates all of them.

BSM Shared Input

An input field that broadcasts its value to all BSM Display blocks.

BSM Display

Shows text from BSM Shared Input blocks in real-time.

Try it yourself

Search for "BSM" in the Gutenberg block inserter to find the demo blocks.