diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 4f42550ba4cfbc..dd7ef824aa6b0c 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -378,7 +378,7 @@ Insert an image to make a visual statement. ([Source](https://github.com/WordPre
- **Name:** core/image
- **Category:** media
-- **Supports:** align (center, full, left, right, wide), anchor, color (~~background~~, ~~text~~), filter (duotone)
+- **Supports:** align (center, full, left, right, wide), anchor, color (~~background~~, ~~text~~), filter (duotone), interactivity
- **Attributes:** alt, aspectRatio, caption, height, href, id, lightbox, linkClass, linkDestination, linkTarget, rel, scale, sizeSlug, title, url, width
## Latest Comments
diff --git a/lib/compat/wordpress-6.5/class-wp-navigation-block-renderer.php b/lib/compat/wordpress-6.5/class-wp-navigation-block-renderer.php
index 52ec4f508246ac..9c2314ebe6890c 100644
--- a/lib/compat/wordpress-6.5/class-wp-navigation-block-renderer.php
+++ b/lib/compat/wordpress-6.5/class-wp-navigation-block-renderer.php
@@ -543,20 +543,28 @@ private static function get_nav_element_directives( $should_load_view_script ) {
*/
private static function handle_view_script_loading( $attributes, $block, $inner_blocks ) {
$should_load_view_script = static::should_load_view_script( $attributes, $inner_blocks );
+ $is_gutenberg_plugin = defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN;
+ $view_js_file = 'wp-block-navigation-view';
+ $script_handles = $block->block_type->view_script_handles;
- $view_js_file = 'wp-block-navigation-view';
-
- // If the script already exists, there is no point in removing it from viewScript.
- if ( ! wp_script_is( $view_js_file ) ) {
- $script_handles = $block->block_type->view_script_handles;
-
- // If the script is not needed, and it is still in the `view_script_handles`, remove it.
- if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) {
- $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) );
+ if ( $is_gutenberg_plugin ) {
+ if ( $should_load_view_script ) {
+ gutenberg_enqueue_module( '@wordpress/block-library/navigation-block' );
}
- // If the script is needed, but it was previously removed, add it again.
- if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) {
- $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) );
+ // Remove the view script because we are using the module.
+ $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) );
+ } else {
+ // If the script already exists, there is no point in removing it from viewScript.
+ if ( ! wp_script_is( $view_js_file ) ) {
+
+ // If the script is not needed, and it is still in the `view_script_handles`, remove it.
+ if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) {
+ $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) );
+ }
+ // If the script is needed, but it was previously removed, add it again.
+ if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) {
+ $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) );
+ }
}
}
}
diff --git a/lib/experimental/interactivity-api/modules.php b/lib/experimental/interactivity-api/modules.php
new file mode 100644
index 00000000000000..7db774de04fd79
--- /dev/null
+++ b/lib/experimental/interactivity-api/modules.php
@@ -0,0 +1,33 @@
+ 'defer',
+ )
+ );
+}
+
+add_action( 'wp_enqueue_scripts', 'gutenberg_register_interactivity_module' );
diff --git a/lib/experimental/interactivity-api/scripts.php b/lib/experimental/interactivity-api/scripts.php
deleted file mode 100644
index ed1fca85500701..00000000000000
--- a/lib/experimental/interactivity-api/scripts.php
+++ /dev/null
@@ -1,40 +0,0 @@
-=' );
- if ( $supports_defer ) {
- // Defer execution of @wordpress/interactivity package but continue loading in head.
- wp_script_add_data( 'wp-interactivity', 'strategy', 'defer' );
- wp_script_add_data( 'wp-interactivity', 'group', 0 );
- } else {
- // Move the @wordpress/interactivity package to the footer.
- wp_script_add_data( 'wp-interactivity', 'group', 1 );
- }
-
- // Move all the view scripts of the interactive blocks to the footer.
- $registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
- foreach ( array_values( $registered_blocks ) as $block ) {
- if ( isset( $block->supports['interactivity'] ) && $block->supports['interactivity'] ) {
- foreach ( $block->view_script_handles as $handle ) {
- // Note that all block view scripts are already made defer by default.
- wp_script_add_data( $handle, 'group', $supports_defer ? 0 : 1 );
- }
- }
- }
-}
-add_action( 'wp_enqueue_scripts', 'gutenberg_interactivity_move_interactive_scripts_to_the_footer', 11 );
diff --git a/lib/experimental/modules/class-gutenberg-modules.php b/lib/experimental/modules/class-gutenberg-modules.php
new file mode 100644
index 00000000000000..ca74d863043ee6
--- /dev/null
+++ b/lib/experimental/modules/class-gutenberg-modules.php
@@ -0,0 +1,195 @@
+ isset( $dependencies['static'] ) || isset( $dependencies['dynamic'] ) ? $dependencies['static'] ?? array() : $dependencies,
+ 'dynamic' => isset( $dependencies['dynamic'] ) ? $dependencies['dynamic'] : array(),
+ );
+
+ self::$registered[ $module_identifier ] = array(
+ 'src' => $src,
+ 'version' => $version,
+ 'dependencies' => $deps,
+ );
+ }
+ }
+
+ /**
+ * Enqueues a module in the page.
+ *
+ * @param string $module_identifier The identifier of the module.
+ */
+ public static function enqueue( $module_identifier ) {
+ // Add the module to the queue if it's not already there.
+ if ( ! in_array( $module_identifier, self::$enqueued, true ) ) {
+ self::$enqueued[] = $module_identifier;
+ }
+ }
+
+ /**
+ * Returns the import map array.
+ *
+ * @return array Associative array with 'imports' key mapping to an array of module identifiers and their respective source strings.
+ */
+ public static function get_import_map() {
+ $imports = array();
+ foreach ( self::get_dependencies( self::$enqueued, array( 'static', 'dynamic' ) ) as $module_identifier => $module ) {
+ $imports[ $module_identifier ] = $module['src'] . self::get_version_query_string( $module['version'] );
+ }
+ return array( 'imports' => $imports );
+ }
+
+ /**
+ * Prints the import map.
+ */
+ public static function print_import_map() {
+ $import_map = self::get_import_map();
+ if ( ! empty( $import_map['imports'] ) ) {
+ echo '';
+ }
+ }
+
+ /**
+ * Prints all the enqueued modules using