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
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:
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:
{
"editorScript": "file:./index.js",
"editorDependencies": ["bsm-store"]
}
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.
Search for "BSM" in the Gutenberg block inserter to find the demo blocks.