diff --git a/docs/manifest.json b/docs/manifest.json
index b483449872cc7..e4eba19d99fa2 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -497,6 +497,30 @@
"markdown_source": "../docs/reference-guides/interactivity-api/README.md",
"parent": "reference-guides"
},
+ {
+ "title": "Core Concepts",
+ "slug": "core-concepts",
+ "markdown_source": "../docs/reference-guides/interactivity-api/core-concepts/README.md",
+ "parent": "interactivity-api"
+ },
+ {
+ "title": "The Reactive and Declarative mindset",
+ "slug": "the-reactive-and-declarative-mindset",
+ "markdown_source": "../docs/reference-guides/interactivity-api/core-concepts/the-reactive-and-declarative-mindset.md",
+ "parent": "core-concepts"
+ },
+ {
+ "title": "Understanding global state, local context and derived state",
+ "slug": "undestanding-global-state-local-context-and-derived-state",
+ "markdown_source": "../docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md",
+ "parent": "core-concepts"
+ },
+ {
+ "title": "Server-side rendering: Processing directives on the server",
+ "slug": "server-side-rendering",
+ "markdown_source": "../docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md",
+ "parent": "core-concepts"
+ },
{
"title": "Quick start guide",
"slug": "iapi-quick-start-guide",
diff --git a/docs/reference-guides/interactivity-api/core-concepts/README.md b/docs/reference-guides/interactivity-api/core-concepts/README.md
new file mode 100644
index 0000000000000..ef59fb6075f92
--- /dev/null
+++ b/docs/reference-guides/interactivity-api/core-concepts/README.md
@@ -0,0 +1,9 @@
+# Core Concepts
+
+This section provides some guides on important concepts and mental models related to Interactivity API development. Use the following links to learn more:
+
+1. **[The Reactive and Declarative mindset](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/the-reactive-and-declarative-mindset):** This guide covers core concepts of reactivity and declarativeness, providing a foundation for effective use of the Interactivity API.
+
+2. **[Understanding global state, local context and derived state](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state):** The guide explains how to effectively use global state, local context, and derived state within the Interactivity API emphasizing the importance of choosing the appropriate state management technique based on the scope and requirements of your data.
+
+3. **[Server-side rendering: Processing directives on the server](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/server-side-rendering):** The Interactivity API allows WordPress to use server-side rendering to create interactive and state-aware HTML, smoothly connected with client-side features while maintaining performance and SEO benefits.
diff --git a/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md b/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md
new file mode 100644
index 0000000000000..2032d12d5670b
--- /dev/null
+++ b/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md
@@ -0,0 +1,491 @@
+# Server-side rendering: Processing directives on the server
+
+WordPress has always been built on the foundation of server-side rendering. Traditionally, when a user requests a WordPress page, the server processes the PHP code, queries the database, and generates the HTML markup that is sent to the browser.
+
+In recent years, modern JavaScript frameworks like Vue, React, or Svelte have revolutionized the way we build web applications. These frameworks provide reactive and declarative programming models that enable developers to create dynamic, interactive user interfaces with ease.
+
+However, when it comes to server-side rendering, these frameworks require a JavaScript-based server, such as NodeJS, to execute their code and generate the initial HTML. This means that PHP-based servers, like WordPress, cannot directly utilize these frameworks without sacrificing their native PHP rendering capabilities. This limitation poses a challenge for WordPress developers who want to leverage the power of reactive and declarative programming while still benefiting from WordPress's traditional server-side rendering strengths. The Interactivity API bridges this gap by bringing [reactive and declarative programming principles](./the-reactive-and-declarative-mindset.md) to WordPress without compromising its server-side rendering foundation.
+
+In this guide, we'll explore how the Interactivity API processes directives on the server, enabling WordPress to deliver interactive, state-aware HTML from the initial page load, while setting the stage for seamless client-side interactivity.
+
+## Processing the directives on the server
+
+The Interactivity API's Server Directive Processing capabilities enable WordPress to generate the initial HTML with the correct interactive state, providing a faster initial render. After the initial server-side render, the Interactivity API's client-side JavaScript takes over, enabling dynamic updates and interactions without requiring full page reloads. This approach combines the best of both worlds: the SEO and performance benefits of traditional WordPress server-side rendering, and the dynamic, reactive user interfaces offered by modern JavaScript frameworks.
+
+To understand how the Server Directive Processing works, let's start with an example where a list of fruits is rendered using the `data-wp-each` directive.
+
+The following are the necessary steps to ensure that the directives are correctly processed by the Server Directive Processing of the Interactivity API during the server-side rendering of WordPress.
+
+- **1. Mark the block as interactive**
+
+ First, to enable the server processing of the interactive block's directives, you must add `supports.interactivity` to the `block.json`:
+
+ ```json
+ {
+ "supports": {
+ "interactivity": true
+ }
+ }
+ ```
+
+- **2. Initialize the global state or local context**
+
+ Then, you must initialize either the global state or the local context that will be used during the server-side rendering of the page.
+
+ If you are using global state, you must use the `wp_interactivity_state` function:
+
+ ```php
+ wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => array( 'Apple', 'Banana', 'Cherry' )
+ ));
+ ```
+
+ If you are using local context, the initial values are defined with the `data-wp-context` directive itself, either by:
+
+ - Adding it directly to the HTML.
+
+ ```html
+
+ ```
+
+- **3. Define the interactive elements using directives**
+
+ Next, you need to add the necessary directives to the HTML markup.
+
+ ```html
+
+
+
+
+
+ ```
+
+ In this example:
+
+ - The `data-wp-interactive` directive activates the interactivity for the DOM element and its children.
+ - The `data-wp-each` directive is used to render a list of elements. The directive can be used in `` tags, with the value being a reference path to an array stored in the global state or the local context.
+ - The `data-wp-text` directive sets the inner text of an HTML element. Here, it points to `context.item`, which is where the `data-wp-each` directive stores each item of the array.
+
+ The exact same directives can also be used when using local context instead of global state. The only difference is that `data-wp-each` points to `context.fruits` instead of `state.fruits`:
+
+ ```html
+
+
+
+
+
+ ```
+
+That's it! Once you've set up your interctive block with `supports.interactivity`, initialized your global state or local context, and added the directives to the HTML markup, the Interactivity API will take care of the rest. There's no additional code required from the developer to process these directives on the server side.
+
+Behind the scenes, WordPress uses the `wp_interactivity_process_directives` function to find and process the directives in the HTML markup of your block. This function uses the HTML API to make the necessary changes to the markup, based on the found directives and the initial global state and/or local context.
+
+As a result, the HTML markup sent to the browser is already in its final form, with all directives correctly processed. This means that when the page first loads in the browser, it already contains the correct initial state of all interactive elements, without needing any JavaScript to modify it.
+
+This is how the final HTML markup of the fruit list example would look like (directives omitted):
+
+```html
+
+
Apple
+
Banana
+
Cherry
+
+```
+
+As you can see, the `data-wp-each` directive has generated a `
` element for each fruit in the array, and the `data-wp-text` directive has been processed, populating each `
` with the correct fruit name.
+
+## Manipulating the global state and local context in the client
+
+One of the key strengths of the Interactivity API is how it bridges the gap between server-side rendering and client-side interactivity. To do so, the global state and local context initialized on the server are also serialized and made available to the Interactivity API stores in the client, allowing the application to continue functioning and manipulating the DOM dynamically.
+
+Let's extend this example to include a button that the user can click to add a new fruit to the list:
+
+```html
+
+```
+
+This new button has a `data-wp-on-async--click` directive that references `actions.addMango`, which is defined in our JavaScript store:
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ actions: {
+ addMango() {
+ state.fruits.push( 'Mango' );
+ },
+ },
+} );
+```
+
+The same example would also work if you were using local context:
+
+```javascript
+store( 'myFruitPlugin', {
+ actions: {
+ addMango() {
+ const context = getContext();
+ context.fruits.push( 'Mango' );
+ },
+ },
+} );
+```
+
+Now, when the user clicks the "Add Mango" button:
+
+1. The `addMango` action is triggered.
+2. The `'Mango'` item is added to the `state.fruits` (or `context.fruits`) array.
+3. The Interactivity API automatically updates the DOM, adding a new `
` element for the new fruit.
+
+```html
+
+
Apple
+
Banana
+
Cherry
+
Mango
+
+```
+
+Remember: initializing the state on the client is not necessary when it has already been done on the server.
+
+```javascript
+store( 'myFruitPlugin', {
+ state: {
+ fruits: [ 'Apple', 'Banana', 'Cherry' ], // This is not necessary!
+ },
+} );
+```
+
+## Initializing the derived state in the server
+
+The derived state, regardless of whether it derives from the global state, local context, or both, can also be processed on the server by the Server Directive Processing.
+
+_Please, visit the [Understanding global state, local context and derived state](./undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how derived state works in the Interactivity API._
+
+### Derived state that can be defined statically
+
+Let's imagine adding a button that can delete all fruits:
+
+```html
+
+```
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ actions: {
+ // ...
+ deleteFruits() {
+ state.fruits = [];
+ },
+ },
+} );
+```
+
+Now, let's display a special message when there is no fruit. To do this, let's use a `data-wp-bind--hidden` directive that references a derived state called `state.hasFruits` to show/hide the message.
+
+```html
+
+
+
+
+
+
+
No fruits, sorry!
+
+```
+
+The derived state `state.hasFruits` is defined on the client using a getter:
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ state: {
+ get hasFruits() {
+ return state.fruits.length > 0;
+ },
+ },
+ // ...
+} );
+```
+
+Up to this point, everything is fine in the client, and when we press the "Delete all fruits" button, the "No fruits, sorry!" message will be displayed. The problem is that since `state.hasFruits` is not defined on the server, the `hidden` attribute will not be part of the initial HTML, which means it will also be showing the message until JavaScript loads, causing not only confusion to the visitor, but also a layout shift when JavaScript finally loads and the message is hidden.
+
+To fix this, you must define the initial value of the derived state in the server using `wp_interactivity_state`.
+
+- When the initial value is known and static, it can be defined directly:
+
+ ```php
+ wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => array( 'Apple', 'Banana', 'Cherry' ),
+ 'hasFruits' => true
+ ));
+ ```
+
+- Or it can be defined by doing the necessary computations:
+
+ ```php
+ $fruits = array( 'Apple', 'Banana', 'Cherry' );
+ $hasFruits = count( $fruits ) > 0;
+
+ wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => $fruits,
+ 'hasFruits' => $hasFruits,
+ ));
+ ```
+
+Regardless of the approach, the key point is that the initial value of `state.hasFruits` is now defined on the server. This allows the Server Directive Processing to handle the `data-wp-bind--hidden` directive and modify the HTML markup, adding the `hidden` attribute when needed.
+
+### Derived state that needs to be defined dynamically
+
+In most cases, the initial derived state can be defined statically, as in the previous example. But sometimes, the value depends on dynamic values that also change in the server, and the derived logic needs to be replicated in PHP.
+
+To see an example of this, let's continue by adding a shopping cart emoji (🛒) for each fruit, depending on whether it is on a shopping list or not.
+
+First, let's add an array that represents the shopping list. _Remember that even though these arrays are static for simplicity sake, usually you will work with dynamic information, for example, information coming from the database._
+
+```php
+wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => array( 'Apple', 'Banana', 'Cherry' ),
+ 'shoppingList' => array( 'Apple', 'Cherry' ),
+));
+```
+
+Now, let's add a derived state on the client that checks if each fruit is on the shopping list or not and returns the emoji.
+
+```javascript
+store( 'myFruitPlugin', {
+ state: {
+ get onShoppingList() {
+ const context = getContext();
+ return state.shoppingList.includes( context.item ) ? '🛒' : '';
+ },
+ },
+ // ...
+} );
+```
+
+And let's use that derived state to show the appropriate emoji for each fruit.
+
+```html
+
+
+
+
+
+
+
+
+```
+
+Again, up to this point, everything is fine on the client side and the visitor will see the correct emoji displayed for the fruits they have on their shopping list. However, since `state.onShoppingList` is not defined on the server, the emoji will not be part of the initial HTML, and it will not be shown until JavaScript loads.
+
+Let's fix that by adding the initial derived state using `wp_interactivity_state`. Remember that this time, the value depends on `context.item` that comes from the `data-wp-each` directive, which makes the derived value dynamic, so let's replicate the JavaScript logic in PHP:
+
+```php
+wp_interactivity_state( 'myFruitPlugin', array(
+ // ...
+ 'onShoppingList' => function() {
+ $state = wp_interactivity_state();
+ $context = wp_interactivity_get_context();
+ return in_array( $context['item'], $state['shoppingList'] ) ? '🛒' : '';
+ }
+));
+```
+
+That's it! Now, our server can compute the derived state and know which fruits are on the shopping list and which are not. This allows the Server Directive Processing to populate the initial HTML with the correct values, ensuring that the user sees the correct information immediately, even before the JavaScript runtime loads.
+
+## Serializing other processed values to be consumed on the client
+
+The `wp_interactivity_state` function is also valuable for sending processed values from the server to the client so they can be consumed later on. This feature is useful in many situations, such as managing translations.
+
+Let's add translations to our example to see how this would work.
+
+```php
+ array( __( 'Apple' ), __( 'Banana' ), __( 'Cherry' ) ),
+ 'shoppingList' => array( __( 'Apple' ), __( 'Cherry' ) ),
+ // ...
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+That's it! Since the Interactivity API works in PHP, you can add translations directly to the global state, the local context and the HTML markup.
+
+But wait, what happens with our `addMango` action? Remember, this action is defined only on JavaScript:
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ actions: {
+ addMango() {
+ state.fruits.push( 'Mango' ); // Not translated!
+ },
+ },
+} );
+```
+
+To fix this issue, you can use the `wp_interactivity_state` function to serialize the translated mango string and then access that value in your action.
+
+```php
+wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => array( __( 'Apple' ), __( 'Banana' ), __( 'Cherry' ) ),
+ 'mango' => __( 'Mango' ),
+));
+```
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ actions: {
+ addMango() {
+ // `state.mango` contains the 'Mango' string already translated.
+ state.fruits.push( state.mango );
+ },
+ },
+} );
+```
+
+Take into account that if your application is more dynamic, you could serialize an array with all the fruit translations and just work with _fruit keywords_ in your actions. For example:
+
+```php
+wp_interactivity_state( 'myFruitPlugin', array(
+ 'fruits' => array( 'apple', 'banana', 'cherry' ),
+ 'translatedFruits' => array(
+ 'apple' => __( 'Apple' ),
+ 'banana' => __( 'Banana' ),
+ 'cherry' => __( 'Cherry' ),
+ 'mango' => __( 'Mango' ),
+ ),
+ 'translatedFruit' => function() {
+ $state = wp_interactivity_state();
+ $context = wp_interactivity_get_context();
+ return $state['translatedFruits'][ $context['item'] ];
+ }
+));
+```
+
+```javascript
+const { state } = store( 'myFruitPlugin', {
+ state: {
+ get translatedFruit() {
+ const context = getContext();
+ return state.translatedFruits[ context.item ];
+ }
+ }
+ actions: {
+ addMango() {
+ state.fruits.push( 'mango' );
+ },
+ },
+} );
+```
+
+```html
+
+
+
+```
+
+Serializing information from the server can also be useful in other scenarios, such as passing Ajax/REST-API URLs and nonces.
+
+```php
+wp_interactivity_state( 'myPlugin', array(
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'myPlugin_nonce' ),
+));
+```
+
+```js
+const { state } = store( 'myPlugin', {
+ actions: {
+ *doSomething() {
+ const formData = new FormData();
+ formData.append( 'action', 'do_something' );
+ formData.append( '_ajax_nonce', state.nonce );
+
+ const data = yield fetch( state.ajaxUrl, {
+ method: 'POST',
+ body: formData,
+ } ).then( ( response ) => response.json() );
+
+ console.log( 'Server data', data );
+ },
+ },
+} );
+```
+
+## Processing directives in classic themes
+
+Server Directive Processing happens automatically in your interactive blocks as soon as you add `supports.interactivity` to your `block.json` file. But what about classic themes?
+
+Classic themes can also use the Interactivity API, and if they want to take advantage of the Server Directive Processing (which they should), they can do so through the `wp_interactivity_process_directives` function. This function receives the HTML markup with unprocessed directives and returns the HTML markup modified according to the initial values of the global state, local context, and derived state.
+
+```php
+// Initializes the global and derived state…
+wp_interactivity_state( '...', /* ... */ );
+
+// The interactive HTML markup that contains the directives.
+$html = '
...
';
+
+// Processes the directives so they are ready to be sent to the client.
+$processed_html = wp_interactivity_process_directives( $html );
+```
+
+That's it! There's nothing else you need to do.
+
+If you want to use `wp_interactivity_process_directives` in a template file, you can use `ob_start` and `ob_get_clean` to capture the HTML output and process it before rendering.
+
+```php
+
+
+
+ ...
+
+
+
+
+
+
this is inactive
+
+
+
+```
+
+As you can see, for each condition, you have to use JavaScript to modify everything in the DOM that has changed, taking into account the previous state.
+
+### The declarative approach
+
+The declarative approach simplifies the process by focusing on _what_ should happen. The UI updates automatically in response to changes in state. Here is a similar example using the Interactivity API's declarative approach:
+
+```html
+
+
+
+
+ this is inactive
+
+
+```
+
+```js
+import { store } from '@wordpress/interactivity';
+
+const { state } = store( 'myInteractivePlugin', {
+ state: {
+ isVisible: false,
+ isActive: false,
+ get visibilityText() {
+ return state.isVisible ? 'hide' : 'show';
+ },
+ get activationText() {
+ return state.isActive ? 'deactivate' : 'activate';
+ },
+ get paragraphText() {
+ return state.isActive ? 'this is active' : 'this is inactive';
+ },
+ },
+ actions: {
+ toggleVisibility() {
+ state.isVisible = ! state.isVisible;
+ if ( ! state.isVisible ) state.isActive = false;
+ },
+ toggleActivation() {
+ state.isActive = ! state.isActive;
+ },
+ },
+} );
+```
+
+In this declarative example, the UI automatically updates based on the current state. All you have to do as developers is to declare the necessary state, any derived state, the actions that modify the state, and which parts of the DOM depend on which parts of the state. The framework takes care of making all the necessary updates to the DOM so that it is always in sync with the current state. The logic remains simple and maintainable regardless of the number of elements controlled by the framework.
+
+### Can you spot the bug?
+
+In the imperative example, a bug has been intentionally introduced for didactical purposes. Can you find it? It's not easy!
+
+
+Show me the answer!
+
+In the case that the Show button is pressed first, then the Activate button, and finally the Hide button, it doesn't add the `inactive` class using `statusParagraph.classList.add('inactive');`. Therefore, the next time the user presses Show, the paragraph will not appear in red.
+
+
+
+These types of bugs are very common in imperative code because you have to manually control all the conditions. On the other hand, they do not exist in declarative code because the framework takes care of updating the DOM and never forgets about anything.
+
+### Benefits of the declarative approach
+
+As demonstrated, the imperative approach requires detailed steps and direct manipulation of the DOM, which can quickly become complex and hard to maintain as the interactivity complexity grows. The more possible states and elements there are, the more conditional logic needs to be added, making the code exponentially more complicated. The declarative approach, on the other hand, simplifies the process by managing the state and letting the framework handle the DOM updates. This leads to more readable, maintainable, and scalable code.
+
+## Reactivity
+
+The Interactivity API is a declarative framework thanks to its leverage of reactivity. In a reactive system, changes to the data automatically trigger updates in the user interface, ensuring that the view always reflects the current state of the application.
+
+### How reactivity works
+
+The Interactivity API uses a fine-grained reactivity system. Here's how it works:
+
+1. **Reactive State**: In the Interactivity API, both the global state and the local context are reactive. This means that when either of these data sources changes, any parts of the UI that depend on them will automatically update.
+
+ - **Global state**: This is global data that can be accessed throughout your interactive blocks.
+ - **Local context**: This is local data that is specific to a particular element and its children.
+ - **Derived State**: In addition to basic state properties, you can define computed properties that automatically update when their dependencies change.
+
+ _Please, visit the [Understanding global state, local context and derived state](./undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how to work with the different types of reactive state in the Interactivity API._
+
+2. **Actions**: These are functions, usually triggered by event handlers, that mutate the global state or local context.
+
+3. **Reactive Bindings**: HTML elements are bound to reactive state values using special attributes like `data-wp-bind`, `data-wp-text`, or `data-wp-class`.
+
+4. **Automatic Updates**: When the actions mutate the global state or local context, the Interactivity API automatically updates all the parts of the DOM that depend on that state (either directly or through the derived state).
+
+Let's break down these concepts by reviewing the previous example:
+
+```javascript
+const { state } = store( 'myInteractivePlugin', {
+ state: {
+ isVisible: false,
+ isActive: false,
+ get visibilityText() {
+ return state.isVisible ? 'hide' : 'show';
+ },
+ // ... other derived state
+ },
+ actions: {
+ toggleVisibility() {
+ state.isVisible = ! state.isVisible;
+ },
+ // ... other actions
+ },
+} );
+```
+
+In this code:
+
+- `isVisible` and `isActive` are basic state properties.
+- `visibilityText` is a derived state that automatically updates when `isVisible` changes.
+- `toggleVisibility` is an action that modifies the state.
+
+The HTML bindings look like this:
+
+```html
+
+```
+
+Here's how reactivity works in practice:
+
+1. When the button is clicked, it triggers the `toggleVisibility` action.
+2. This action updates `state.isVisible`.
+3. The Interactivity API detects this change and automatically:
+ - Updates the button's text content (because of `data-wp-text="state.visibilityText"`).
+ - Changes the `aria-expanded` attribute (due to `data-wp-bind--aria-expanded="state.isVisible"`).
+ - Updates any other parts of the DOM that depend on `isVisible` or `visibilityText`.
+
+### Mutability vs immutability
+
+Unlike many other reactive frameworks, **the Interactivity API does not require the use of immutability** when updating the global state or the local context. You can directly mutate objects and arrays, and the reactivity system will still work as expected. This can lead to more intuitive and straightforward code in many cases.
+
+For example, you can push a new item to an array like this:
+
+```javascript
+const { state } = store( 'myArrayPlugin', {
+ state: {
+ list: [ 'item 1', 'item 2' ],
+ },
+ actions: {
+ addItem() {
+ // Right:
+ state.list.push( 'new item' );
+
+ // Wrong:
+ state.list = [ ...state.list, 'new item' ]; // Don't do this!
+ },
+ },
+} );
+```
+
+There's no need to create a new array or use the spread operator as you might in other frameworks. The Interactivity API will detect this change and update any parts of the UI that depend on `state.list`.
+
+### Reactive side effects
+
+In addition to automatically updating the UI, the Interactivity API allows you to perform side effects when reactive data changes using directives like `data-wp-watch`. Side effects are useful for tasks like logging, making API calls, or updating other parts of your application that aren't directly tied to the UI.
+
+Here's an example of how you might use `data-wp-watch`:
+
+```html
+
+
Counter:
+
+
+```
+
+```javascript
+store( 'myCounterPlugin', {
+ actions: {
+ increment() {
+ const context = getContext();
+ context.counter += 1;
+ },
+ },
+ callbacks: {
+ logCounter: () => {
+ const context = getContext();
+ console.log( `The counter is now: ${ context.counter }` );
+ },
+ },
+} );
+```
+
+In this example:
+
+1. The `data-wp-context` directive adds a local context with a property `counter` whose value is `0`.
+2. The `data-wp-watch` directive is set to `callbacks.logCounter`.
+3. Every time `context.counter` changes, the `logCounter` callback will be executed.
+4. The `logCounter` callback logs the current counter to the console.
+
+This allows you to create declarative side effects that automatically run in response to data changes. Some other use cases for `data-wp-watch` might include:
+
+- Saving data to `localStorage` when the data changes.
+- Sending analytics events.
+- Changing the focus for accessibility purposes.
+- Updating the page title, meta tags, or `` attributes.
+- Triggering animations.
+
+## Conclusion
+
+As you continue to work with the Interactivity API, remember to think in terms of state, actions, and side effects. Define your data, describe how it should change, and let the Interactivity API handle the rest. This mental shift may take some time, especially if you're used to more imperative programming styles, but by embracing it, you'll unlock the full potential of the Interactivity API to create truly dynamic and interactive WordPress blocks that delight your users.
diff --git a/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md b/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md
new file mode 100644
index 0000000000000..0071f26fc8a33
--- /dev/null
+++ b/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md
@@ -0,0 +1,740 @@
+# Understanding global state, local context and derived state
+
+The Interactivity API offers a powerful framework for creating interactive blocks. To make the most of its capabilities, it's crucial to understand when to use global state, local context, or derived state. This guide will clarify these concepts and provide practical examples to help you decide when to use each one.
+
+Let's start with a brief definition of global state, local context and derived state.
+
+- **Global state:** Global data that can be accessed and modified by any interactive block on the page, allowing different parts of your interactive blocks to stay in sync.
+- **Local context:** Local data defined within a specific element in the HTML structure, accessible only to that element and its children, providing independent state for individual blocks.
+- **Derived state:** Computed values based on global state or local context, dynamically calculated on-demand to ensure consistent data representation without storing redundant data.
+
+Let's now dive into each of these concepts to study them in more detail and provide some examples.
+
+## Global state
+
+**Global state** in the Interactivity API refers to global data that can be accessed and modified by any interactive block on the page. It serves as a shared information hub, allowing different parts of your blocks to communicate and stay in sync. Global state is the ideal mechanism for exchanging information between interactive blocks, regardless of their position in the DOM tree.
+
+You should use global state when:
+
+- You need to share data between multiple interactive blocks that are not directly related in the DOM hierarchy.
+- You want to maintain a single source of truth for certain data across all your interactive blocks.
+- You're dealing with data that affects multiple parts of your UI simultaneously.
+- You want to implement features that are global for the page.
+
+### Working with global state
+
+- **Initializing the global state**
+
+ Typically, the initial global state values should be defined on the server using the `wp_interactivity_state` function:
+
+ ```php
+ // Populates the initial global state values.
+ wp_interactivity_state( 'myPlugin', array(
+ 'isDarkTheme' => true,
+ 'show' => false,
+ 'helloText' => __( 'world' ),
+ ));
+ ```
+
+ These initial global state values will be used during the rendering of the page in PHP to populate the HTML markup that is sent to the browser.
+
+ - HTML markup written in the PHP file by the developer:
+
+ ```html
+
+
+ Hello
+
+
+
+ ```
+
+ - HTML markup after the directives have been processed and it is ready to be sent to the browser:
+
+ ```html
+
+
+ Hello world
+
+
+
+ ```
+
+ _Please, visit [the Server-side Rendering guide](./server-side-rendering.md) to learn more about how directives are processed on the server._
+
+ In cases where the global state is not used during the rendering of the page in PHP, it can also be defined directly on the client.
+
+ ```js
+ const { state } = store( 'myPlugin', {
+ state: {
+ isLoading: false,
+ },
+ actions: {
+ *loadSomething() {
+ state.isLoading = true;
+ // ...
+ },
+ },
+ } );
+ ```
+
+ _Please note that, although this works, in general it is a good practice to define all the global state on the server._
+
+- **Accessing the global state**
+
+ In the HTML markup, you can access the global state values directly by referencing `state` in the directive attribute values:
+
+ ```html
+
+
+
+ ```
+
+ In JavaScript, the `store` function from the package at `@wordpress/interactivity` works both as a setter and a getter, returning the store of the selected namespace.
+
+ To access the global state in your actions and callbacks, you can use the `state` property of the object returned by the `store` function:
+
+ ```js
+ const myPluginStore = store( 'myPlugin' );
+
+ myPluginStore.state; // This is the state of the 'myPlugin' namespace.
+ ```
+
+ You can also destructure the object returned by `store`:
+
+ ```js
+ const { state } = store( 'myPlugin' );
+ ```
+
+ And you can do the same even if you are defining the store at that moment, which is the most common scenario:
+
+ ```js
+ const { state } = store( 'myPlugin', {
+ state: {
+ // ...
+ },
+ actions: {
+ toggle() {
+ state.show = ! state.show;
+ },
+ },
+ } );
+ ```
+
+ The global state initialized on the server using the `wp_interactivity_state` function is also included in that object because it is automatically serialized from the server to the client:
+
+ ```php
+ wp_interactivity_state( 'myPlugin', array(
+ 'someValue' => 1,
+ ));
+ ```
+
+ ```js
+ const { state } = store( 'myPlugin', {
+ state: {
+ otherValue: 2,
+ },
+ actions: {
+ readGlobalState() {
+ state.someValue; // It exists and its initial value is 1.
+ state.otherValue; // It exists and its initial value is 2.
+ },
+ },
+ } );
+ ```
+
+ Lastly, all calls to the `store` function with the same namespace are merged together:
+
+ ```js
+ store( 'myPlugin', { state: { someValue: 1 } } );
+
+ store( 'myPlugin', { state: { otherValue: 2 } } );
+
+ /* All calls to `store` return a stable reference to the same object, so you
+ * can get a reference to `state` from any of them. */
+ const { state } = store( 'myPlugin' );
+
+ store( 'myPlugin', {
+ actions: {
+ readValues() {
+ state.someValue; // It exists and its initial value is 1.
+ state.otherValue; // It exists and its initial value is 2.
+ },
+ },
+ } );
+ ```
+
+- **Updating the global state**
+
+ To update the global state, all you need to do is mutate the `state` object once you have obtained it from the `store` function:
+
+ ```js
+ const { state } = store( 'myPlugin', {
+ actions: {
+ updateValues() {
+ state.someValue = 3;
+ state.otherValue = 4;
+ },
+ },
+ } );
+ ```
+
+ Changes to the global state will automatically trigger updates in any directives that depend on the modified values.
+
+ _Please, visit [The Reactive and Declarative mindset](./the-reactive-and-declarative-mindset.md) guide to learn more about how reactivity works in the Interactivity API._
+
+### Example: Two interactive blocks using global state to communicate
+
+In this example, there are two independent interactive blocks. One displays a counter, and the other a button to increment that counter. These blocks can be positioned anywhere on the page, regardless of the HTML structure. In other words, one does not need to be an inner block of the other.
+
+- **Counter Block**
+
+ ```php
+ 0
+ ));
+ ?>
+
+
+ >
+ Counter:
+
+ ```
+
+- **Increment Block**
+
+ ```php
+
+ >
+
+
+ ```
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ actions: {
+ increment() {
+ state.counter += 1;
+ },
+ },
+ } );
+ ```
+
+In this example:
+
+1. The global state is initialized on the server using `wp_interactivity_state`, setting an initial `counter` of 0.
+2. The Counter Block displays the current counter using `data-wp-text="state.counter"`, which reads from the global state.
+3. The Increment Block contains a button that triggers the `increment` action when clicked, using `data-wp-on-async--click="actions.increment"`.
+4. In JavaScript, the `increment` action directly modifies the global state by incrementing `state.counter`.
+
+Both blocks are independent and can be placed anywhere on the page. They don't need to be nested or directly related in the DOM structure. Multiple instances of these interactive blocks can be added to the page, and they will all share and update the same global counter value.
+
+## Local context
+
+**Local context** in the Interactivity API refers to local data defined within a specific element in the HTML structure. Unlike global state, local context is only accessible to the element where it's defined and its child elements.
+
+The local context is particularly useful when you need independent state for individual interactive blocks, ensuring that each instance of a block can maintain its own unique data without interfering with others.
+
+You should use local context when:
+
+- You need to maintain separate state for multiple instances of the same interactive block.
+- You want to encapsulate data that's only relevant to a specific interactive block and its children.
+- You need to implement features that are isolated to a specific part of your UI.
+
+### Working with local context
+
+- **Initializing the local context**
+
+ The local context is initialized directly within the HTML structure using the `data-wp-context` directive. This directive accepts a JSON string that defines the initial values for that piece of context.
+
+ ```html
+
+
+
+ ```
+
+ You can also initialize the local context on the server using the `wp_interactivity_data_wp_context` PHP helper, which ensures proper escaping and formatting of the stringified values:
+
+ ```php
+ 0 );
+ ?>
+
+
>
+
+
+ ```
+
+- **Accessing the local context**
+
+ In the HTML markup, you can access the local context values directly by referencing `context` in the directive values:
+
+ ```html
+
+
+
+ ```
+
+ In JavaScript, you can access the local context values using the `getContext` function:
+
+ ```js
+ store( 'myPlugin', {
+ actions: {
+ sendAnalyticsEvent() {
+ const { counter } = getContext();
+ myAnalyticsLibrary.sendEvent( 'updated counter', counter );
+ },
+ },
+ callbacks: {
+ logCounter() {
+ const { counter } = getContext();
+ console.log( `Current counter: ${ counter }` );
+ },
+ },
+ } );
+ ```
+
+ The `getContext` function returns the local context of the element that triggered the action/callback execution.
+
+- **Updating the local context**
+
+ To update the local context values in JavaScript, you can modify the object returned by `getContext`:
+
+ ```js
+ store( 'myPlugin', {
+ actions: {
+ increment() {
+ const context = getContext();
+ context.counter += 1;
+ },
+ updateName( event ) {
+ const context = getContext();
+ context.name = event.target.value;
+ },
+ },
+ } );
+ ```
+
+ Changes to the local context will automatically trigger updates in any directives that depend on the modified values.
+
+ _Please, visit [The Reactive and Declarative mindset](./the-reactive-and-declarative-mindset.md) guide to learn more about how reactivity works in the Interactivity API._
+
+- **Nesting local contexts**
+
+ Local contexts can be nested, with child contexts inheriting and potentially overriding values from parent contexts:
+
+ ```html
+
+
Theme:
+
Counter:
+
+
+
Theme:
+
Counter:
+
+
+ ```
+
+ In this example, the inner `div` will have a `theme` value of `"dark"`, but will inherit the `counter` value `0` from its parent context.
+
+### Example: One interactive block using local context to have independent state
+
+In this example, there is a single interactive block that shows a counter and can increment it. By using local context, each instance of this block will have its own independent counter, even if multiple blocks are added to the page.
+
+```php
+
+ data-wp-context='{ "counter": 0 }'
+>
+
Counter:
+
+
+```
+
+```js
+store( 'myCounterPlugin', {
+ actions: {
+ increment() {
+ const context = getContext();
+ context.counter += 1;
+ },
+ },
+} );
+```
+
+In this example:
+
+1. A local context with an initial `counter` value of `0` is defined using the `data-wp-context` directive.
+2. The counter is displayed using `data-wp-text="context.counter"`, which reads from the local context.
+3. The increment button uses `data-wp-on-async--click="actions.increment"` to trigger the increment action.
+4. In JavaScript, the `getContext` function is used to access and modify the local context for each block instance.
+
+A user will be able to add multiple instances of this block to a page, and each will maintain its own independent counter. Clicking the "Increment" button on one block will only affect that specific block's counter and not the others.
+
+## Derived state
+
+**Derived state** in the Interactivity API refers to a value that is computed from other parts of the global state or local context. It's calculated on demand rather than stored. It ensures consistency, reduces redundancies, and enhances the declarative nature of your code.
+
+Derived state is a fundamental concept in modern state management, not unique to the Interactivity API. It's also used in other popular state management systems like Redux, where it's called `selectors`, or Preact Signals, where it's known as `computed` values.
+
+Derived state offers several key benefits that make it an essential part of a well-designed application state, including:
+
+1. **Single source of truth:** Derived state encourages you to store only the essential, raw data in your state. Any values that can be calculated from this core data become derived state. This approach reduces the risk of inconsistencies in your interactive blocks.
+
+2. **Automatic updates:** When you use derived state, values are recalculated automatically whenever the underlying data changes. This ensures that all parts of your interactive blocks always have access to the most up-to-date information without manual intervention.
+
+3. **Simplified state management:** By computing values on-demand rather than storing and updating them manually, you reduce the complexity of your state management logic. This leads to cleaner, more maintainable code.
+
+4. **Improved performance:** In many cases, derived state can be optimized to recalculate only when necessary, potentially improving your interactive blocks' performance.
+
+5. **Easier debugging:** With derived state, it's clearer where data originates and how it's transformed. This can make it easier to track down issues in your interactive blocks.
+
+In essence, derived state allows you to express relationships between different pieces of data in your interactive blocks declaratively, instead of imperatively updating related values whenever something changes.
+
+_Please, visit [The Reactive and Declarative mindset](./the-reactive-and-declarative-mindset.md) guide to learn more about how to leverage declarative coding in the Interactivity API._
+
+You should use derived state:
+
+- When a part of your global state or local context can be computed from other state values.
+- To avoid redundant data that needs to be manually kept in sync.
+- To ensure consistency across your interactive blocks by automatically updating derived values.
+- To simplify your actions by removing the need to update multiple related state properties.
+
+### Working with derived state
+
+- **Initializing the derived state**
+
+ Typically, the derived state should be initialized on the server using the `wp_interactivity_state` function in the exact same way as the global state.
+
+ - When the initial value is known and static, it can be defined directly:
+
+ ```php
+ wp_interactivity_state( 'myCounterPlugin', array(
+ 'counter' => 1, // This is global state.
+ 'double' => 2, // This is derived state.
+ ));
+ ```
+
+ - Or it can be defined by doing the necessary computations:
+
+ ```php
+ $counter = 1;
+ $double = $counter * 2;
+
+ wp_interactivity_state( 'myCounterPlugin', array(
+ 'counter' => $counter, // This is global state.
+ 'double' => $double, // This is derived state.
+ ));
+ ```
+
+ Regardless of the approach, the initial derived state values will be used during the rendering of the page in PHP, and the HTML can be populated with the correct values.
+
+ _Please, visit [the Server-side Rendering guide](./server-side-rendering.md) to learn more about how directives are processed on the server._
+
+ The same mechanism applies even when the derived state property depends on the local context.
+
+ ```php
+ $counter );
+
+ wp_interactivity_state( 'myCounterPlugin', array(
+ 'double' => $counter * 2, // This is derived state.
+ ));
+ ?>
+
+
+ >
+
+ Counter:
+
+
+ Double:
+
+
+ ```
+
+ In JavaScript, the derived state is defined using getters:
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ state: {
+ get double() {
+ return state.counter * 2;
+ },
+ },
+ } );
+ ```
+
+ Derived state can depend on local context, or local context and global state at the same time.
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ state: {
+ get double() {
+ const { counter } = getContext();
+ // Depends on local context.
+ return counter * 2;
+ },
+ get product() {
+ const { counter } = getContext();
+ // Depends on local context and global state.
+ return counter * state.factor;
+ },
+ },
+ } );
+ ```
+
+ In some cases, when the derived state depends on the local context and the local context can change dynamically in the server, instead of the initial derived state, you can use a function (Closure) that calculates it dynamically.
+
+ ```php
+ array( 1, 2, 3 ),
+ 'factor' => 3,
+ 'product' => function() {
+ $state = wp_interactivity_state();
+ $context = wp_interactivity_get_context();
+ return $context['item'] * $state['factor'];
+ }
+ ));
+ ?>
+
+
+
+
+ ```
+
+ This `data-wp-each` template will render this HTML (directives omitted):
+
+ ```html
+ 3
+ 6
+ 9
+ ```
+
+- **Accessing the derived state**
+
+ In the HTML markup, the syntax for the derived state is the same as the one for the global state, just by referencing `state` in the directive attribute values.
+
+ ```html
+
+ ```
+
+ The same happens in JavaScript. Both global state and derived state can be consumed through the `state` property of the store:
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ // ...
+ actions: {
+ readValues() {
+ state.counter; // Regular state, returns 1.
+ state.double; // Derived state, returns 2.
+ },
+ },
+ } );
+ ```
+
+ This lack of distinction is intentional, allowing developers to consume both derived and global state uniformly, and making them interchangeable in practice.
+
+ You can also access the derived state from another derived state and, thus, create multiple levels of computed values.
+
+ ```js
+ const { state } = store( 'myPlugin', {
+ state: {
+ get double() {
+ return state.counter * 2;
+ },
+ get doublePlusOne() {
+ return state.double + 1;
+ },
+ },
+ } );
+ ```
+
+- **Updating the derived state**
+
+ The derived state cannot be updated directly. To update its values, you need to update the global state or local context on which that derived state depends.
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ // ...
+ actions: {
+ updateValues() {
+ state.counter; // Regular state, returns 1.
+ state.double; // Derived state, returns 2.
+
+ state.counter = 2;
+
+ state.counter; // Regular state, returns 2.
+ state.double; // Derived state, returns 4.
+ },
+ },
+ } );
+ ```
+
+### Example: Not using derived state vs using derived state
+
+Let's consider a scenario where there is a counter and the double value needs to be displayed, and let's compare two approaches: one without derived state and one with derived state.
+
+- **Not using derived state**
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ state: {
+ counter: 1,
+ double: 2,
+ },
+ actions: {
+ increment() {
+ state.counter += 1;
+ state.double = state.counter * 2;
+ },
+ },
+ } );
+ ```
+
+ In this approach, both the `state.counter` and `state.double` values are manually updated in the `increment` action. While this works, it has several drawbacks:
+
+ - It's less declarative.
+ - It can lead to bugs if `state.counter` is updated from multiple places and developers forget to keep `state.double` in sync.
+ - It requires more cognitive load to remember to update related values.
+
+- **Using derived state**
+
+ ```js
+ const { state } = store( 'myCounterPlugin', {
+ state: {
+ counter: 1,
+ get double() {
+ return state.counter * 2;
+ },
+ },
+ actions: {
+ increment() {
+ state.counter += 1;
+ },
+ },
+ } );
+ ```
+
+ In this improved version:
+
+ - `state.double` is defined as a getter, automatically deriving its value from `state.counter`.
+ - The `increment` action only needs to update `state.counter`.
+ - `state.double` is always guaranteed to have the correct value, regardless of how or where `state.counter` is updated.
+
+### Example: Using derived state with local context
+
+Let's now consider a scenario where there is a local context that initializes a counter.
+
+```js
+store( 'myCounterPlugin', {
+ state: {
+ get double() {
+ const { counter } = getContext();
+ return counter * 2;
+ },
+ },
+ actions: {
+ increment() {
+ const context = getContext();
+ context.counter += 1;
+ },
+ },
+} );
+```
+
+```html
+
+
+
+ Double:
+
+
+
+
+
+
+
+ Double:
+
+
+
+
+
+```
+
+In this example, the derived state `state.double` reads from the local context present in each element and returns the correct value for each instance where it is used.
+
+### Example: Using derived state with both local context and global state
+
+Let's now consider a scenario where there are a global tax rate and local product prices and calculate the final price, including tax.
+
+```html
+
+
Product Price: $
+
Tax Rate:
+
Price (inc. tax): $
+
+```
+
+```js
+const { state } = store( 'myProductPlugin', {
+ state: {
+ taxRate: 0.21,
+ get taxRatePercentage() {
+ return `${ state.taxRate * 100 }%`;
+ },
+ get priceWithTax() {
+ const { priceWithoutTax } = getContext();
+ return price * ( 1 + state.taxRate );
+ },
+ },
+ actions: {
+ updateTaxRate( event ) {
+ // Updates the global tax rate.
+ state.taxRate = event.target.value;
+ },
+ updatePrice( event ) {
+ // Updates the local product price.
+ const context = getContext();
+ context.priceWithoutTax = event.target.value;
+ },
+ },
+} );
+```
+
+In this example, `priceWithTax` is derived from both the global `taxRate` and the local `priceWithoutTax`. Every time you update the global state or local context through the `updateTaxRate` or `updatePrice` actions, the Interactivity API recomputes the derived state and updates the necessary parts of the DOM.
+
+By using derived state, you create a more maintainable and less error-prone codebase. It ensures that related state values are always in sync, reduces the complexity of your actions, and makes your code more declarative and easier to reason about.
+
+## Conclusion
+
+Remember, the key to effective state management is to keep your state minimal and avoid redundancy. Use derived state to compute values dynamically, and choose between global state and local context based on the scope and requirements of your data. This will lead to a cleaner, more robust architecture that is easier to debug and maintain.
diff --git a/docs/toc.json b/docs/toc.json
index fa80ee6c4f440..719ffa344e374 100644
--- a/docs/toc.json
+++ b/docs/toc.json
@@ -204,6 +204,19 @@
},
{
"docs/reference-guides/interactivity-api/README.md": [
+ {
+ "docs/reference-guides/interactivity-api/core-concepts/README.md": [
+ {
+ "docs/reference-guides/interactivity-api/core-concepts/the-reactive-and-declarative-mindset.md": []
+ },
+ {
+ "docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md": []
+ },
+ {
+ "docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md": []
+ }
+ ]
+ },
{
"docs/reference-guides/interactivity-api/iapi-quick-start-guide.md": []
},