diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md
index 38a93552bcbef..a0acea3ced683 100644
--- a/docs/reference-guides/data/data-core-block-editor.md
+++ b/docs/reference-guides/data/data-core-block-editor.md
@@ -846,6 +846,10 @@ _Returns_
 
 -   `string|false`: Block Template Lock
 
+### getUnsyncedPatterns
+
+Undocumented declaration.
+
 ### hasBlockMovingClientId
 
 Returns whether block moving mode is enabled.
diff --git a/docs/reference-guides/data/data-core-blocks.md b/docs/reference-guides/data/data-core-blocks.md
index 084c9c1d7a5fb..0c3c95beb1f1b 100644
--- a/docs/reference-guides/data/data-core-blocks.md
+++ b/docs/reference-guides/data/data-core-blocks.md
@@ -246,6 +246,14 @@ _Returns_
 
 -   `(WPBlockVariation[]|void)`: Block variations.
 
+### getBootstrappedBlockType
+
+Undocumented declaration.
+
+### getBootstrappedBlockTypes
+
+Undocumented declaration.
+
 ### getCategories
 
 Returns all the available block categories.
diff --git a/lib/blocks.php b/lib/blocks.php
index 698b5d6873748..fc20c98d37f6e 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -227,6 +227,13 @@ function gutenberg_deregister_core_block_and_assets( $block_name ) {
 				}
 			}
 		}
+		if ( ! empty( $block_type->editor_script_handles ) ) {
+			foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
+				if ( str_starts_with( $editor_script_handle, 'wp-block-' ) ) {
+					wp_deregister_script( $editor_script_handle );
+				}
+			}
+		}
 		$registry->unregister( $block_name );
 	}
 }
diff --git a/lib/init.php b/lib/init.php
index 71b6a276c60f3..3fa7fdbaece97 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -57,3 +57,55 @@ function gutenberg_menu() {
 	);
 }
 add_action( 'admin_menu', 'gutenberg_menu', 9 );
+
+// disable loading and enqueuing block editor scripts and styles
+add_filter( 'should_load_block_editor_scripts_and_styles', '__return_false', 11 );
+
+function get_block_importmap() {
+	$block_registry = WP_Block_Type_Registry::get_instance();
+	$scripts = wp_scripts();
+	$styles = wp_styles();
+	$blocks         = array();
+
+	foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
+		$imports = array();
+		if ( isset( $block_type->editor_script_handles ) ) {
+			foreach ( $block_type->editor_script_handles as $handle ) {
+				$spec = $scripts->registered[ $handle ];
+				$imports[] = array(
+					'type' => 'script',
+					'handle' => $spec->handle,
+					'src' => $spec->src,
+					'ver' => $spec->ver
+				);
+			}
+		}
+		if ( isset( $block_type->editor_style_handles ) ) {
+			foreach ( $block_type->editor_style_handles as $handle ) {
+				if ( ! isset( $styles->registered[ $handle ] ) ) {
+					continue;
+				}
+				$spec = $styles->registered[ $handle ];
+				$imports[] = array(
+					'type' => 'style',
+					'handle' => $spec->handle,
+					'src' => $spec->src,
+					'ver' => $spec->ver
+				);
+			}
+		}
+		if ( ! empty( $imports ) ) {
+			$blocks[ $block_name ] = $imports;
+		}
+	}
+
+	return $blocks;
+}
+
+function emit_importmap() {
+	wp_register_script( 'wp-importmap', '');
+	wp_add_inline_script( 'wp-importmap', 'wp.importmap = ' . wp_json_encode( get_block_importmap() ) . ';' );
+  wp_enqueue_script('wp-importmap');
+}
+
+add_action( 'enqueue_block_editor_assets', 'emit_importmap' );
\ No newline at end of file
diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js
index bc06c9de5aaaf..e3539d7ba2e76 100644
--- a/packages/block-editor/src/autocompleters/block.js
+++ b/packages/block-editor/src/autocompleters/block.js
@@ -116,7 +116,7 @@ function createBlockCompleter() {
 		allowContext( before, after ) {
 			return ! ( /\S/.test( before ) || /\S/.test( after ) );
 		},
-		getOptionCompletion( inserterItem ) {
+		async getOptionCompletion( inserterItem ) {
 			const {
 				name,
 				initialAttributes,
@@ -124,12 +124,11 @@ function createBlockCompleter() {
 				syncStatus,
 				content,
 			} = inserterItem;
-
 			return {
 				action: 'replace',
 				value:
 					syncStatus === 'unsynced'
-						? parse( content, {
+						? await parse( content, {
 								__unstableSkipMigrationLogs: true,
 						  } )
 						: createBlock(
diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js
index 5d942d2b25c70..2f82cda208619 100644
--- a/packages/block-editor/src/components/block-actions/index.js
+++ b/packages/block-editor/src/components/block-actions/index.js
@@ -103,7 +103,7 @@ export default function BlockActions( {
 			selectBlock( clientIds[ 0 ] );
 			setBlockMovingClientId( clientIds[ 0 ] );
 		},
-		onGroup() {
+		async onGroup() {
 			if ( ! blocks.length ) {
 				return;
 			}
@@ -111,7 +111,10 @@ export default function BlockActions( {
 			const groupingBlockName = getGroupingBlockName();
 
 			// Activate the `transform` on `core/group` which does the conversion.
-			const newBlocks = switchToBlockType( blocks, groupingBlockName );
+			const newBlocks = await switchToBlockType(
+				blocks,
+				groupingBlockName
+			);
 
 			if ( ! newBlocks ) {
 				return;
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index c053235c2d0d3..de89fd6cd8cf5 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -307,7 +307,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 				) {
 					removeBlock( _clientId );
 				} else {
-					registry.batch( () => {
+					registry.batch( async () => {
 						if (
 							canInsertBlockType(
 								getBlockName( firstClientId ),
@@ -321,7 +321,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 								getBlockIndex( _clientId )
 							);
 						} else {
-							const replacement = switchToBlockType(
+							const replacement = await switchToBlockType(
 								getBlock( firstClientId ),
 								getDefaultBlockName()
 							);
diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js
index 027ed12a7483a..5adb68d7c1764 100644
--- a/packages/block-editor/src/components/block-list/block.native.js
+++ b/packages/block-editor/src/components/block-list/block.native.js
@@ -500,7 +500,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 				) {
 					removeBlock( _clientId );
 				} else {
-					registry.batch( () => {
+					registry.batch( async () => {
 						if (
 							canInsertBlockType(
 								getBlockName( firstClientId ),
@@ -514,7 +514,7 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 								getBlockIndex( _clientId )
 							);
 						} else {
-							const replacement = switchToBlockType(
+							const replacement = await switchToBlockType(
 								getBlock( firstClientId ),
 								getDefaultBlockName()
 							);
diff --git a/packages/block-editor/src/components/block-styles/use-styles-for-block.js b/packages/block-editor/src/components/block-styles/use-styles-for-block.js
index 6d73dca557b05..d89557be05864 100644
--- a/packages/block-editor/src/components/block-styles/use-styles-for-block.js
+++ b/packages/block-editor/src/components/block-styles/use-styles-for-block.js
@@ -24,20 +24,13 @@ import { store as blockEditorStore } from '../../store';
  */
 function useGenericPreviewBlock( block, type ) {
 	return useMemo( () => {
-		const example = type?.example;
-		const blockName = type?.name;
-
-		if ( example && blockName ) {
-			return getBlockFromExample( blockName, {
-				attributes: example.attributes,
-				innerBlocks: example.innerBlocks,
-			} );
+		if ( type && type.blockName && type.example ) {
+			return getBlockFromExample( type.blockName, type.example );
 		}
-
 		if ( block ) {
 			return cloneBlock( block );
 		}
-	}, [ type?.example ? block?.name : block, type ] );
+	}, [ type, type?.example ? block?.name : block ] );
 }
 
 /**
diff --git a/packages/block-editor/src/components/block-switcher/block-transformations-menu.js b/packages/block-editor/src/components/block-switcher/block-transformations-menu.js
index 033201d7facad..f89b1605391bc 100644
--- a/packages/block-editor/src/components/block-switcher/block-transformations-menu.js
+++ b/packages/block-editor/src/components/block-switcher/block-transformations-menu.js
@@ -7,7 +7,7 @@ import {
 	getBlockMenuDefaultClassName,
 	switchToBlockType,
 } from '@wordpress/blocks';
-import { useState, useMemo } from '@wordpress/element';
+import { useEffect, useState, useMemo } from '@wordpress/element';
 
 /**
  * Internal dependencies
@@ -63,6 +63,21 @@ function useGroupedTransforms( possibleBlockTransformations ) {
 	return transformations;
 }
 
+function Preview( { blocks, transformName } ) {
+	const [ switched, setSwitched ] = useState( null );
+	useEffect( () => {
+		switchToBlockType( blocks, transformName ).then( ( result ) =>
+			setSwitched( result )
+		);
+	}, [ blocks, transformName ] );
+
+	if ( ! switched ) {
+		return null;
+	}
+
+	return <PreviewBlockPopover blocks={ blocks } />;
+}
+
 const BlockTransformationsMenu = ( {
 	className,
 	possibleBlockTransformations,
@@ -91,11 +106,10 @@ const BlockTransformationsMenu = ( {
 		<>
 			<MenuGroup label={ __( 'Transform to' ) } className={ className }>
 				{ hoveredTransformItemName && (
-					<PreviewBlockPopover
-						blocks={ switchToBlockType(
-							blocks,
-							hoveredTransformItemName
-						) }
+					<Preview
+						key={ hoveredTransformItemName }
+						blocks={ blocks }
+						transformName={ hoveredTransformItemName }
 					/>
 				) }
 				{ !! possibleBlockVariationTransformations?.length && (
diff --git a/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js b/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js
index 33952378f601f..a9ae04bcf90b3 100644
--- a/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js
+++ b/packages/block-editor/src/components/block-switcher/block-transformations-menu.native.js
@@ -55,10 +55,10 @@ const BlockTransformationsMenu = ( {
 	const getAnchor = () =>
 		anchorNodeRef ? findNodeHandle( anchorNodeRef ) : undefined;
 
-	function onPickerSelect( value ) {
+	async function onPickerSelect( value ) {
 		replaceBlocks(
 			selectedBlockClientId,
-			switchToBlockType( selectedBlock, value )
+			await switchToBlockType( selectedBlock, value )
 		);
 
 		const selectedItem = pickerOptions().find(
diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js
index 8117c6f539b4f..eb58a1dc1e1d9 100644
--- a/packages/block-editor/src/components/block-switcher/index.js
+++ b/packages/block-editor/src/components/block-switcher/index.js
@@ -13,9 +13,11 @@ import {
 	store as blocksStore,
 	isReusableBlock,
 	isTemplatePart,
+	getTransformItemsForBlocks,
 } from '@wordpress/blocks';
 import { useSelect, useDispatch } from '@wordpress/data';
 import { copy } from '@wordpress/icons';
+import { useState, useEffect } from '@wordpress/element';
 
 /**
  * Internal dependencies
@@ -33,56 +35,58 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
 	const { replaceBlocks, multiSelect, updateBlockAttributes } =
 		useDispatch( blockEditorStore );
 	const blockInformation = useBlockDisplayInformation( blocks[ 0 ].clientId );
-	const {
-		possibleBlockTransformations,
-		canRemove,
-		hasBlockStyles,
-		icon,
-		patterns,
-	} = useSelect(
-		( select ) => {
-			const {
-				getBlockRootClientId,
-				getBlockTransformItems,
-				__experimentalGetPatternTransformItems,
-				canRemoveBlocks,
-			} = select( blockEditorStore );
-			const { getBlockStyles, getBlockType } = select( blocksStore );
-			const rootClientId = getBlockRootClientId(
-				Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
-			);
-			const [ { name: firstBlockName } ] = blocks;
-			const _isSingleBlockSelected = blocks.length === 1;
-			const styles =
-				_isSingleBlockSelected && getBlockStyles( firstBlockName );
-			let _icon;
-			if ( _isSingleBlockSelected ) {
-				_icon = blockInformation?.icon; // Take into account active block variations.
-			} else {
-				const isSelectionOfSameType =
-					new Set( blocks.map( ( { name } ) => name ) ).size === 1;
-				// When selection consists of blocks of multiple types, display an
-				// appropriate icon to communicate the non-uniformity.
-				_icon = isSelectionOfSameType
-					? getBlockType( firstBlockName )?.icon
-					: copy;
-			}
-			return {
-				possibleBlockTransformations: getBlockTransformItems(
-					blocks,
-					rootClientId
-				),
-				canRemove: canRemoveBlocks( clientIds, rootClientId ),
-				hasBlockStyles: !! styles?.length,
-				icon: _icon,
-				patterns: __experimentalGetPatternTransformItems(
-					blocks,
-					rootClientId
-				),
-			};
-		},
-		[ clientIds, blocks, blockInformation?.icon ]
-	);
+	const { blockTransformations, canRemove, hasBlockStyles, icon, patterns } =
+		useSelect(
+			( select ) => {
+				const {
+					getBlockRootClientId,
+					getBlockTransformItems,
+					__experimentalGetPatternTransformItems,
+					canRemoveBlocks,
+				} = select( blockEditorStore );
+				const { getBlockStyles, getBlockType } = select( blocksStore );
+				const rootClientId = getBlockRootClientId(
+					Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
+				);
+				const [ { name: firstBlockName } ] = blocks;
+				const _isSingleBlockSelected = blocks.length === 1;
+				const styles =
+					_isSingleBlockSelected && getBlockStyles( firstBlockName );
+				let _icon;
+				if ( _isSingleBlockSelected ) {
+					_icon = blockInformation?.icon; // Take into account active block variations.
+				} else {
+					const isSelectionOfSameType =
+						new Set( blocks.map( ( { name } ) => name ) ).size ===
+						1;
+					// When selection consists of blocks of multiple types, display an
+					// appropriate icon to communicate the non-uniformity.
+					_icon = isSelectionOfSameType
+						? getBlockType( firstBlockName )?.icon
+						: copy;
+				}
+				return {
+					blockTransformations:
+						getBlockTransformItems( rootClientId ),
+					canRemove: canRemoveBlocks( clientIds, rootClientId ),
+					hasBlockStyles: !! styles?.length,
+					icon: _icon,
+					patterns: __experimentalGetPatternTransformItems(
+						blocks,
+						rootClientId
+					),
+				};
+			},
+			[ clientIds, blocks, blockInformation?.icon ]
+		);
+
+	const [ possibleBlockTransformations, setPossibleBlockTransformations ] =
+		useState( null );
+	useEffect( () => {
+		getTransformItemsForBlocks( blockTransformations, blocks ).then(
+			( result ) => setPossibleBlockTransformations( result )
+		);
+	}, [ blockTransformations, blocks ] );
 
 	const blockVariationTransformations = useBlockVariationTransforms( {
 		clientIds,
@@ -108,8 +112,8 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
 	}
 
 	// Simple block tranformation based on the `Block Transforms` API.
-	function onBlockTransform( name ) {
-		const newBlocks = switchToBlockType( blocks, name );
+	async function onBlockTransform( name ) {
+		const newBlocks = await switchToBlockType( blocks, name );
 		replaceBlocks( clientIds, newBlocks );
 		selectForMultipleBlocks( newBlocks );
 	}
@@ -134,7 +138,7 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
 	 * by allowing to exclude blocks from wildcard transformations.
 	 */
 	const hasPossibleBlockTransformations =
-		!! possibleBlockTransformations.length && canRemove && ! isTemplate;
+		possibleBlockTransformations?.length && canRemove && ! isTemplate;
 	const hasPossibleBlockVariationTransformations =
 		!! blockVariationTransformations?.length;
 	const hasPatternTransformation = !! patterns?.length && canRemove;
diff --git a/packages/block-editor/src/components/convert-to-group-buttons/index.js b/packages/block-editor/src/components/convert-to-group-buttons/index.js
index c2bb3fb25b845..1053f76ba7efe 100644
--- a/packages/block-editor/src/components/convert-to-group-buttons/index.js
+++ b/packages/block-editor/src/components/convert-to-group-buttons/index.js
@@ -23,9 +23,9 @@ function ConvertToGroupButton( {
 	onClose = () => {},
 } ) {
 	const { replaceBlocks } = useDispatch( blockEditorStore );
-	const onConvertToGroup = () => {
+	const onConvertToGroup = async () => {
 		// Activate the `transform` on the Grouping Block which does the conversion.
-		const newBlocks = switchToBlockType(
+		const newBlocks = await switchToBlockType(
 			blocksSelection,
 			groupingBlockName
 		);
diff --git a/packages/block-editor/src/components/convert-to-group-buttons/toolbar.js b/packages/block-editor/src/components/convert-to-group-buttons/toolbar.js
index 38405fe3ee6ab..cde6fe11daec9 100644
--- a/packages/block-editor/src/components/convert-to-group-buttons/toolbar.js
+++ b/packages/block-editor/src/components/convert-to-group-buttons/toolbar.js
@@ -40,16 +40,12 @@ function BlockGroupToolbar() {
 		[ clientIds, groupingBlockName ]
 	);
 
-	const onConvertToGroup = ( layout ) => {
-		const newBlocks = switchToBlockType(
+	const onConvertToGroup = async ( layout ) => {
+		const newBlocks = await switchToBlockType(
 			blocksSelection,
 			groupingBlockName
 		);
 
-		if ( typeof layout !== 'string' ) {
-			layout = 'group';
-		}
-
 		if ( newBlocks && newBlocks.length > 0 ) {
 			// Because the block is not in the store yet we can't use
 			// updateBlockAttributes so need to manually update attributes.
@@ -69,10 +65,10 @@ function BlockGroupToolbar() {
 		return null;
 	}
 
-	const canInsertRow = !! variations.find(
+	const canInsertRow = variations?.some(
 		( { name } ) => name === 'group-row'
 	);
-	const canInsertStack = !! variations.find(
+	const canInsertStack = variations?.some(
 		( { name } ) => name === 'group-stack'
 	);
 
@@ -81,7 +77,7 @@ function BlockGroupToolbar() {
 			<ToolbarButton
 				icon={ group }
 				label={ _x( 'Group', 'verb' ) }
-				onClick={ onConvertToGroup }
+				onClick={ () => onConvertToGroup( 'group' ) }
 			/>
 			{ canInsertRow && (
 				<ToolbarButton
diff --git a/packages/block-editor/src/components/inserter-list-item/index.js b/packages/block-editor/src/components/inserter-list-item/index.js
index 5cc59dfbac005..033db5a9456ef 100644
--- a/packages/block-editor/src/components/inserter-list-item/index.js
+++ b/packages/block-editor/src/components/inserter-list-item/index.js
@@ -6,8 +6,10 @@ import classnames from 'classnames';
 /**
  * WordPress dependencies
  */
+import { useSelect } from '@wordpress/data';
 import { useMemo, useRef, memo } from '@wordpress/element';
 import {
+	store as blocksStore,
 	createBlock,
 	createBlocksFromInnerBlocksTemplate,
 	isReusableBlock,
@@ -26,29 +28,44 @@ import InserterDraggableBlocks from '../inserter-draggable-blocks';
 function InserterListItem( {
 	className,
 	isFirst,
-	item,
+	item: { name, isDisabled },
 	onSelect,
 	onHover,
 	isDraggable,
 	...props
 } ) {
 	const isDragging = useRef( false );
-	const itemIconStyle = item.icon
+
+	const { item } = useSelect(
+		( select ) => ( {
+			item: select( blocksStore ).getBlockType( name ),
+		} ),
+		[ name ]
+	);
+
+	const itemIconStyle = item?.icon
 		? {
 				backgroundColor: item.icon.background,
 				color: item.icon.foreground,
 		  }
 		: {};
-	const blocks = useMemo(
-		() => [
+
+	const blocks = useMemo( () => {
+		if ( ! item ) {
+			return [];
+		}
+		return [
 			createBlock(
 				item.name,
 				item.initialAttributes,
 				createBlocksFromInnerBlocksTemplate( item.innerBlocks )
 			),
-		],
-		[ item.name, item.initialAttributes, item.innerBlocks ]
-	);
+		];
+	}, [ item ] );
+
+	if ( ! item ) {
+		return null;
+	}
 
 	const isSynced =
 		( isReusableBlock( item ) && item.syncStatus !== 'unsynced' ) ||
@@ -89,7 +106,7 @@ function InserterListItem( {
 							'block-editor-block-types-list__item',
 							className
 						) }
-						disabled={ item.isDisabled }
+						disabled={ isDisabled }
 						onClick={ ( event ) => {
 							event.preventDefault();
 							onSelect(
diff --git a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
index 566d0476fbd0f..914a76276725d 100644
--- a/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
+++ b/packages/block-editor/src/components/inserter/hooks/use-block-types-state.js
@@ -36,19 +36,18 @@ const useBlockTypesState = ( rootClientId, onInsert ) => {
 	}, [] );
 
 	const onSelectItem = useCallback(
-		(
-			{ name, initialAttributes, innerBlocks, syncStatus, content },
-			shouldFocusBlock
-		) => {
+		async ( item, shouldFocusBlock ) => {
 			const insertedBlock =
-				syncStatus === 'unsynced'
-					? parse( content, {
+				item.syncStatus === 'unsynced'
+					? await parse( item.content, {
 							__unstableSkipMigrationLogs: true,
 					  } )
 					: createBlock(
-							name,
-							initialAttributes,
-							createBlocksFromInnerBlocksTemplate( innerBlocks )
+							item.name,
+							item.initialAttributes,
+							createBlocksFromInnerBlocksTemplate(
+								item.innerBlocks
+							)
 					  );
 
 			onInsert( insertedBlock, undefined, shouldFocusBlock );
diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js
index 24c099869ae0d..6123fdc8866fc 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -199,6 +199,8 @@ function InserterMenu(
 		},
 	} ) );
 
+	const readyToShow = showPatterns !== null;
+
 	const showPatternPanel =
 		selectedTab === 'patterns' &&
 		! delayedFilterValue &&
@@ -236,7 +238,7 @@ function InserterMenu(
 					placeholder={ __( 'Search' ) }
 					ref={ searchRef }
 				/>
-				{ !! delayedFilterValue && (
+				{ readyToShow && delayedFilterValue && (
 					<div className="block-editor-inserter__no-tab-container">
 						<InserterSearchResults
 							filterValue={ delayedFilterValue }
@@ -254,7 +256,7 @@ function InserterMenu(
 						/>
 					</div>
 				) }
-				{ showAsTabs && (
+				{ readyToShow && showAsTabs && (
 					<InserterTabs
 						showPatterns={ showPatterns }
 						showMedia={ showMedia }
@@ -263,7 +265,7 @@ function InserterMenu(
 						tabsContents={ inserterTabsContents }
 					/>
 				) }
-				{ ! delayedFilterValue && ! showAsTabs && (
+				{ readyToShow && ! delayedFilterValue && ! showAsTabs && (
 					<div className="block-editor-inserter__no-tab-container">
 						{ blocksTab }
 					</div>
diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js
index 540b51a4757e0..9edc1d7001f83 100644
--- a/packages/block-editor/src/components/inserter/quick-inserter.js
+++ b/packages/block-editor/src/components/inserter/quick-inserter.js
@@ -68,7 +68,7 @@ export default function QuickInserter( {
 	);
 
 	const showPatterns =
-		patterns.length && ( !! filterValue || prioritizePatterns );
+		patterns?.length && ( !! filterValue || prioritizePatterns );
 	const showSearch =
 		( showPatterns && patterns.length > SEARCH_THRESHOLD ) ||
 		blockTypes.length > SEARCH_THRESHOLD;
diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js
index 41b1d3fe37c56..a1f588b25cdcb 100644
--- a/packages/block-editor/src/components/media-placeholder/index.js
+++ b/packages/block-editor/src/components/media-placeholder/index.js
@@ -290,7 +290,7 @@ export function MediaPlaceholder( {
 	}
 
 	async function onHTMLDrop( HTML ) {
-		const blocks = pasteHandler( { HTML } );
+		const blocks = await pasteHandler( { HTML } );
 		return await handleBlocksDrop( blocks );
 	}
 
diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js
index acadfb24a7221..911c5809d3268 100644
--- a/packages/block-editor/src/components/rich-text/index.native.js
+++ b/packages/block-editor/src/components/rich-text/index.native.js
@@ -300,16 +300,17 @@ export function RichTextWrapper(
 	);
 
 	const onEnter = useCallback(
-		( { value, onChange, shiftKey } ) => {
+		async ( { value, onChange, shiftKey } ) => {
 			const canSplit = onReplace && onSplit;
 
 			if ( onReplace ) {
-				const transforms = getBlockTransforms( 'from' ).filter(
-					( { type } ) => type === 'enter'
+				const transforms = await getBlockTransforms( 'from' );
+				const enterTransforms = transforms.filter(
+					( t ) => t.type === 'enter'
+				);
+				const transformation = findTransform( enterTransforms, ( t ) =>
+					t.regExp.test( value.text )
 				);
-				const transformation = findTransform( transforms, ( item ) => {
-					return item.regExp.test( value.text );
-				} );
 
 				if ( transformation ) {
 					onReplace( [
@@ -364,7 +365,7 @@ export function RichTextWrapper(
 	);
 
 	const onPaste = useCallback(
-		( {
+		async ( {
 			value,
 			onChange,
 			html,
@@ -394,7 +395,7 @@ export function RichTextWrapper(
 			// Only process file if no HTML is present.
 			// Note: a pasted file may have the URL as plain text.
 			if ( files && files.length && ! html ) {
-				const content = pasteHandler( {
+				const content = await pasteHandler( {
 					HTML: filePasteHandler( files ),
 					mode: 'BLOCKS',
 					tagName,
@@ -433,7 +434,7 @@ export function RichTextWrapper(
 				mode = 'BLOCKS';
 			}
 
-			const content = pasteHandler( {
+			const content = await pasteHandler( {
 				HTML: html,
 				plainText,
 				mode,
@@ -486,7 +487,7 @@ export function RichTextWrapper(
 	);
 
 	const inputRule = useCallback(
-		( value, valueToFormat ) => {
+		async ( value, valueToFormat ) => {
 			if ( ! onReplace ) {
 				return;
 			}
@@ -503,14 +504,12 @@ export function RichTextWrapper(
 			}
 
 			const trimmedTextBefore = text.slice( 0, startPosition ).trim();
-			const prefixTransforms = getBlockTransforms( 'from' ).filter(
-				( { type } ) => type === 'prefix'
-			);
+			const prefixTransforms = (
+				await getBlockTransforms( 'from' )
+			 ).filter( ( { type } ) => type === 'prefix' );
 			const transformation = findTransform(
 				prefixTransforms,
-				( { prefix } ) => {
-					return trimmedTextBefore === prefix;
-				}
+				( { prefix } ) => trimmedTextBefore === prefix
 			);
 
 			if ( ! transformation ) {
diff --git a/packages/block-editor/src/components/rich-text/use-enter.js b/packages/block-editor/src/components/rich-text/use-enter.js
index 40d4f9be4759f..6bb6db1d3c870 100644
--- a/packages/block-editor/src/components/rich-text/use-enter.js
+++ b/packages/block-editor/src/components/rich-text/use-enter.js
@@ -20,7 +20,7 @@ export function useEnter( props ) {
 	const propsRef = useRef( props );
 	propsRef.current = props;
 	return useRefEffect( ( element ) => {
-		function onKeyDown( event ) {
+		async function onKeyDown( event ) {
 			if ( event.defaultPrevented ) {
 				return;
 			}
@@ -47,9 +47,9 @@ export function useEnter( props ) {
 			const canSplit = onReplace && onSplit;
 
 			if ( onReplace ) {
-				const transforms = getBlockTransforms( 'from' ).filter(
-					( { type } ) => type === 'enter'
-				);
+				const transforms = (
+					await getBlockTransforms( 'from' )
+				 ).filter( ( { type } ) => type === 'enter' );
 				const transformation = findTransform( transforms, ( item ) => {
 					return item.regExp.test( _value.text );
 				} );
diff --git a/packages/block-editor/src/components/rich-text/use-input-rules.js b/packages/block-editor/src/components/rich-text/use-input-rules.js
index 5640a85f5f269..1e7b3986c5198 100644
--- a/packages/block-editor/src/components/rich-text/use-input-rules.js
+++ b/packages/block-editor/src/components/rich-text/use-input-rules.js
@@ -83,7 +83,7 @@ export function useInputRules( props ) {
 	const propsRef = useRef( props );
 	propsRef.current = props;
 	return useRefEffect( ( element ) => {
-		function inputRule() {
+		async function inputRule() {
 			const { getValue, onReplace, selectionChange } = propsRef.current;
 
 			if ( ! onReplace ) {
@@ -102,14 +102,12 @@ export function useInputRules( props ) {
 			}
 
 			const trimmedTextBefore = text.slice( 0, start ).trim();
-			const prefixTransforms = getBlockTransforms( 'from' ).filter(
-				( { type } ) => type === 'prefix'
-			);
+			const prefixTransforms = (
+				await getBlockTransforms( 'from' )
+			 ).filter( ( { type } ) => type === 'prefix' );
 			const transformation = findTransform(
 				prefixTransforms,
-				( { prefix } ) => {
-					return trimmedTextBefore === prefix;
-				}
+				( { prefix } ) => trimmedTextBefore === prefix
 			);
 
 			if ( ! transformation ) {
@@ -128,7 +126,7 @@ export function useInputRules( props ) {
 			return true;
 		}
 
-		function onInput( event ) {
+		async function onInput( event ) {
 			const { inputType, type } = event;
 			const {
 				getValue,
@@ -142,8 +140,8 @@ export function useInputRules( props ) {
 				return;
 			}
 
-			if ( __unstableAllowPrefixTransformations && inputRule ) {
-				if ( inputRule() ) return;
+			if ( __unstableAllowPrefixTransformations ) {
+				if ( await inputRule() ) return;
 			}
 
 			const value = getValue();
diff --git a/packages/block-editor/src/components/rich-text/use-paste-handler.js b/packages/block-editor/src/components/rich-text/use-paste-handler.js
index 1302e2d0dce46..78382eb8d12b1 100644
--- a/packages/block-editor/src/components/rich-text/use-paste-handler.js
+++ b/packages/block-editor/src/components/rich-text/use-paste-handler.js
@@ -24,7 +24,7 @@ export function usePasteHandler( props ) {
 	const propsRef = useRef( props );
 	propsRef.current = props;
 	return useRefEffect( ( element ) => {
-		function _onPaste( event ) {
+		async function _onPaste( event ) {
 			const {
 				isSelected,
 				disableFormats,
@@ -78,7 +78,7 @@ export function usePasteHandler( props ) {
 				// eslint-disable-next-line no-console
 				window.console.log( 'Received items:\n\n', files );
 
-				const fromTransforms = getBlockTransforms( 'from' );
+				const fromTransforms = await getBlockTransforms( 'from' );
 				const blocks = files
 					.reduce( ( accumulator, file ) => {
 						const transformation = findTransform(
@@ -127,7 +127,7 @@ export function usePasteHandler( props ) {
 				mode = 'BLOCKS';
 			}
 
-			const content = pasteHandler( {
+			const content = await pasteHandler( {
 				HTML: html,
 				plainText,
 				mode,
diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js
index c49af2f80fca2..3d5a608f5a07b 100644
--- a/packages/block-editor/src/components/use-on-block-drop/index.js
+++ b/packages/block-editor/src/components/use-on-block-drop/index.js
@@ -149,13 +149,13 @@ export function onFilesDrop(
 	canInsertBlockType,
 	insertOrReplaceBlocks
 ) {
-	return ( files ) => {
+	return async ( files ) => {
 		if ( ! getSettings().mediaUpload ) {
 			return;
 		}
 
 		const transformation = findTransform(
-			getBlockTransforms( 'from' ),
+			await getBlockTransforms( 'from' ),
 			( transform ) =>
 				transform.type === 'files' &&
 				canInsertBlockType( transform.blockName, targetRootClientId ) &&
@@ -186,8 +186,8 @@ export function onHTMLDrop(
 	targetBlockIndex,
 	insertOrReplaceBlocks
 ) {
-	return ( HTML ) => {
-		const blocks = pasteHandler( { HTML, mode: 'BLOCKS' } );
+	return async ( HTML ) => {
+		const blocks = await pasteHandler( { HTML, mode: 'BLOCKS' } );
 
 		if ( blocks.length ) {
 			insertOrReplaceBlocks( blocks );
diff --git a/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js b/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js
index 5b78d2f8656b6..f8a2652b694b4 100644
--- a/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js
+++ b/packages/block-editor/src/components/writing-flow/use-clipboard-handler.js
@@ -46,7 +46,7 @@ export default function useClipboardHandler() {
 	const notifyCopy = useNotifyCopy();
 
 	return useRefEffect( ( node ) => {
-		function handler( event ) {
+		async function handler( event ) {
 			if ( event.defaultPrevented ) {
 				// This was likely already handled in rich-text/use-paste-handler.js.
 				return;
@@ -157,7 +157,7 @@ export default function useClipboardHandler() {
 				let blocks = [];
 
 				if ( files.length ) {
-					const fromTransforms = getBlockTransforms( 'from' );
+					const fromTransforms = await getBlockTransforms( 'from' );
 					blocks = files
 						.reduce( ( accumulator, file ) => {
 							const transformation = findTransform(
@@ -175,7 +175,7 @@ export default function useClipboardHandler() {
 						}, [] )
 						.flat();
 				} else {
-					blocks = pasteHandler( {
+					blocks = await pasteHandler( {
 						HTML: html,
 						plainText,
 						mode: 'BLOCKS',
diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js
index b21436161cb8c..3df8eddaf5b07 100644
--- a/packages/block-editor/src/store/actions.js
+++ b/packages/block-editor/src/store/actions.js
@@ -703,7 +703,7 @@ export const synchronizeTemplate =
  */
 export const __unstableDeleteSelection =
 	( isForward ) =>
-	( { registry, select, dispatch } ) => {
+	async ( { registry, select, dispatch } ) => {
 		const selectionAnchor = select.getSelectionStart();
 		const selectionFocus = select.getSelectionEnd();
 
@@ -784,7 +784,10 @@ export const __unstableDeleteSelection =
 		const blocksWithTheSameType =
 			blockA.name === blockB.name
 				? [ followingBlock ]
-				: switchToBlockType( followingBlock, targetBlockType.name );
+				: await switchToBlockType(
+						followingBlock,
+						targetBlockType.name
+				  );
 
 		// If the block types can not match, do nothing
 		if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
@@ -957,7 +960,7 @@ export const __unstableExpandSelection =
  */
 export const mergeBlocks =
 	( firstBlockClientId, secondBlockClientId ) =>
-	( { registry, select, dispatch } ) => {
+	async ( { registry, select, dispatch } ) => {
 		const blocks = [ firstBlockClientId, secondBlockClientId ];
 		dispatch( { type: 'MERGE_BLOCKS', blocks } );
 
@@ -975,7 +978,7 @@ export const mergeBlocks =
 		) {
 			// If there's no merge function defined, attempt merging inner
 			// blocks.
-			const blocksWithTheSameType = switchToBlockType(
+			const blocksWithTheSameType = await switchToBlockType(
 				blockB,
 				blockAType.name
 			);
@@ -1112,7 +1115,7 @@ export const mergeBlocks =
 		const blocksWithTheSameType =
 			blockA.name === blockB.name
 				? [ cloneB ]
-				: switchToBlockType( cloneB, blockA.name );
+				: await switchToBlockType( cloneB, blockA.name );
 
 		// If the block types can not match, do nothing.
 		if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) {
diff --git a/packages/block-editor/src/store/index.js b/packages/block-editor/src/store/index.js
index 0bcc00cb5f6ae..fc999278ed68c 100644
--- a/packages/block-editor/src/store/index.js
+++ b/packages/block-editor/src/store/index.js
@@ -11,6 +11,7 @@ import * as selectors from './selectors';
 import * as privateActions from './private-actions';
 import * as privateSelectors from './private-selectors';
 import * as actions from './actions';
+import * as resolvers from './resolvers';
 import { STORE_NAME } from './constants';
 import { unlock } from '../lock-unlock';
 
@@ -23,6 +24,7 @@ export const storeConfig = {
 	reducer,
 	selectors,
 	actions,
+	resolvers,
 };
 
 /**
diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js
index 5319a3b255365..6572277217eb0 100644
--- a/packages/block-editor/src/store/reducer.js
+++ b/packages/block-editor/src/store/reducer.js
@@ -1991,6 +1991,17 @@ export function lastFocus( state = false, action ) {
 	return state;
 }
 
+export function parsedPatterns( state = {}, action ) {
+	switch ( action.type ) {
+		case 'SET_PARSED_PATTERN':
+			return {
+				...state,
+				[ action.patternName ]: action.parsedPattern,
+			};
+	}
+	return state;
+}
+
 const combinedReducers = combineReducers( {
 	blocks,
 	isTyping,
@@ -2020,6 +2031,7 @@ const combinedReducers = combineReducers( {
 	blockRemovalRules,
 	openedBlockSettingsMenu,
 	registeredInserterMediaCategories,
+	parsedPatterns,
 } );
 
 function withAutomaticChangeReset( reducer ) {
diff --git a/packages/block-editor/src/store/resolvers.js b/packages/block-editor/src/store/resolvers.js
new file mode 100644
index 0000000000000..f072b6689706a
--- /dev/null
+++ b/packages/block-editor/src/store/resolvers.js
@@ -0,0 +1,69 @@
+/**
+ * WordPress dependencies
+ */
+import { parse } from '@wordpress/blocks';
+
+export const __experimentalGetParsedPattern =
+	( patternName ) =>
+	async ( { select, dispatch } ) => {
+		const patterns = select(
+			( state ) => state.root.settings.__experimentalBlockPatterns
+		);
+		const unsyncedPatterns = select.getUnsyncedPatterns();
+
+		const pattern = [ ...patterns, ...unsyncedPatterns ].find(
+			( { name } ) => name === patternName
+		);
+
+		if ( ! pattern ) {
+			return;
+		}
+
+		const blocks = await parse( pattern.content, {
+			__unstableSkipMigrationLogs: true,
+		} );
+
+		const parsedPattern = { ...pattern, blocks };
+
+		dispatch( {
+			type: 'SET_PARSED_PATTERN',
+			patternName: pattern.name,
+			parsedPattern,
+		} );
+	};
+
+export const __experimentalGetAllowedPatterns =
+	() =>
+	async ( { select, dispatch } ) => {
+		const patterns = select(
+			( state ) => state.root.settings.__experimentalBlockPatterns
+		);
+		const unsyncedPatterns = select.getUnsyncedPatterns();
+
+		const inserterPatterns = [ ...patterns, ...unsyncedPatterns ].filter(
+			( { inserter = true } ) => inserter
+		);
+
+		for ( const pattern of inserterPatterns ) {
+			const blocks = await parse( pattern.content, {
+				__unstableSkipMigrationLogs: true,
+			} );
+
+			const parsedPattern = { ...pattern, blocks };
+
+			dispatch( {
+				type: 'SET_PARSED_PATTERN',
+				patternName: pattern.name,
+				parsedPattern,
+			} );
+		}
+	};
+
+__experimentalGetAllowedPatterns.shouldInvalidate = ( action ) => {
+	return (
+		action.type === 'UPDATE_SETTINGS' &&
+		action.settings.__experimentalBlockPatterns
+	);
+};
+
+export const getPatternsByBlockTypes = __experimentalGetAllowedPatterns;
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index b6d455333c7a5..3eb7ccd25866a 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -8,10 +8,10 @@ import createSelector from 'rememo';
  */
 import {
 	getBlockType,
-	getBlockTypes,
+	getBootstrappedBlockType,
+	getBootstrappedBlockTypes,
 	getBlockVariations,
 	hasBlockSupport,
-	getPossibleBlockTransformations,
 	parse,
 	switchToBlockType,
 	store as blocksStore,
@@ -1508,7 +1508,7 @@ const canInsertBlockTypeUnmemoized = (
 		blockType = blockName;
 		blockName = blockType.name;
 	} else {
-		blockType = getBlockType( blockName );
+		blockType = getBootstrappedBlockType( blockName );
 	}
 	if ( ! blockType ) {
 		return false;
@@ -1975,7 +1975,7 @@ export const getInserterItems = createSelector(
 			buildScope: 'inserter',
 		} );
 
-		const blockTypeInserterItems = getBlockTypes()
+		const blockTypeInserterItems = getBootstrappedBlockTypes()
 			.filter( ( blockType ) =>
 				canIncludeBlockTypeInInserter( state, blockType, rootClientId )
 			)
@@ -1987,10 +1987,9 @@ export const getInserterItems = createSelector(
 			if ( ! variations.some( ( { isDefault } ) => isDefault ) ) {
 				accumulator.push( item );
 			}
-			if ( variations.length ) {
-				const variationMapper = getItemFromVariation( state, item );
-				accumulator.push( ...variations.map( variationMapper ) );
-			}
+			accumulator.push(
+				...variations.map( getItemFromVariation( state, item ) )
+			);
 			return accumulator;
 		}, [] );
 
@@ -1999,18 +1998,10 @@ export const getInserterItems = createSelector(
 		// the core blocks (usually by using the `init` action),
 		// thus affecting the display order.
 		// We don't sort reusable blocks as they are handled differently.
-		const groupByType = ( blocks, block ) => {
-			const { core, noncore } = blocks;
-			const type = block.name.startsWith( 'core/' ) ? core : noncore;
-
-			type.push( block );
-			return blocks;
-		};
-		const { core: coreItems, noncore: nonCoreItems } = items.reduce(
-			groupByType,
-			{ core: [], noncore: [] }
+		const sortedBlockTypes = orderBy(
+			items,
+			( block ) => ! block.name.startsWith( 'core/' ) // false < true
 		);
-		const sortedBlockTypes = [ ...coreItems, ...nonCoreItems ];
 		return [ ...sortedBlockTypes, ...syncedPatternInserterItems ];
 	},
 	( state, rootClientId ) => [
@@ -2021,7 +2012,7 @@ export const getInserterItems = createSelector(
 		state.settings.allowedBlockTypes,
 		state.settings.templateLock,
 		getReusableBlocks( state ),
-		getBlockTypes(),
+		getBootstrappedBlockTypes(),
 	]
 );
 
@@ -2052,37 +2043,18 @@ export const getInserterItems = createSelector(
  * @property {number}          frecency     Heuristic that combines frequency and recency.
  */
 export const getBlockTransformItems = createSelector(
-	( state, blocks, rootClientId = null ) => {
-		const normalizedBlocks = Array.isArray( blocks ) ? blocks : [ blocks ];
+	( state, rootClientId = null ) => {
 		const buildBlockTypeTransformItem = buildBlockTypeItem( state, {
 			buildScope: 'transform',
 		} );
-		const blockTypeTransformItems = getBlockTypes()
+		const blockTypeTransformItems = getBootstrappedBlockTypes()
 			.filter( ( blockType ) =>
 				canIncludeBlockTypeInInserter( state, blockType, rootClientId )
 			)
-			.map( buildBlockTypeTransformItem );
-
-		const itemsByName = Object.fromEntries(
-			Object.entries( blockTypeTransformItems ).map( ( [ , value ] ) => [
-				value.name,
-				value,
-			] )
-		);
+			.map( buildBlockTypeTransformItem )
+			.map( ( value ) => [ value.name, value ] );
 
-		const possibleTransforms = getPossibleBlockTransformations(
-			normalizedBlocks
-		).reduce( ( accumulator, block ) => {
-			if ( itemsByName[ block?.name ] ) {
-				accumulator.push( itemsByName[ block.name ] );
-			}
-			return accumulator;
-		}, [] );
-		return orderBy(
-			possibleTransforms,
-			( block ) => itemsByName[ block.name ].frecency,
-			'desc'
-		);
+		return Object.fromEntries( blockTypeTransformItems );
 	},
 	( state, blocks, rootClientId ) => [
 		state.blockListSettings[ rootClientId ],
@@ -2090,7 +2062,7 @@ export const getBlockTransformItems = createSelector(
 		state.preferences.insertUsage,
 		state.settings.allowedBlockTypes,
 		state.settings.templateLock,
-		getBlockTypes(),
+		getBootstrappedBlockTypes(),
 	]
 );
 
@@ -2102,29 +2074,21 @@ export const getBlockTransformItems = createSelector(
  *
  * @return {boolean} Items that appear in inserter.
  */
-export const hasInserterItems = createSelector(
-	( state, rootClientId = null ) => {
-		const hasBlockType = getBlockTypes().some( ( blockType ) =>
-			canIncludeBlockTypeInInserter( state, blockType, rootClientId )
-		);
-		if ( hasBlockType ) {
-			return true;
-		}
-		const hasReusableBlock =
-			canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId ) &&
-			getReusableBlocks( state ).length > 0;
+export const hasInserterItems = ( state, rootClientId = null ) => {
+	const hasBlockType = getBootstrappedBlockTypes().some( ( blockType ) =>
+		canIncludeBlockTypeInInserter( state, blockType, rootClientId )
+	);
 
-		return hasReusableBlock;
-	},
-	( state, rootClientId ) => [
-		state.blockListSettings[ rootClientId ],
-		state.blocks.byClientId,
-		state.settings.allowedBlockTypes,
-		state.settings.templateLock,
-		getReusableBlocks( state ),
-		getBlockTypes(),
-	]
-);
+	if ( hasBlockType ) {
+		return true;
+	}
+
+	const hasReusableBlock =
+		canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId ) &&
+		getReusableBlocks( state ).length > 0;
+
+	return hasReusableBlock;
+};
 
 /**
  * Returns the list of allowed inserter blocks for inner blocks children.
@@ -2140,9 +2104,10 @@ export const getAllowedBlocks = createSelector(
 			return;
 		}
 
-		const blockTypes = getBlockTypes().filter( ( blockType ) =>
+		const blockTypes = getBootstrappedBlockTypes().filter( ( blockType ) =>
 			canIncludeBlockTypeInInserter( state, blockType, rootClientId )
 		);
+
 		const hasReusableBlock =
 			canInsertBlockTypeUnmemoized( state, 'core/block', rootClientId ) &&
 			getReusableBlocks( state ).length > 0;
@@ -2158,7 +2123,7 @@ export const getAllowedBlocks = createSelector(
 		state.settings.allowedBlockTypes,
 		state.settings.templateLock,
 		getReusableBlocks( state ),
-		getBlockTypes(),
+		getBootstrappedBlockTypes(),
 	]
 );
 
@@ -2321,6 +2286,7 @@ export const __experimentalGetAllowedPatterns = createSelector(
 		state.settings.templateLock,
 		state.blockListSettings[ rootClientId ],
 		state.blocks.byClientId.get( rootClientId ),
+		state.parsedPatterns,
 	]
 );
 
@@ -2344,6 +2310,11 @@ export const getPatternsByBlockTypes = createSelector(
 			state,
 			rootClientId
 		);
+
+		if ( ! patterns ) {
+			return null;
+		}
+
 		const normalizedBlockNames = Array.isArray( blockNames )
 			? blockNames
 			: [ blockNames ];
diff --git a/packages/block-library/src/archives/block.json b/packages/block-library/src/archives/block.json
index 7e0f5181d2c3d..5e8d4c4ce992f 100644
--- a/packages/block-library/src/archives/block.json
+++ b/packages/block-library/src/archives/block.json
@@ -49,5 +49,6 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-archives-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/audio/block.json b/packages/block-library/src/audio/block.json
index 04df268a74a63..901edf1fe03d5 100644
--- a/packages/block-library/src/audio/block.json
+++ b/packages/block-library/src/audio/block.json
@@ -56,6 +56,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-audio-editor",
-	"style": "wp-block-audio"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/avatar/block.json b/packages/block-library/src/avatar/block.json
index fa86541b2963f..1cf3b2deb5fc4 100644
--- a/packages/block-library/src/avatar/block.json
+++ b/packages/block-library/src/avatar/block.json
@@ -55,6 +55,7 @@
 	"selectors": {
 		"border": ".wp-block-avatar img"
 	},
-	"editorStyle": "wp-block-avatar-editor",
-	"style": "wp-block-avatar"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/block/block.json b/packages/block-library/src/block/block.json
index aeccdbfc1051d..f6f5aee72a502 100644
--- a/packages/block-library/src/block/block.json
+++ b/packages/block-library/src/block/block.json
@@ -15,7 +15,9 @@
 	"supports": {
 		"customClassName": false,
 		"html": false,
-		"inserter": false,
-		"renaming": false
-	}
+	"inserter": false,
+	"renaming": false
+},
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json
index 3c232700a876e..c758617d47425 100644
--- a/packages/block-library/src/button/block.json
+++ b/packages/block-library/src/button/block.json
@@ -124,6 +124,7 @@
 		{ "name": "fill", "label": "Fill", "isDefault": true },
 		{ "name": "outline", "label": "Outline" }
 	],
-	"editorStyle": "wp-block-button-editor",
-	"style": "wp-block-button"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/buttons/block.json b/packages/block-library/src/buttons/block.json
index 4dc420bd41885..8f47da58f2196 100644
--- a/packages/block-library/src/buttons/block.json
+++ b/packages/block-library/src/buttons/block.json
@@ -40,6 +40,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-buttons-editor",
-	"style": "wp-block-buttons"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/calendar/block.json b/packages/block-library/src/calendar/block.json
index 974d47ff8ec2c..4b7b1eb56aff4 100644
--- a/packages/block-library/src/calendar/block.json
+++ b/packages/block-library/src/calendar/block.json
@@ -39,5 +39,7 @@
 			}
 		}
 	},
-	"style": "wp-block-calendar"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/categories/block.json b/packages/block-library/src/categories/block.json
index 5014da8298049..4a93be19f82dc 100644
--- a/packages/block-library/src/categories/block.json
+++ b/packages/block-library/src/categories/block.json
@@ -53,6 +53,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-categories-editor",
-	"style": "wp-block-categories"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/code/block.json b/packages/block-library/src/code/block.json
index bd5db3c918b96..5c9fda719d241 100644
--- a/packages/block-library/src/code/block.json
+++ b/packages/block-library/src/code/block.json
@@ -58,5 +58,7 @@
 			}
 		}
 	},
-	"style": "wp-block-code"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/column/block.json b/packages/block-library/src/column/block.json
index 7f61f307fab15..ce1d1e3a55190 100644
--- a/packages/block-library/src/column/block.json
+++ b/packages/block-library/src/column/block.json
@@ -69,5 +69,6 @@
 			}
 		},
 		"layout": true
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/columns/block.json b/packages/block-library/src/columns/block.json
index dff9c45ae2dde..7386411001a99 100644
--- a/packages/block-library/src/columns/block.json
+++ b/packages/block-library/src/columns/block.json
@@ -80,6 +80,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-columns-editor",
-	"style": "wp-block-columns"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/comment-author-avatar/block.json b/packages/block-library/src/comment-author-avatar/block.json
index 050d87c3b4634..cb288b97d72a4 100644
--- a/packages/block-library/src/comment-author-avatar/block.json
+++ b/packages/block-library/src/comment-author-avatar/block.json
@@ -40,5 +40,7 @@
 			"margin": true,
 			"padding": true
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/comment-author-name/block.json b/packages/block-library/src/comment-author-name/block.json
index 93350779fc8bd..e6e2b7379b43d 100644
--- a/packages/block-library/src/comment-author-name/block.json
+++ b/packages/block-library/src/comment-author-name/block.json
@@ -49,5 +49,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comment-content/block.json b/packages/block-library/src/comment-content/block.json
index 9ac4b5453365b..a42ac9f646813 100644
--- a/packages/block-library/src/comment-content/block.json
+++ b/packages/block-library/src/comment-content/block.json
@@ -42,5 +42,8 @@
 			}
 		},
 		"html": false
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/comment-date/block.json b/packages/block-library/src/comment-date/block.json
index 7e4776c68ff2f..a16c74e0afeb0 100644
--- a/packages/block-library/src/comment-date/block.json
+++ b/packages/block-library/src/comment-date/block.json
@@ -45,5 +45,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comment-edit-link/block.json b/packages/block-library/src/comment-edit-link/block.json
index 505305f60987a..844bc43b53811 100644
--- a/packages/block-library/src/comment-edit-link/block.json
+++ b/packages/block-library/src/comment-edit-link/block.json
@@ -45,5 +45,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comment-reply-link/block.json b/packages/block-library/src/comment-reply-link/block.json
index c10129412145c..6accfffa4b70a 100644
--- a/packages/block-library/src/comment-reply-link/block.json
+++ b/packages/block-library/src/comment-reply-link/block.json
@@ -41,5 +41,6 @@
 			}
 		},
 		"html": false
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comment-template/block.json b/packages/block-library/src/comment-template/block.json
index 7b9bfc5e0340f..59129c0efda74 100644
--- a/packages/block-library/src/comment-template/block.json
+++ b/packages/block-library/src/comment-template/block.json
@@ -30,5 +30,7 @@
 			}
 		}
 	},
-	"style": "wp-block-comment-template"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/comments-pagination-next/block.json b/packages/block-library/src/comments-pagination-next/block.json
index d619865ebd6f5..3328a2f55fc66 100644
--- a/packages/block-library/src/comments-pagination-next/block.json
+++ b/packages/block-library/src/comments-pagination-next/block.json
@@ -36,5 +36,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comments-pagination-numbers/block.json b/packages/block-library/src/comments-pagination-numbers/block.json
index fcebb52763870..fe4d86e83e99d 100644
--- a/packages/block-library/src/comments-pagination-numbers/block.json
+++ b/packages/block-library/src/comments-pagination-numbers/block.json
@@ -31,5 +31,7 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/comments-pagination-previous/block.json b/packages/block-library/src/comments-pagination-previous/block.json
index 2dab1e9dd7367..da9e127b4cbb7 100644
--- a/packages/block-library/src/comments-pagination-previous/block.json
+++ b/packages/block-library/src/comments-pagination-previous/block.json
@@ -36,5 +36,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/comments-pagination/block.json b/packages/block-library/src/comments-pagination/block.json
index a11decd201e94..5f9043a7eb450 100644
--- a/packages/block-library/src/comments-pagination/block.json
+++ b/packages/block-library/src/comments-pagination/block.json
@@ -50,6 +50,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-comments-pagination-editor",
-	"style": "wp-block-comments-pagination"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/comments-title/block.json b/packages/block-library/src/comments-title/block.json
index 4107f5d590cde..796f9f96c4e07 100644
--- a/packages/block-library/src/comments-title/block.json
+++ b/packages/block-library/src/comments-title/block.json
@@ -62,5 +62,7 @@
 				"__experimentalFontWeight": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/comments/block.json b/packages/block-library/src/comments/block.json
index b35ea3505c816..3f6af4aef1156 100644
--- a/packages/block-library/src/comments/block.json
+++ b/packages/block-library/src/comments/block.json
@@ -47,6 +47,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-comments-editor",
-	"usesContext": [ "postId", "postType" ]
+	"usesContext": [ "postId", "postType" ],
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/cover/block.json b/packages/block-library/src/cover/block.json
index d2c55dd26b4d7..78a1914f528ad 100644
--- a/packages/block-library/src/cover/block.json
+++ b/packages/block-library/src/cover/block.json
@@ -131,6 +131,7 @@
 			"allowJustification": false
 		}
 	},
-	"editorStyle": "wp-block-cover-editor",
-	"style": "wp-block-cover"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/details/block.json b/packages/block-library/src/details/block.json
index a71d3af2a5ed3..4d74f629a2da0 100644
--- a/packages/block-library/src/details/block.json
+++ b/packages/block-library/src/details/block.json
@@ -60,6 +60,7 @@
 			"allowEditing": false
 		}
 	},
-	"editorStyle": "wp-block-details-editor",
-	"style": "wp-block-details"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/embed/block.json b/packages/block-library/src/embed/block.json
index 5aac8bbd6b8ca..eb0c6b6e499be 100644
--- a/packages/block-library/src/embed/block.json
+++ b/packages/block-library/src/embed/block.json
@@ -46,6 +46,7 @@
 			"margin": true
 		}
 	},
-	"editorStyle": "wp-block-embed-editor",
-	"style": "wp-block-embed"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/file/block.json b/packages/block-library/src/file/block.json
index 9dc6677e4adce..f18c47f875c70 100644
--- a/packages/block-library/src/file/block.json
+++ b/packages/block-library/src/file/block.json
@@ -73,6 +73,7 @@
 		"interactivity": true
 	},
 	"viewScript": "file:./view.min.js",
-	"editorStyle": "wp-block-file-editor",
-	"style": "wp-block-file"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:/style.css"
 }
diff --git a/packages/block-library/src/footnotes/block.json b/packages/block-library/src/footnotes/block.json
index 3192df7796978..018933d991313 100644
--- a/packages/block-library/src/footnotes/block.json
+++ b/packages/block-library/src/footnotes/block.json
@@ -57,5 +57,7 @@
 			}
 		}
 	},
-	"style": "wp-block-footnotes"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/freeform/block.json b/packages/block-library/src/freeform/block.json
index d40e8ea13dc11..42257f88be075 100644
--- a/packages/block-library/src/freeform/block.json
+++ b/packages/block-library/src/freeform/block.json
@@ -17,5 +17,6 @@
 		"customClassName": false,
 		"reusable": false
 	},
-	"editorStyle": "wp-block-freeform-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json
index fad92aed59bf7..f2e14a116058f 100644
--- a/packages/block-library/src/gallery/block.json
+++ b/packages/block-library/src/gallery/block.json
@@ -134,6 +134,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-gallery-editor",
-	"style": "wp-block-gallery"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json
index 92bbc1b0d1135..9c4e73acce23d 100644
--- a/packages/block-library/src/group/block.json
+++ b/packages/block-library/src/group/block.json
@@ -85,6 +85,7 @@
 			"allowSizingOnChildren": true
 		}
 	},
-	"editorStyle": "wp-block-group-editor",
-	"style": "wp-block-group"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json
index 72cc67caddd9e..bac67ab9489c8 100644
--- a/packages/block-library/src/heading/block.json
+++ b/packages/block-library/src/heading/block.json
@@ -62,6 +62,7 @@
 		"__unstablePasteTextInline": true,
 		"__experimentalSlashInserter": true
 	},
-	"editorStyle": "wp-block-heading-editor",
-	"style": "wp-block-heading"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/home-link/block.json b/packages/block-library/src/home-link/block.json
index a9827b7718b9b..8eaa42c989fbb 100644
--- a/packages/block-library/src/home-link/block.json
+++ b/packages/block-library/src/home-link/block.json
@@ -38,6 +38,5 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-home-link-editor",
-	"style": "wp-block-home-link"
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/html/block.json b/packages/block-library/src/html/block.json
index b1a2ad60625d1..4c4b010624ae0 100644
--- a/packages/block-library/src/html/block.json
+++ b/packages/block-library/src/html/block.json
@@ -18,5 +18,6 @@
 		"className": false,
 		"html": false
 	},
-	"editorStyle": "wp-block-html-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json
index c5191e3dd8654..806f1fb5341a8 100644
--- a/packages/block-library/src/image/block.json
+++ b/packages/block-library/src/image/block.json
@@ -128,7 +128,8 @@
 		},
 		{ "name": "rounded", "label": "Rounded" }
 	],
-	"editorStyle": "wp-block-image-editor",
-	"style": "wp-block-image",
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css",
 	"viewScript": "file:./view.min.js"
 }
diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js
index b74079b2b8b79..bcbe08538a26c 100644
--- a/packages/block-library/src/image/image.js
+++ b/packages/block-library/src/image/image.js
@@ -324,10 +324,10 @@ export default function Image( {
 	const canEditImage = id && naturalWidth && naturalHeight && imageEditing;
 	const allowCrop = ! multiImageSelection && canEditImage && ! isEditingImage;
 
-	function switchToCover() {
+	async function switchToCover() {
 		replaceBlocks(
 			clientId,
-			switchToBlockType( getBlock( clientId ), 'core/cover' )
+			await switchToBlockType( getBlock( clientId ), 'core/cover' )
 		);
 	}
 
diff --git a/packages/block-library/src/latest-comments/block.json b/packages/block-library/src/latest-comments/block.json
index 0b213e9b7903a..23dc1038abd7e 100644
--- a/packages/block-library/src/latest-comments/block.json
+++ b/packages/block-library/src/latest-comments/block.json
@@ -48,6 +48,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-latest-comments-editor",
-	"style": "wp-block-latest-comments"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/latest-posts/block.json b/packages/block-library/src/latest-posts/block.json
index f36164614dd50..83cae6049bb48 100644
--- a/packages/block-library/src/latest-posts/block.json
+++ b/packages/block-library/src/latest-posts/block.json
@@ -112,6 +112,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-latest-posts-editor",
-	"style": "wp-block-latest-posts"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/list-item/block.json b/packages/block-library/src/list-item/block.json
index 06997c2ac23f8..ba7dbb5595f4b 100644
--- a/packages/block-library/src/list-item/block.json
+++ b/packages/block-library/src/list-item/block.json
@@ -42,5 +42,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/list-item/edit.js b/packages/block-library/src/list-item/edit.js
index 7733a76280752..d31f26555eefc 100644
--- a/packages/block-library/src/list-item/edit.js
+++ b/packages/block-library/src/list-item/edit.js
@@ -106,9 +106,9 @@ export default function ListItemEdit( {
 					onMerge={ onMerge }
 					onReplace={
 						onReplace
-							? ( blocks, ...args ) => {
+							? async ( blocks, ...args ) => {
 									onReplace(
-										convertToListItems( blocks ),
+										await convertToListItems( blocks ),
 										...args
 									);
 							  }
diff --git a/packages/block-library/src/list-item/edit.native.js b/packages/block-library/src/list-item/edit.native.js
index cf2e77c08d2e8..b8b3690a41fa8 100644
--- a/packages/block-library/src/list-item/edit.native.js
+++ b/packages/block-library/src/list-item/edit.native.js
@@ -130,12 +130,12 @@ export default function ListItemEdit( {
 		[ clientId, onSplit ]
 	);
 	const onReplaceList = useCallback(
-		( blocks, ...args ) => {
+		async ( blocks, ...args ) => {
 			if ( ! preventDefault.current ) {
-				onReplace( convertToListItems( blocks ), ...args );
+				onReplace( await convertToListItems( blocks ), ...args );
 			}
 		},
-		[ clientId, onReplace, convertToListItems ]
+		[ onReplace ]
 	);
 	const onLayout = useCallback( ( { nativeEvent } ) => {
 		setContentWidth( ( prevState ) => {
diff --git a/packages/block-library/src/list-item/utils.js b/packages/block-library/src/list-item/utils.js
index 5e5b51a8af680..29252fb4f2a12 100644
--- a/packages/block-library/src/list-item/utils.js
+++ b/packages/block-library/src/list-item/utils.js
@@ -3,19 +3,19 @@
  */
 import { switchToBlockType } from '@wordpress/blocks';
 
-function convertBlockToList( block ) {
-	const list = switchToBlockType( block, 'core/list' );
+async function convertBlockToList( block ) {
+	const list = await switchToBlockType( block, 'core/list' );
 	if ( list ) {
 		return list;
 	}
-	const paragraph = switchToBlockType( block, 'core/paragraph' );
+	const paragraph = await switchToBlockType( block, 'core/paragraph' );
 	if ( ! paragraph ) {
 		return null;
 	}
-	return switchToBlockType( paragraph, 'core/list' );
+	return await switchToBlockType( paragraph, 'core/list' );
 }
 
-export function convertToListItems( blocks ) {
+export async function convertToListItems( blocks ) {
 	const listItems = [];
 
 	for ( let block of blocks ) {
@@ -23,7 +23,7 @@ export function convertToListItems( blocks ) {
 			listItems.push( block );
 		} else if ( block.name === 'core/list' ) {
 			listItems.push( ...block.innerBlocks );
-		} else if ( ( block = convertBlockToList( block ) ) ) {
+		} else if ( ( block = await convertBlockToList( block ) ) ) {
 			for ( const { innerBlocks } of block ) {
 				listItems.push( ...innerBlocks );
 			}
diff --git a/packages/block-library/src/list/block.json b/packages/block-library/src/list/block.json
index e2fb9e4c9e3b0..e3b2f055fe719 100644
--- a/packages/block-library/src/list/block.json
+++ b/packages/block-library/src/list/block.json
@@ -72,6 +72,7 @@
 		"__experimentalOnMerge": true,
 		"__experimentalSlashInserter": true
 	},
-	"editorStyle": "wp-block-list-editor",
-	"style": "wp-block-list"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/loginout/block.json b/packages/block-library/src/loginout/block.json
index 59fceec596e37..178ab12544ee3 100644
--- a/packages/block-library/src/loginout/block.json
+++ b/packages/block-library/src/loginout/block.json
@@ -40,5 +40,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/media-text/block.json b/packages/block-library/src/media-text/block.json
index cdeb4ce13e8f5..227417cd5723b 100644
--- a/packages/block-library/src/media-text/block.json
+++ b/packages/block-library/src/media-text/block.json
@@ -125,6 +125,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-media-text-editor",
-	"style": "wp-block-media-text"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/missing/block.json b/packages/block-library/src/missing/block.json
index 242a1d2c6b21a..0aef889ad0f33 100644
--- a/packages/block-library/src/missing/block.json
+++ b/packages/block-library/src/missing/block.json
@@ -24,5 +24,6 @@
 		"inserter": false,
 		"html": false,
 		"reusable": false
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/more/block.json b/packages/block-library/src/more/block.json
index bfd95652ea176..5e4c115da9bd9 100644
--- a/packages/block-library/src/more/block.json
+++ b/packages/block-library/src/more/block.json
@@ -22,5 +22,6 @@
 		"html": false,
 		"multiple": false
 	},
-	"editorStyle": "wp-block-more-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/navigation-link/block.json b/packages/block-library/src/navigation-link/block.json
index d8f2fe31aef9d..352c4f8a21c97 100644
--- a/packages/block-library/src/navigation-link/block.json
+++ b/packages/block-library/src/navigation-link/block.json
@@ -74,6 +74,7 @@
 		},
 		"renaming": false
 	},
-	"editorStyle": "wp-block-navigation-link-editor",
-	"style": "wp-block-navigation-link"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/navigation-link/link-ui.js b/packages/block-library/src/navigation-link/link-ui.js
index 3c3d91e7b0a05..3b8259e1fb7c1 100644
--- a/packages/block-library/src/navigation-link/link-ui.js
+++ b/packages/block-library/src/navigation-link/link-ui.js
@@ -113,10 +113,10 @@ function LinkControlTransforms( { clientId } ) {
 					return (
 						<Button
 							key={ `transform-${ index }` }
-							onClick={ () =>
+							onClick={ async () =>
 								replaceBlock(
 									clientId,
-									switchToBlockType(
+									await switchToBlockType(
 										getBlock( clientId ),
 										item.name
 									)
diff --git a/packages/block-library/src/navigation-submenu/block.json b/packages/block-library/src/navigation-submenu/block.json
index 91364109ea740..17deaccf7220f 100644
--- a/packages/block-library/src/navigation-submenu/block.json
+++ b/packages/block-library/src/navigation-submenu/block.json
@@ -60,6 +60,6 @@
 		"reusable": false,
 		"html": false
 	},
-	"editorStyle": "wp-block-navigation-submenu-editor",
-	"style": "wp-block-navigation-submenu"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json
index 9ec919ae38d1f..bfc9bbd351398 100644
--- a/packages/block-library/src/navigation/block.json
+++ b/packages/block-library/src/navigation/block.json
@@ -137,6 +137,7 @@
 		"renaming": false
 	},
 	"viewScript": "file:./view.min.js",
-	"editorStyle": "wp-block-navigation-editor",
-	"style": "wp-block-navigation"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/nextpage/block.json b/packages/block-library/src/nextpage/block.json
index ab88d4a7be4f0..bfd9a4ca516b0 100644
--- a/packages/block-library/src/nextpage/block.json
+++ b/packages/block-library/src/nextpage/block.json
@@ -13,5 +13,6 @@
 		"className": false,
 		"html": false
 	},
-	"editorStyle": "wp-block-nextpage-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/page-list-item/block.json b/packages/block-library/src/page-list-item/block.json
index abd86924949ab..40ceb51e3bfb8 100644
--- a/packages/block-library/src/page-list-item/block.json
+++ b/packages/block-library/src/page-list-item/block.json
@@ -47,6 +47,5 @@
 		"inserter": false,
 		"__experimentalToolbar": false
 	},
-	"editorStyle": "wp-block-page-list-editor",
-	"style": "wp-block-page-list"
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/page-list/block.json b/packages/block-library/src/page-list/block.json
index 7f4f2ce86c425..d58b2bf8a070a 100644
--- a/packages/block-library/src/page-list/block.json
+++ b/packages/block-library/src/page-list/block.json
@@ -49,6 +49,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-page-list-editor",
-	"style": "wp-block-page-list"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js
index 8f21f781396c6..214e93d73a8f6 100644
--- a/packages/block-library/src/page-list/edit.js
+++ b/packages/block-library/src/page-list/edit.js
@@ -113,6 +113,31 @@ function BlockContent( {
 	}
 }
 
+function getBlockList( parentId, pagesByParentId ) {
+	const childPages = pagesByParentId.get( parentId );
+
+	if ( ! childPages?.length ) {
+		return [];
+	}
+
+	return childPages.map( ( page ) => {
+		const hasChildren = pagesByParentId.has( page.id );
+		const pageProps = {
+			id: page.id,
+			label:
+				// translators: displayed when a page has an empty title.
+				page.title?.rendered?.trim() !== ''
+					? page.title?.rendered
+					: __( '(no title)' ),
+			title: page.title?.rendered,
+			link: page.url,
+			hasChildren,
+		};
+		const children = getBlockList( page.id, pagesByParentId );
+		return createBlock( 'core/page-list-item', pageProps, children );
+	} );
+}
+
 export default function PageListEdit( {
 	context,
 	clientId,
@@ -209,39 +234,8 @@ export default function PageListEdit( {
 	);
 
 	const blockList = useMemo(
-		function getBlockList( parentId = parentPageID ) {
-			const childPages = pagesByParentId.get( parentId );
-
-			if ( ! childPages?.length ) {
-				return [];
-			}
-
-			return childPages.reduce( ( template, page ) => {
-				const hasChildren = pagesByParentId.has( page.id );
-				const pageProps = {
-					id: page.id,
-					label:
-						// translators: displayed when a page has an empty title.
-						page.title?.rendered?.trim() !== ''
-							? page.title?.rendered
-							: __( '(no title)' ),
-					title: page.title?.rendered,
-					link: page.url,
-					hasChildren,
-				};
-				let item = null;
-				const children = getBlockList( page.id );
-				item = createBlock(
-					'core/page-list-item',
-					pageProps,
-					children
-				);
-				template.push( item );
-
-				return template;
-			}, [] );
-		},
-		[ pagesByParentId, parentPageID ]
+		() => getBlockList( parentPageID, pagesByParentId ),
+		[ parentPageID, pagesByParentId ]
 	);
 
 	const {
diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json
index 3fe4fbb34e102..7aeb77fe8e9cd 100644
--- a/packages/block-library/src/paragraph/block.json
+++ b/packages/block-library/src/paragraph/block.json
@@ -67,6 +67,7 @@
 		"__experimentalSelector": "p",
 		"__unstablePasteTextInline": true
 	},
-	"editorStyle": "wp-block-paragraph-editor",
-	"style": "wp-block-paragraph"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/pattern/block.json b/packages/block-library/src/pattern/block.json
index da02f7b72747e..2770f99de1dfb 100644
--- a/packages/block-library/src/pattern/block.json
+++ b/packages/block-library/src/pattern/block.json
@@ -15,5 +15,6 @@
 		"slug": {
 			"type": "string"
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-author-biography/block.json b/packages/block-library/src/post-author-biography/block.json
index 5d7a4d4585747..6612f278f42a4 100644
--- a/packages/block-library/src/post-author-biography/block.json
+++ b/packages/block-library/src/post-author-biography/block.json
@@ -38,5 +38,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-author-name/block.json b/packages/block-library/src/post-author-name/block.json
index 89e4b38de2c28..a70ac2047bb09 100644
--- a/packages/block-library/src/post-author-name/block.json
+++ b/packages/block-library/src/post-author-name/block.json
@@ -48,5 +48,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-author/block.json b/packages/block-library/src/post-author/block.json
index 47dceef55604f..6d75ca1f08371 100644
--- a/packages/block-library/src/post-author/block.json
+++ b/packages/block-library/src/post-author/block.json
@@ -63,5 +63,6 @@
 			}
 		}
 	},
-	"style": "wp-block-post-author"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-comment/block.json b/packages/block-library/src/post-comment/block.json
index 85bdb7dd6cf32..9cae6b88750c8 100644
--- a/packages/block-library/src/post-comment/block.json
+++ b/packages/block-library/src/post-comment/block.json
@@ -18,5 +18,6 @@
 	"supports": {
 		"html": false,
 		"inserter": false
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-comments-count/block.json b/packages/block-library/src/post-comments-count/block.json
index 43c1aaf713776..ab78b46044848 100644
--- a/packages/block-library/src/post-comments-count/block.json
+++ b/packages/block-library/src/post-comments-count/block.json
@@ -39,5 +39,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-comments-form/block.json b/packages/block-library/src/post-comments-form/block.json
index ff157beb5ced9..ed04ce0cf4a41 100644
--- a/packages/block-library/src/post-comments-form/block.json
+++ b/packages/block-library/src/post-comments-form/block.json
@@ -39,10 +39,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-post-comments-form-editor",
-	"style": [
-		"wp-block-post-comments-form",
-		"wp-block-buttons",
-		"wp-block-button"
-	]
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": [ "file:./style.css", "wp-block-buttons", "wp-block-button" ]
 }
diff --git a/packages/block-library/src/post-comments-link/block.json b/packages/block-library/src/post-comments-link/block.json
index 74e268c3c20b1..7cbe667d1e64f 100644
--- a/packages/block-library/src/post-comments-link/block.json
+++ b/packages/block-library/src/post-comments-link/block.json
@@ -40,5 +40,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json
index b0e0487a0b824..c4b4fc5b896d8 100644
--- a/packages/block-library/src/post-content/block.json
+++ b/packages/block-library/src/post-content/block.json
@@ -39,5 +39,5 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-post-content-editor"
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/post-date/block.json b/packages/block-library/src/post-date/block.json
index 11ebc32d9cabe..1efddccc0baa6 100644
--- a/packages/block-library/src/post-date/block.json
+++ b/packages/block-library/src/post-date/block.json
@@ -51,5 +51,7 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-excerpt/block.json b/packages/block-library/src/post-excerpt/block.json
index 33b7818ebed9f..a38491c618d0a 100644
--- a/packages/block-library/src/post-excerpt/block.json
+++ b/packages/block-library/src/post-excerpt/block.json
@@ -52,6 +52,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-post-excerpt-editor",
-	"style": "wp-block-post-excerpt"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json
index 34e3bd6b2325f..2b62692bd2c10 100644
--- a/packages/block-library/src/post-featured-image/block.json
+++ b/packages/block-library/src/post-featured-image/block.json
@@ -79,6 +79,7 @@
 			"padding": true
 		}
 	},
-	"editorStyle": "wp-block-post-featured-image-editor",
-	"style": "wp-block-post-featured-image"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-navigation-link/block.json b/packages/block-library/src/post-navigation-link/block.json
index e1b6d4fa90a40..fa7467108d0c1 100644
--- a/packages/block-library/src/post-navigation-link/block.json
+++ b/packages/block-library/src/post-navigation-link/block.json
@@ -51,5 +51,6 @@
 			}
 		}
 	},
-	"style": "wp-block-post-navigation-link"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-template/block.json b/packages/block-library/src/post-template/block.json
index d2f7c09693121..d08d6aebaaf29 100644
--- a/packages/block-library/src/post-template/block.json
+++ b/packages/block-library/src/post-template/block.json
@@ -50,6 +50,7 @@
 			}
 		}
 	},
-	"style": "wp-block-post-template",
-	"editorStyle": "wp-block-post-template-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-terms/block.json b/packages/block-library/src/post-terms/block.json
index 0da7fb02f8134..f8a5108ae4857 100644
--- a/packages/block-library/src/post-terms/block.json
+++ b/packages/block-library/src/post-terms/block.json
@@ -56,5 +56,6 @@
 			}
 		}
 	},
-	"style": "wp-block-post-terms"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-time-to-read/block.json b/packages/block-library/src/post-time-to-read/block.json
index 281e9bb1f1b21..396748e5a28fa 100644
--- a/packages/block-library/src/post-time-to-read/block.json
+++ b/packages/block-library/src/post-time-to-read/block.json
@@ -43,5 +43,7 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/post-title/block.json b/packages/block-library/src/post-title/block.json
index 75a4fa3c3a60f..37e8cd9a52568 100644
--- a/packages/block-library/src/post-title/block.json
+++ b/packages/block-library/src/post-title/block.json
@@ -59,5 +59,6 @@
 			}
 		}
 	},
-	"style": "wp-block-post-title"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/preformatted/block.json b/packages/block-library/src/preformatted/block.json
index def870e7ad2fb..b8ab601764f81 100644
--- a/packages/block-library/src/preformatted/block.json
+++ b/packages/block-library/src/preformatted/block.json
@@ -42,5 +42,6 @@
 			}
 		}
 	},
-	"style": "wp-block-preformatted"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/pullquote/block.json b/packages/block-library/src/pullquote/block.json
index 7fc81d5683bd1..92f0b8645a6e3 100644
--- a/packages/block-library/src/pullquote/block.json
+++ b/packages/block-library/src/pullquote/block.json
@@ -67,6 +67,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-pullquote-editor",
-	"style": "wp-block-pullquote"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/query-no-results/block.json b/packages/block-library/src/query-no-results/block.json
index 32088752bb060..8656db22b4a90 100644
--- a/packages/block-library/src/query-no-results/block.json
+++ b/packages/block-library/src/query-no-results/block.json
@@ -29,5 +29,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/query-pagination-next/block.json b/packages/block-library/src/query-pagination-next/block.json
index 95b1169dc992f..de5c921abb605 100644
--- a/packages/block-library/src/query-pagination-next/block.json
+++ b/packages/block-library/src/query-pagination-next/block.json
@@ -42,5 +42,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/query-pagination-numbers/block.json b/packages/block-library/src/query-pagination-numbers/block.json
index f22d88115d68c..caba49d73800c 100644
--- a/packages/block-library/src/query-pagination-numbers/block.json
+++ b/packages/block-library/src/query-pagination-numbers/block.json
@@ -38,5 +38,6 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-query-pagination-numbers-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/query-pagination-previous/block.json b/packages/block-library/src/query-pagination-previous/block.json
index fbaac543c1da3..68a0912c2e49b 100644
--- a/packages/block-library/src/query-pagination-previous/block.json
+++ b/packages/block-library/src/query-pagination-previous/block.json
@@ -42,5 +42,6 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/query-pagination/block.json b/packages/block-library/src/query-pagination/block.json
index e32a9ba9b495f..db55e141ab4d3 100644
--- a/packages/block-library/src/query-pagination/block.json
+++ b/packages/block-library/src/query-pagination/block.json
@@ -56,6 +56,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-query-pagination-editor",
-	"style": "wp-block-query-pagination"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/query-title/block.json b/packages/block-library/src/query-title/block.json
index 65eb03d310c12..0f30f8b8342ce 100644
--- a/packages/block-library/src/query-title/block.json
+++ b/packages/block-library/src/query-title/block.json
@@ -54,5 +54,6 @@
 			}
 		}
 	},
-	"style": "wp-block-query-title"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/query/block.json b/packages/block-library/src/query/block.json
index d30eccf376579..a81b6361b784f 100644
--- a/packages/block-library/src/query/block.json
+++ b/packages/block-library/src/query/block.json
@@ -51,7 +51,7 @@
 		"html": false,
 		"layout": true
 	},
-	"editorStyle": "wp-block-query-editor",
-	"style": "wp-block-query",
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
 	"viewScript": "file:./view.min.js"
 }
diff --git a/packages/block-library/src/quote/block.json b/packages/block-library/src/quote/block.json
index 9deca000efe06..83abf63892df8 100644
--- a/packages/block-library/src/quote/block.json
+++ b/packages/block-library/src/quote/block.json
@@ -66,8 +66,11 @@
 			"label": "Default",
 			"isDefault": true
 		},
-		{ "name": "plain", "label": "Plain" }
+		{
+			"name": "plain",
+			"label": "Plain"
+		}
 	],
-	"editorStyle": "wp-block-quote-editor",
-	"style": "wp-block-quote"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/read-more/block.json b/packages/block-library/src/read-more/block.json
index d3386a49d66b8..bf762a264be3d 100644
--- a/packages/block-library/src/read-more/block.json
+++ b/packages/block-library/src/read-more/block.json
@@ -52,5 +52,6 @@
 			}
 		}
 	},
-	"style": "wp-block-read-more"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/rss/block.json b/packages/block-library/src/rss/block.json
index 2535eda5946fb..7aa549f54a275 100644
--- a/packages/block-library/src/rss/block.json
+++ b/packages/block-library/src/rss/block.json
@@ -45,6 +45,7 @@
 		"align": true,
 		"html": false
 	},
-	"editorStyle": "wp-block-rss-editor",
-	"style": "wp-block-rss"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/search/block.json b/packages/block-library/src/search/block.json
index 5669a9089d0e0..a56ced7ca9818 100644
--- a/packages/block-library/src/search/block.json
+++ b/packages/block-library/src/search/block.json
@@ -92,6 +92,7 @@
 		"html": false
 	},
 	"viewScript": "file:./view.min.js",
-	"editorStyle": "wp-block-search-editor",
-	"style": "wp-block-search"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/separator/block.json b/packages/block-library/src/separator/block.json
index 970f6b5cbb582..0e301c50ccc08 100644
--- a/packages/block-library/src/separator/block.json
+++ b/packages/block-library/src/separator/block.json
@@ -31,10 +31,21 @@
 		}
 	},
 	"styles": [
-		{ "name": "default", "label": "Default", "isDefault": true },
-		{ "name": "wide", "label": "Wide Line" },
-		{ "name": "dots", "label": "Dots" }
+		{
+			"name": "default",
+			"label": "Default",
+			"isDefault": true
+		},
+		{
+			"name": "wide",
+			"label": "Wide Line"
+		},
+		{
+			"name": "dots",
+			"label": "Dots"
+		}
 	],
-	"editorStyle": "wp-block-separator-editor",
-	"style": "wp-block-separator"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/shortcode/block.json b/packages/block-library/src/shortcode/block.json
index 22d838a7198e1..186e15ec0d6a1 100644
--- a/packages/block-library/src/shortcode/block.json
+++ b/packages/block-library/src/shortcode/block.json
@@ -17,5 +17,6 @@
 		"customClassName": false,
 		"html": false
 	},
-	"editorStyle": "wp-block-shortcode-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/site-logo/block.json b/packages/block-library/src/site-logo/block.json
index d1e3d1b20c3da..68ef12956bce8 100644
--- a/packages/block-library/src/site-logo/block.json
+++ b/packages/block-library/src/site-logo/block.json
@@ -53,8 +53,12 @@
 			"label": "Default",
 			"isDefault": true
 		},
-		{ "name": "rounded", "label": "Rounded" }
+		{
+			"name": "rounded",
+			"label": "Rounded"
+		}
 	],
-	"editorStyle": "wp-block-site-logo-editor",
-	"style": "wp-block-site-logo"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/site-tagline/block.json b/packages/block-library/src/site-tagline/block.json
index 22fb59aab5ead..46cc79876b1b6 100644
--- a/packages/block-library/src/site-tagline/block.json
+++ b/packages/block-library/src/site-tagline/block.json
@@ -45,5 +45,6 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-site-tagline-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json
index 4a2685e6941fc..f8e07a92bd6b4 100644
--- a/packages/block-library/src/site-title/block.json
+++ b/packages/block-library/src/site-title/block.json
@@ -60,6 +60,6 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-site-title-editor",
-	"style": "wp-block-site-title"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/social-link/block.json b/packages/block-library/src/social-link/block.json
index 50e95efedb630..e1c76c2c311db 100644
--- a/packages/block-library/src/social-link/block.json
+++ b/packages/block-library/src/social-link/block.json
@@ -33,5 +33,6 @@
 		"reusable": false,
 		"html": false
 	},
-	"editorStyle": "wp-block-social-link-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/social-links/block.json b/packages/block-library/src/social-links/block.json
index 20206511a4c96..3dc3e8444e4c9 100644
--- a/packages/block-library/src/social-links/block.json
+++ b/packages/block-library/src/social-links/block.json
@@ -80,10 +80,21 @@
 		}
 	},
 	"styles": [
-		{ "name": "default", "label": "Default", "isDefault": true },
-		{ "name": "logos-only", "label": "Logos Only" },
-		{ "name": "pill-shape", "label": "Pill Shape" }
+		{
+			"name": "default",
+			"label": "Default",
+			"isDefault": true
+		},
+		{
+			"name": "logos-only",
+			"label": "Logos Only"
+		},
+		{
+			"name": "pill-shape",
+			"label": "Pill Shape"
+		}
 	],
-	"editorStyle": "wp-block-social-links-editor",
-	"style": "wp-block-social-links"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/spacer/block.json b/packages/block-library/src/spacer/block.json
index a9da8d537f1b6..3fa552b181b20 100644
--- a/packages/block-library/src/spacer/block.json
+++ b/packages/block-library/src/spacer/block.json
@@ -25,6 +25,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-spacer-editor",
-	"style": "wp-block-spacer"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/table-of-contents/block.json b/packages/block-library/src/table-of-contents/block.json
index 5fa7e37e6acbd..db1db3a47bdcb 100644
--- a/packages/block-library/src/table-of-contents/block.json
+++ b/packages/block-library/src/table-of-contents/block.json
@@ -47,5 +47,6 @@
 			}
 		}
 	},
-	"example": {}
+	"example": {},
+	"editorScript": "file:./editor.min.js"
 }
diff --git a/packages/block-library/src/table/block.json b/packages/block-library/src/table/block.json
index 470886a1247fe..eb16dc9afd99c 100644
--- a/packages/block-library/src/table/block.json
+++ b/packages/block-library/src/table/block.json
@@ -205,6 +205,7 @@
 		},
 		{ "name": "stripes", "label": "Stripes" }
 	],
-	"editorStyle": "wp-block-table-editor",
-	"style": "wp-block-table"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/tag-cloud/block.json b/packages/block-library/src/tag-cloud/block.json
index 9481dc945666a..1f62518635eba 100644
--- a/packages/block-library/src/tag-cloud/block.json
+++ b/packages/block-library/src/tag-cloud/block.json
@@ -50,5 +50,6 @@
 			"__experimentalLetterSpacing": true
 		}
 	},
-	"editorStyle": "wp-block-tag-cloud-editor"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json
index 3b0946718bcb9..2cd4c04a7a88f 100644
--- a/packages/block-library/src/template-part/block.json
+++ b/packages/block-library/src/template-part/block.json
@@ -26,5 +26,6 @@
 		"reusable": false,
 		"renaming": false
 	},
-	"editorStyle": "wp-block-template-part-editor"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css"
 }
diff --git a/packages/block-library/src/template-part/edit/utils/transformers.js b/packages/block-library/src/template-part/edit/utils/transformers.js
index 7932a00572941..449e914d5f793 100644
--- a/packages/block-library/src/template-part/edit/utils/transformers.js
+++ b/packages/block-library/src/template-part/edit/utils/transformers.js
@@ -70,31 +70,31 @@ export function transformWidgetToBlock( widget ) {
  * @param {Object} block Legacy Widget block object
  * @return {Object|undefined} a block
  */
-function switchLegacyWidgetType( block ) {
-	const transforms = getPossibleBlockTransformations( [ block ] ).filter(
-		( item ) => {
-			// The block without any transformations can't be a wildcard.
-			if ( ! item.transforms ) {
-				return true;
-			}
+async function switchLegacyWidgetType( block ) {
+	const transforms = (
+		await getPossibleBlockTransformations( [ block ] )
+	 ).filter( ( item ) => {
+		// The block without any transformations can't be a wildcard.
+		if ( ! item.transforms ) {
+			return true;
+		}
 
-			const hasWildCardFrom = item.transforms?.from?.find(
-				( from ) => from.blocks && from.blocks.includes( '*' )
-			);
-			const hasWildCardTo = item.transforms?.to?.find(
-				( to ) => to.blocks && to.blocks.includes( '*' )
-			);
+		const hasWildCardFrom = item.transforms.from?.some( ( from ) =>
+			from.blocks?.includes( '*' )
+		);
+		const hasWildCardTo = item.transforms.to?.find(
+			( to ) => to.blocks && to.blocks.includes( '*' )
+		);
 
-			// Skip wildcard transformations.
-			return ! hasWildCardFrom && ! hasWildCardTo;
-		}
-	);
+		// Skip wildcard transformations.
+		return ! hasWildCardFrom && ! hasWildCardTo;
+	} );
 
 	if ( ! transforms.length ) {
 		return undefined;
 	}
 
-	return switchToBlockType( block, transforms[ 0 ].name );
+	return await switchToBlockType( block, transforms[ 0 ].name );
 }
 
 function transformInnerBlocks( innerBlocks = [] ) {
diff --git a/packages/block-library/src/term-description/block.json b/packages/block-library/src/term-description/block.json
index fc91a4aff4c48..4739962774217 100644
--- a/packages/block-library/src/term-description/block.json
+++ b/packages/block-library/src/term-description/block.json
@@ -38,5 +38,7 @@
 				"fontSize": true
 			}
 		}
-	}
+	},
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/text-columns/block.json b/packages/block-library/src/text-columns/block.json
index 3af169fadbb3b..0cd8a1963bfc2 100644
--- a/packages/block-library/src/text-columns/block.json
+++ b/packages/block-library/src/text-columns/block.json
@@ -31,6 +31,7 @@
 	"supports": {
 		"inserter": false
 	},
-	"editorStyle": "wp-block-text-columns-editor",
-	"style": "wp-block-text-columns"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/verse/block.json b/packages/block-library/src/verse/block.json
index 846a1dc99caaf..f9cff98235e28 100644
--- a/packages/block-library/src/verse/block.json
+++ b/packages/block-library/src/verse/block.json
@@ -57,6 +57,6 @@
 			"style": true
 		}
 	},
-	"style": "wp-block-verse",
-	"editorStyle": "wp-block-verse-editor"
+	"editorScript": "file:./editor.min.js",
+	"style": "file:./style.css"
 }
diff --git a/packages/block-library/src/video/block.json b/packages/block-library/src/video/block.json
index 5d4680f39e79a..adfd1321bdd9a 100644
--- a/packages/block-library/src/video/block.json
+++ b/packages/block-library/src/video/block.json
@@ -90,6 +90,7 @@
 			}
 		}
 	},
-	"editorStyle": "wp-block-video-editor",
-	"style": "wp-block-video"
+	"editorScript": "file:./editor.min.js",
+	"editorStyle": "file:./editor.css",
+	"style": "file:./style.css"
 }
diff --git a/packages/blocks/README.md b/packages/blocks/README.md
index 8e6fdc9d900db..0022738d67c8d 100644
--- a/packages/blocks/README.md
+++ b/packages/blocks/README.md
@@ -198,6 +198,14 @@ _Returns_
 
 -   `Array`: Block settings.
 
+### getBootstrappedBlockType
+
+Undocumented declaration.
+
+### getBootstrappedBlockTypes
+
+Undocumented declaration.
+
 ### getChildBlockNames
 
 Returns an array with the child blocks of a given block.
@@ -278,6 +286,10 @@ _Returns_
 
 -   `Object|string`: Save element or raw HTML string.
 
+### getTransformItemsForBlocks
+
+Undocumented declaration.
+
 ### getUnregisteredTypeHandlerName
 
 Retrieves name of block handling unregistered block types, or undefined if no handler has been defined.
diff --git a/packages/blocks/src/api/factory.js b/packages/blocks/src/api/factory.js
index 53333ef688085..2846164be5c3f 100644
--- a/packages/blocks/src/api/factory.js
+++ b/packages/blocks/src/api/factory.js
@@ -13,7 +13,8 @@ import { createHooks, applyFilters } from '@wordpress/hooks';
  */
 import {
 	getBlockType,
-	getBlockTypes,
+	loadBlockType,
+	loadBlockTypes,
 	getGroupingBlockName,
 } from './registration';
 import {
@@ -153,6 +154,11 @@ export function cloneBlock( block, mergeAttributes = {}, newInnerBlocks ) {
  * @return {boolean} Is the transform possible?
  */
 const isPossibleTransformForSource = ( transform, direction, blocks ) => {
+	// Only consider 'block' type transforms as valid.
+	if ( transform.type !== 'block' ) {
+		return false;
+	}
+
 	if ( ! blocks.length ) {
 		return false;
 	}
@@ -160,38 +166,29 @@ const isPossibleTransformForSource = ( transform, direction, blocks ) => {
 	// If multiple blocks are selected, only multi block transforms
 	// or wildcard transforms are allowed.
 	const isMultiBlock = blocks.length > 1;
-	const firstBlockName = blocks[ 0 ].name;
-	const isValidForMultiBlocks =
-		isWildcardBlockTransform( transform ) ||
-		! isMultiBlock ||
-		transform.isMultiBlock;
-	if ( ! isValidForMultiBlocks ) {
+	const isWildcardTransform = isWildcardBlockTransform( transform );
+	if ( isMultiBlock && ! isWildcardTransform && ! transform.isMultiBlock ) {
 		return false;
 	}
 
 	// Check non-wildcard transforms to ensure that transform is valid
 	// for a block selection of multiple blocks of different types.
+	const firstBlockName = blocks[ 0 ].name;
 	if (
-		! isWildcardBlockTransform( transform ) &&
+		! isWildcardTransform &&
 		! blocks.every( ( block ) => block.name === firstBlockName )
 	) {
 		return false;
 	}
 
-	// Only consider 'block' type transforms as valid.
-	const isBlockType = transform.type === 'block';
-	if ( ! isBlockType ) {
-		return false;
-	}
-
 	// Check if the transform's block name matches the source block (or is a wildcard)
 	// only if this is a transform 'from'.
 	const sourceBlock = blocks[ 0 ];
-	const hasMatchingName =
-		direction !== 'from' ||
-		transform.blocks.indexOf( sourceBlock.name ) !== -1 ||
-		isWildcardBlockTransform( transform );
-	if ( ! hasMatchingName ) {
+	if (
+		direction === 'from' &&
+		! isWildcardTransform &&
+		! transform.blocks.includes( sourceBlock.name )
+	) {
 		return false;
 	}
 
@@ -222,25 +219,22 @@ const isPossibleTransformForSource = ( transform, direction, blocks ) => {
  *
  * @return {Array} Block types that the blocks can be transformed into.
  */
-const getBlockTypesForPossibleFromTransforms = ( blocks ) => {
+const getBlockTypesForPossibleFromTransforms = async ( blocks ) => {
 	if ( ! blocks.length ) {
 		return [];
 	}
 
-	const allBlockTypes = getBlockTypes();
-
+	const allBlockTypes = await loadBlockTypes();
+	const fromTransforms = await getBlockTransforms( 'from' );
 	// filter all blocks to find those with a 'from' transform.
 	const blockTypesWithPossibleFromTransforms = allBlockTypes.filter(
-		( blockType ) => {
-			const fromTransforms = getBlockTransforms( 'from', blockType.name );
-			return !! findTransform( fromTransforms, ( transform ) => {
-				return isPossibleTransformForSource(
-					transform,
-					'from',
-					blocks
-				);
-			} );
-		}
+		( blockType ) =>
+			findTransform(
+				fromTransforms,
+				( transform ) =>
+					transform.blockName === blockType.name &&
+					isPossibleTransformForSource( transform, 'from', blocks )
+			)
 	);
 
 	return blockTypesWithPossibleFromTransforms;
@@ -254,28 +248,26 @@ const getBlockTypesForPossibleFromTransforms = ( blocks ) => {
  *
  * @return {Array} Block types that the source can be transformed into.
  */
-const getBlockTypesForPossibleToTransforms = ( blocks ) => {
+const getBlockTypesForPossibleToTransforms = async ( blocks ) => {
 	if ( ! blocks.length ) {
 		return [];
 	}
 
 	const sourceBlock = blocks[ 0 ];
-	const blockType = getBlockType( sourceBlock.name );
+	const blockType = await loadBlockType( sourceBlock.name );
 	const transformsTo = blockType
-		? getBlockTransforms( 'to', blockType.name )
+		? await getBlockTransforms( 'to', blockType.name )
 		: [];
 
 	// filter all 'to' transforms to find those that are possible.
-	const possibleTransforms = transformsTo.filter( ( transform ) => {
-		return (
-			transform && isPossibleTransformForSource( transform, 'to', blocks )
-		);
-	} );
+	const possibleTransforms = transformsTo.filter( ( transform ) =>
+		isPossibleTransformForSource( transform, 'to', blocks )
+	);
 
 	// Build a list of block names using the possible 'to' transforms.
-	const blockNames = possibleTransforms
-		.map( ( transformation ) => transformation.blocks )
-		.flat();
+	const blockNames = possibleTransforms.flatMap(
+		( transformation ) => transformation.blocks
+	);
 
 	// Map block names to block types.
 	return blockNames.map( getBlockType );
@@ -316,22 +308,36 @@ export const isContainerGroupBlock = ( name ) =>
  *
  * @return {Array} Block types that the blocks argument can be transformed to.
  */
-export function getPossibleBlockTransformations( blocks ) {
+export async function getPossibleBlockTransformations( blocks ) {
 	if ( ! blocks.length ) {
 		return [];
 	}
 
 	const blockTypesForFromTransforms =
-		getBlockTypesForPossibleFromTransforms( blocks );
+		await getBlockTypesForPossibleFromTransforms( blocks );
 	const blockTypesForToTransforms =
-		getBlockTypesForPossibleToTransforms( blocks );
+		await getBlockTypesForPossibleToTransforms( blocks );
 
-	return [
-		...new Set( [
+	return Array.from(
+		new Set( [
 			...blockTypesForFromTransforms,
 			...blockTypesForToTransforms,
-		] ),
-	];
+		] )
+	);
+}
+
+export async function getTransformItemsForBlocks( items, blocks ) {
+	const normalizedBlocks = Array.isArray( blocks ) ? blocks : [ blocks ];
+	const possibleTransforms = (
+		await getPossibleBlockTransformations( normalizedBlocks )
+	).reduce( ( accumulator, block ) => {
+		if ( items[ block?.name ] ) {
+			accumulator.push( items[ block.name ] );
+		}
+		return accumulator;
+	}, [] );
+
+	return possibleTransforms;
 }
 
 /**
@@ -378,12 +384,16 @@ export function findTransform( transforms, predicate ) {
  *
  * @return {Array} Block transforms for direction.
  */
-export function getBlockTransforms( direction, blockTypeOrName ) {
+export async function getBlockTransforms( direction, blockTypeOrName ) {
 	// When retrieving transforms for all block types, recurse into self.
 	if ( blockTypeOrName === undefined ) {
-		return getBlockTypes()
-			.map( ( { name } ) => getBlockTransforms( direction, name ) )
-			.flat();
+		const blockTypes = await loadBlockTypes();
+		const blockTransforms = await Promise.all(
+			blockTypes.map( ( { name } ) =>
+				getBlockTransforms( direction, name )
+			)
+		);
+		return blockTransforms.flat();
 	}
 
 	// Validate that block type exists and has array of direction.
@@ -455,7 +465,7 @@ function maybeCheckTransformIsMatch( transform, blocks ) {
  *
  * @return {?Array} Array of blocks or null.
  */
-export function switchToBlockType( blocks, name ) {
+export async function switchToBlockType( blocks, name ) {
 	const blocksArray = Array.isArray( blocks ) ? blocks : [ blocks ];
 	const isMultiBlock = blocksArray.length > 1;
 	const firstBlock = blocksArray[ 0 ];
@@ -463,8 +473,8 @@ export function switchToBlockType( blocks, name ) {
 
 	// Find the right transformation by giving priority to the "to"
 	// transformation.
-	const transformationsFrom = getBlockTransforms( 'from', name );
-	const transformationsTo = getBlockTransforms( 'to', sourceName );
+	const transformationsFrom = await getBlockTransforms( 'from', name );
+	const transformationsTo = await getBlockTransforms( 'to', sourceName );
 
 	const transformation =
 		findTransform(
@@ -581,13 +591,13 @@ export function switchToBlockType( blocks, name ) {
  */
 export const getBlockFromExample = ( name, example ) => {
 	try {
-		return createBlock(
-			name,
-			example.attributes,
-			( example.innerBlocks ?? [] ).map( ( innerBlock ) =>
+		let innerBlocks = [];
+		if ( example.innerBlocks ) {
+			innerBlocks = example.innerBlocks.map( ( innerBlock ) =>
 				getBlockFromExample( innerBlock.name, innerBlock )
-			)
-		);
+			);
+		}
+		return createBlock( name, example.attributes, innerBlocks );
 	} catch {
 		return createBlock( 'core/missing', {
 			originalName: name,
diff --git a/packages/blocks/src/api/index.js b/packages/blocks/src/api/index.js
index 2ddeb3a60f0ab..c96c2dc389612 100644
--- a/packages/blocks/src/api/index.js
+++ b/packages/blocks/src/api/index.js
@@ -14,6 +14,7 @@ export {
 	getBlockTransforms,
 	findTransform,
 	getBlockFromExample,
+	getTransformItemsForBlocks,
 } from './factory';
 
 // The block tree is composed of a collection of block nodes. Blocks contained
@@ -125,6 +126,8 @@ export {
 	getBlockSupport,
 	hasBlockSupport,
 	getBlockVariations,
+	getBootstrappedBlockType,
+	getBootstrappedBlockTypes,
 	isReusableBlock,
 	isTemplatePart,
 	getChildBlockNames,
diff --git a/packages/blocks/src/api/parser/index.js b/packages/blocks/src/api/parser/index.js
index f8ff0c68964dc..ca4859d133ddc 100644
--- a/packages/blocks/src/api/parser/index.js
+++ b/packages/blocks/src/api/parser/index.js
@@ -10,7 +10,7 @@ import { autop } from '@wordpress/autop';
 import {
 	getFreeformContentHandlerName,
 	getUnregisteredTypeHandlerName,
-	getBlockType,
+	loadBlockType,
 } from '../registration';
 import { getSaveContent } from '../serializer';
 import { validateBlock } from '../validation';
@@ -193,7 +193,7 @@ function applyBlockValidation( unvalidatedBlock, blockType ) {
  *
  * @return {WPBlock | undefined} Fully parsed block.
  */
-export function parseRawBlock( rawBlock, options ) {
+export async function parseRawBlock( rawBlock, options ) {
 	let normalizedBlock = normalizeRawBlock( rawBlock, options );
 
 	// During the lifecycle of the project, we renamed some old blocks
@@ -202,12 +202,12 @@ export function parseRawBlock( rawBlock, options ) {
 	normalizedBlock = convertLegacyBlocks( normalizedBlock );
 
 	// Try finding the type for known block name.
-	let blockType = getBlockType( normalizedBlock.blockName );
+	let blockType = await loadBlockType( normalizedBlock.blockName );
 
 	// If not blockType is found for the specified name, fallback to the "unregistedBlockType".
 	if ( ! blockType ) {
 		normalizedBlock = createMissingBlockType( normalizedBlock );
-		blockType = getBlockType( normalizedBlock.blockName );
+		blockType = await loadBlockType( normalizedBlock.blockName );
 	}
 
 	// If it's an empty freeform block or there's no blockType (no missing block handler)
@@ -223,8 +223,13 @@ export function parseRawBlock( rawBlock, options ) {
 	}
 
 	// Parse inner blocks recursively.
-	const parsedInnerBlocks = normalizedBlock.innerBlocks
-		.map( ( innerBlock ) => parseRawBlock( innerBlock, options ) )
+	const parsedInnerBlocks = (
+		await Promise.all(
+			normalizedBlock.innerBlocks.map( ( innerBlock ) =>
+				parseRawBlock( innerBlock, options )
+			)
+		)
+	 )
 		// See https://github.com/WordPress/gutenberg/pull/17164.
 		.filter( ( innerBlock ) => !! innerBlock );
 
@@ -307,12 +312,13 @@ export function parseRawBlock( rawBlock, options ) {
  *
  * @return {Array} Block list.
  */
-export default function parse( content, options ) {
-	return grammarParse( content ).reduce( ( accumulator, rawBlock ) => {
-		const block = parseRawBlock( rawBlock, options );
+export default async function parse( content, options ) {
+	const blocks = [];
+	for ( const rawBlock of grammarParse( content ) ) {
+		const block = await parseRawBlock( rawBlock, options );
 		if ( block ) {
-			accumulator.push( block );
+			blocks.push( block );
 		}
-		return accumulator;
-	}, [] );
+	}
+	return blocks;
 }
diff --git a/packages/blocks/src/api/raw-handling/get-raw-transforms.js b/packages/blocks/src/api/raw-handling/get-raw-transforms.js
index 060c72d00f847..32d3009baea1c 100644
--- a/packages/blocks/src/api/raw-handling/get-raw-transforms.js
+++ b/packages/blocks/src/api/raw-handling/get-raw-transforms.js
@@ -3,8 +3,9 @@
  */
 import { getBlockTransforms } from '../factory';
 
-export function getRawTransforms() {
-	return getBlockTransforms( 'from' )
+export async function getRawTransforms() {
+	const transforms = await getBlockTransforms( 'from' );
+	return transforms
 		.filter( ( { type } ) => type === 'raw' )
 		.map( ( transform ) => {
 			return transform.isMatch
diff --git a/packages/blocks/src/api/raw-handling/index.js b/packages/blocks/src/api/raw-handling/index.js
index afd68a4a9e1e7..a8a80e9271402 100644
--- a/packages/blocks/src/api/raw-handling/index.js
+++ b/packages/blocks/src/api/raw-handling/index.js
@@ -35,7 +35,7 @@ export function deprecatedGetPhrasingContentSchema( context ) {
  *
  * @return {Array} A list of blocks.
  */
-export function rawHandler( { HTML = '' } ) {
+export async function rawHandler( { HTML = '' } ) {
 	// If we detect block delimiters, parse entirely as blocks.
 	if ( HTML.indexOf( '<!-- wp:' ) !== -1 ) {
 		return parse( HTML );
@@ -43,7 +43,7 @@ export function rawHandler( { HTML = '' } ) {
 
 	// An array of HTML strings and block objects. The blocks replace matched
 	// shortcodes.
-	const pieces = shortcodeConverter( HTML );
+	const pieces = await shortcodeConverter( HTML );
 	const blockContentSchema = getBlockContentSchema();
 
 	return pieces
diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js
index d0bf3e05979c6..e80472e036b5c 100644
--- a/packages/blocks/src/api/raw-handling/paste-handler.js
+++ b/packages/blocks/src/api/raw-handling/paste-handler.js
@@ -76,7 +76,7 @@ function filterInlineHTML( HTML ) {
  *
  * @return {Array|string} A list of blocks or a string, depending on `handlerMode`.
  */
-export function pasteHandler( {
+export async function pasteHandler( {
 	HTML = '',
 	plainText = '',
 	mode = 'AUTO',
@@ -100,7 +100,7 @@ export function pasteHandler( {
 		const content = HTML ? HTML : plainText;
 
 		if ( content.indexOf( '<!-- wp:' ) !== -1 ) {
-			return parse( content );
+			return await parse( content );
 		}
 	}
 
@@ -135,7 +135,7 @@ export function pasteHandler( {
 
 	// An array of HTML strings and block objects. The blocks replace matched
 	// shortcodes.
-	const pieces = shortcodeConverter( HTML );
+	const pieces = await shortcodeConverter( HTML );
 
 	// The call to shortcodeConverter will always return more than one element
 	// if shortcodes are matched. The reason is when shortcodes are matched
diff --git a/packages/blocks/src/api/raw-handling/shortcode-converter.js b/packages/blocks/src/api/raw-handling/shortcode-converter.js
index 3434717553a98..bff65cced76bf 100644
--- a/packages/blocks/src/api/raw-handling/shortcode-converter.js
+++ b/packages/blocks/src/api/raw-handling/shortcode-converter.js
@@ -14,19 +14,19 @@ import { applyBuiltInValidationFixes } from '../parser/apply-built-in-validation
 const castArray = ( maybeArray ) =>
 	Array.isArray( maybeArray ) ? maybeArray : [ maybeArray ];
 
-function segmentHTMLToShortcodeBlock(
+async function segmentHTMLToShortcodeBlock(
 	HTML,
 	lastIndex = 0,
 	excludedBlockNames = []
 ) {
 	// Get all matches.
-	const transformsFrom = getBlockTransforms( 'from' );
+	const transformsFrom = await getBlockTransforms( 'from' );
 
 	const transformation = findTransform(
 		transformsFrom,
 		( transform ) =>
-			excludedBlockNames.indexOf( transform.blockName ) === -1 &&
 			transform.type === 'shortcode' &&
+			! excludedBlockNames.includes( transform.blockName ) &&
 			castArray( transform.tag ).some( ( tag ) =>
 				regexp( tag ).test( HTML )
 			)
@@ -60,7 +60,7 @@ function segmentHTMLToShortcodeBlock(
 				/^\s*(\n|<\/p>)/.test( afterHTML )
 			)
 		) {
-			return segmentHTMLToShortcodeBlock( HTML, lastIndex );
+			return await segmentHTMLToShortcodeBlock( HTML, lastIndex );
 		}
 
 		// If a transformation's `isMatch` predicate fails for the inbound
@@ -75,7 +75,7 @@ function segmentHTMLToShortcodeBlock(
 			transformation.isMatch &&
 			! transformation.isMatch( match.shortcode.attrs )
 		) {
-			return segmentHTMLToShortcodeBlock( HTML, previousIndex, [
+			return await segmentHTMLToShortcodeBlock( HTML, previousIndex, [
 				...excludedBlockNames,
 				transformation.blockName,
 			] );
@@ -143,9 +143,9 @@ function segmentHTMLToShortcodeBlock(
 		}
 
 		return [
-			...segmentHTMLToShortcodeBlock( beforeHTML ),
+			...( await segmentHTMLToShortcodeBlock( beforeHTML ) ),
 			...blocks,
-			...segmentHTMLToShortcodeBlock( afterHTML ),
+			...( await segmentHTMLToShortcodeBlock( afterHTML ) ),
 		];
 	}
 
diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js
index b1dbee47b9b7c..6659ef6a34247 100644
--- a/packages/blocks/src/api/registration.js
+++ b/packages/blocks/src/api/registration.js
@@ -3,7 +3,7 @@
 /**
  * WordPress dependencies
  */
-import { select, dispatch } from '@wordpress/data';
+import { select, resolveSelect, dispatch } from '@wordpress/data';
 import { _x } from '@wordpress/i18n';
 
 /**
@@ -234,20 +234,16 @@ export function registerBlockType( blockNameOrMetadata, settings ) {
 		);
 		return;
 	}
-	if ( select( blocksStore ).getBlockType( name ) ) {
-		console.error( 'Block "' + name + '" is already registered.' );
-		return;
-	}
 
 	const { addBootstrappedBlockType, addUnprocessedBlockType } = unlock(
 		dispatch( blocksStore )
 	);
 
-	if ( isObject( blockNameOrMetadata ) ) {
-		const metadata = getBlockSettingsFromMetadata( blockNameOrMetadata );
-		addBootstrappedBlockType( name, metadata );
-	}
+	const metadata = isObject( blockNameOrMetadata )
+		? getBlockSettingsFromMetadata( blockNameOrMetadata )
+		: getBlockSettingsFromMetadata( settings );
 
+	addBootstrappedBlockType( name, metadata );
 	addUnprocessedBlockType( name, settings );
 
 	return select( blocksStore ).getBlockType( name );
@@ -501,9 +497,16 @@ export function getDefaultBlockName() {
  * @return {?Object} Block type.
  */
 export function getBlockType( name ) {
-	return select( blocksStore )?.getBlockType( name );
+	return select( blocksStore ).getBlockType( name );
+}
+
+export function getBootstrappedBlockType( name ) {
+	return select( blocksStore ).getBootstrappedBlockType( name );
 }
 
+export function loadBlockType( name ) {
+	return resolveSelect( blocksStore ).getBlockType( name );
+}
 /**
  * Returns all registered blocks.
  *
@@ -513,6 +516,17 @@ export function getBlockTypes() {
 	return select( blocksStore ).getBlockTypes();
 }
 
+export function getBootstrappedBlockTypes() {
+	return select( blocksStore ).getBootstrappedBlockTypes();
+}
+
+export async function loadBlockTypes() {
+	await Promise.all(
+		getBootstrappedBlockTypes().map( ( { name } ) => loadBlockType( name ) )
+	);
+	return getBlockTypes();
+}
+
 /**
  * Returns the block support value for a feature, if defined.
  *
diff --git a/packages/blocks/src/api/utils.js b/packages/blocks/src/api/utils.js
index 0d17836faea7e..db41453bcdfb6 100644
--- a/packages/blocks/src/api/utils.js
+++ b/packages/blocks/src/api/utils.js
@@ -17,7 +17,11 @@ import { RichTextData } from '@wordpress/rich-text';
  * Internal dependencies
  */
 import { BLOCK_ICON_DEFAULT } from './constants';
-import { getBlockType, getDefaultBlockName } from './registration';
+import {
+	getBlockType,
+	getBootstrappedBlockType,
+	getDefaultBlockName,
+} from './registration';
 
 extend( [ namesPlugin, a11yPlugin ] );
 
@@ -38,26 +42,26 @@ const ICON_COLORS = [ '#191e23', '#f8f9f9' ];
  * @return {boolean} Whether the block is an unmodified block.
  */
 export function isUnmodifiedBlock( block ) {
-	return Object.entries( getBlockType( block.name )?.attributes ?? {} ).every(
-		( [ key, definition ] ) => {
-			const value = block.attributes[ key ];
-
-			// Every attribute that has a default must match the default.
-			if ( definition.hasOwnProperty( 'default' ) ) {
-				return value === definition.default;
-			}
-
-			// The rich text type is a bit different from the rest because it
-			// has an implicit default value of an empty RichTextData instance,
-			// so check the length of the value.
-			if ( definition.type === 'rich-text' ) {
-				return ! value?.length;
-			}
+	return Object.entries(
+		getBootstrappedBlockType( block.name )?.attributes ?? {}
+	).every( ( [ key, definition ] ) => {
+		const value = block.attributes[ key ];
+
+		// Every attribute that has a default must match the default.
+		if ( definition.hasOwnProperty( 'default' ) ) {
+			return value === definition.default;
+		}
 
-			// Every attribute that doesn't have a default should be undefined.
-			return value === undefined;
+		// The rich text type is a bit different from the rest because it
+		// has an implicit default value of an empty RichTextData instance,
+		// so check the length of the value.
+		if ( definition.type === 'rich-text' ) {
+			return ! value?.length;
 		}
-	);
+
+		// Every attribute that doesn't have a default should be undefined.
+		return value === undefined;
+	} );
 }
 
 /**
@@ -271,7 +275,7 @@ export function getDefault( attributeSchema ) {
  */
 export function __experimentalSanitizeBlockAttributes( name, attributes ) {
 	// Get the type definition associated with a registered block.
-	const blockType = getBlockType( name );
+	const blockType = getBootstrappedBlockType( name );
 
 	if ( undefined === blockType ) {
 		throw new Error( `Block type '${ name }' is not registered.` );
diff --git a/packages/blocks/src/store/index.js b/packages/blocks/src/store/index.js
index ffda3ffe00026..97610167c127e 100644
--- a/packages/blocks/src/store/index.js
+++ b/packages/blocks/src/store/index.js
@@ -11,6 +11,7 @@ import * as selectors from './selectors';
 import * as privateSelectors from './private-selectors';
 import * as actions from './actions';
 import * as privateActions from './private-actions';
+import * as resolvers from './resolvers';
 import { STORE_NAME } from './constants';
 import { unlock } from '../lock-unlock';
 
@@ -25,6 +26,7 @@ export const store = createReduxStore( STORE_NAME, {
 	reducer,
 	selectors,
 	actions,
+	resolvers,
 } );
 
 register( store );
diff --git a/packages/blocks/src/store/private-selectors.js b/packages/blocks/src/store/private-selectors.js
index 7e4311658c869..82e97ad55e713 100644
--- a/packages/blocks/src/store/private-selectors.js
+++ b/packages/blocks/src/store/private-selectors.js
@@ -153,18 +153,6 @@ export const getSupportedStyles = createSelector(
 	( state, name ) => [ state.blockTypes[ name ] ]
 );
 
-/**
- * Returns the bootstrapped block type metadata for a give block name.
- *
- * @param {Object} state Data state.
- * @param {string} name  Block name.
- *
- * @return {Object} Bootstrapped block type metadata for a block.
- */
-export function getBootstrappedBlockType( state, name ) {
-	return state.bootstrappedBlockTypes[ name ];
-}
-
 /**
  * Returns all the unprocessed (before applying the `registerBlockType` filter)
  * block type settings as passed during block registration.
diff --git a/packages/blocks/src/store/resolvers.js b/packages/blocks/src/store/resolvers.js
new file mode 100644
index 0000000000000..a5fd486b7c4e0
--- /dev/null
+++ b/packages/blocks/src/store/resolvers.js
@@ -0,0 +1,46 @@
+function loaderElement( type, src ) {
+	if ( type === 'script' ) {
+		const script = document.createElement( 'script' );
+		script.src = src;
+		return script;
+	}
+
+	if ( type === 'style' ) {
+		const link = document.createElement( 'link' );
+		link.rel = 'stylesheet';
+		link.type = 'text/css';
+		link.href = src;
+		return link;
+	}
+
+	return null;
+}
+
+export const getBlockType = ( name ) => async () => {
+	const mods = window.wp.importmap[ name ];
+	if ( ! mods ) {
+		return;
+	}
+
+	await Promise.all(
+		mods
+			.filter( ( mod ) => mod.src )
+			.map(
+				( mod ) =>
+					new Promise( ( resolve, reject ) => {
+						const node = loaderElement( mod.type, mod.src );
+						node.onload = () => {
+							resolve();
+						};
+						node.onerror = () => {
+							reject(
+								new Error(
+									`Failed to load ${ mod.type } ${ name } ${ mod.handle } ${ mod.src }`
+								)
+							);
+						};
+						document.body.appendChild( node );
+					} )
+			)
+	);
+};
diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js
index b2b8ab8106f09..5018b8be2743c 100644
--- a/packages/blocks/src/store/selectors.js
+++ b/packages/blocks/src/store/selectors.js
@@ -29,7 +29,7 @@ import { getValueFromObjectPath } from './utils';
  */
 const getNormalizedBlockType = ( state, nameOrType ) =>
 	'string' === typeof nameOrType
-		? getBlockType( state, nameOrType )
+		? getBootstrappedBlockType( state, nameOrType )
 		: nameOrType;
 
 /**
@@ -65,6 +65,15 @@ export const getBlockTypes = createSelector(
 	( state ) => [ state.blockTypes ]
 );
 
+export const getBootstrappedBlockTypes = createSelector(
+	( state ) => Object.values( state.bootstrappedBlockTypes ),
+	( state ) => [ state.bootstrappedBlockTypes ]
+);
+
+export const getBootstrappedBlockType = ( state, name ) => {
+	return state.bootstrappedBlockTypes[ name ];
+};
+
 /**
  * Returns a block type by name.
  *
@@ -301,11 +310,7 @@ export function getActiveBlockVariation( state, blockName, attributes, scope ) {
 export function getDefaultBlockVariation( state, blockName, scope ) {
 	const variations = getBlockVariations( state, blockName, scope );
 
-	const defaultVariation = [ ...variations ]
-		.reverse()
-		.find( ( { isDefault } ) => !! isDefault );
-
-	return defaultVariation || variations[ 0 ];
+	return variations.findLast( ( v, i ) => v.isDefault || i === 0 );
 }
 
 /**
diff --git a/packages/components/src/autocomplete/index.tsx b/packages/components/src/autocomplete/index.tsx
index b76e8548c08f2..abfa674f7f210 100644
--- a/packages/components/src/autocomplete/index.tsx
+++ b/packages/components/src/autocomplete/index.tsx
@@ -108,7 +108,7 @@ export function useAutocomplete( {
 		onChange( insert( record, toInsert, start, end ) );
 	}
 
-	function select( option: KeyedOption ) {
+	async function select( option: KeyedOption ) {
 		const { getOptionCompletion } = autocompleter || {};
 
 		if ( option.isDisabled ) {
@@ -116,7 +116,10 @@ export function useAutocomplete( {
 		}
 
 		if ( getOptionCompletion ) {
-			const completion = getOptionCompletion( option.value, filterValue );
+			const completion = await getOptionCompletion(
+				option.value,
+				filterValue
+			);
 
 			const isCompletionObject = (
 				obj: OptionCompletion
diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js
index 4b82b62e318bc..372c6e8789bb5 100644
--- a/packages/core-data/src/entity-provider.js
+++ b/packages/core-data/src/entity-provider.js
@@ -5,7 +5,8 @@ import {
 	createContext,
 	useContext,
 	useCallback,
-	useMemo,
+	useState,
+	useEffect,
 } from '@wordpress/element';
 import { useSelect, useDispatch } from '@wordpress/data';
 import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
@@ -171,19 +172,30 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
 	const { __unstableCreateUndoLevel, editEntityRecord } =
 		useDispatch( STORE_NAME );
 
-	const blocks = useMemo( () => {
-		if ( ! id ) {
-			return undefined;
-		}
+	const [ blocks, setBlocks ] = useState( EMPTY_ARRAY );
+	useEffect( () => {
+		let mounted = true;
 
 		if ( editedBlocks ) {
-			return editedBlocks;
+			setBlocks( editedBlocks );
 		}
 
-		return content && typeof content !== 'function'
-			? parse( content )
-			: EMPTY_ARRAY;
-	}, [ id, editedBlocks, content ] );
+		// Load the blocks from the content if not already in state
+		// Guard against other instances that might have
+		// set content to a function already or the blocks are already in state.
+		if ( content && typeof content !== 'function' ) {
+			parse( content ).then( ( parsedBlocks ) => {
+				// Don't set if another `editedBlocks` or `content` value arrived during parsing.
+				if ( mounted ) {
+					setBlocks( parsedBlocks );
+				}
+			} );
+		}
+
+		return () => {
+			mounted = false;
+		};
+	}, [ editedBlocks, content ] );
 
 	const updateFootnotes = useCallback(
 		( _blocks ) => updateFootnotesFromMeta( _blocks, meta ),
diff --git a/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts b/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts
index 75102983069d4..b1a6c1126ed47 100644
--- a/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts
+++ b/packages/e2e-test-utils-playwright/src/editor/transform-block-to.ts
@@ -15,7 +15,7 @@ export async function transformBlockTo( this: Editor, name: string ) {
 	);
 
 	await this.page.evaluate(
-		( [ blockName ] ) => {
+		async ( [ blockName ] ) => {
 			const clientIds = window.wp.data
 				.select( 'core/block-editor' )
 				.getSelectedBlockClientIds();
@@ -26,7 +26,10 @@ export async function transformBlockTo( this: Editor, name: string ) {
 				.dispatch( 'core/block-editor' )
 				.replaceBlocks(
 					clientIds,
-					window.wp.blocks.switchToBlockType( blocks, blockName )
+					await window.wp.blocks.switchToBlockType(
+						blocks,
+						blockName
+					)
 				);
 		},
 		[ name ]
diff --git a/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js b/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
index 4285a410f891e..b54147aafd742 100644
--- a/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
+++ b/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
@@ -159,6 +159,9 @@ describe( 'Inserting blocks', () => {
 
 	it( 'should insert block with the slash inserter when using multiple words', async () => {
 		await page.keyboard.press( 'Enter' );
+		await canvas().waitForSelector(
+			'[data-type="core/paragraph"][data-empty="true"'
+		);
 		await page.keyboard.type( '/tag cloud' );
 		await page.waitForXPath(
 			`//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Tag Cloud')]`
diff --git a/packages/edit-post/src/components/start-page-options/index.js b/packages/edit-post/src/components/start-page-options/index.js
index 0ef3e166e8ee1..872594a260f33 100644
--- a/packages/edit-post/src/components/start-page-options/index.js
+++ b/packages/edit-post/src/components/start-page-options/index.js
@@ -21,28 +21,35 @@ function useStartPatterns() {
 	// A pattern is a start pattern if it includes 'core/post-content' in its blockTypes,
 	// and it has no postTypes declared and the current post type is page or if
 	// the current post type is part of the postTypes declared.
-	const { blockPatternsWithPostContentBlockType, postType } = useSelect(
-		( select ) => {
-			const { getPatternsByBlockTypes } = select( blockEditorStore );
-			const { getCurrentPostType } = select( editorStore );
-			return {
-				blockPatternsWithPostContentBlockType:
-					getPatternsByBlockTypes( 'core/post-content' ),
-				postType: getCurrentPostType(),
-			};
-		},
+	const [ blockPatternsWithPostContentBlockType ] = useSelect(
+		( select ) => [
+			select( blockEditorStore ).getPatternsByBlockTypes(
+				'core/post-content'
+			),
+		],
+		[]
+	);
+
+	const [ postType ] = useSelect(
+		( select ) => [ select( editorStore ).getCurrentPostType() ],
 		[]
 	);
 
 	return useMemo( () => {
+		if ( ! blockPatternsWithPostContentBlockType ) {
+			return null;
+		}
+
 		// filter patterns without postTypes declared if the current postType is page
 		// or patterns that declare the current postType in its post type array.
 		return blockPatternsWithPostContentBlockType.filter( ( pattern ) => {
-			return (
-				( postType === 'page' && ! pattern.postTypes ) ||
-				( Array.isArray( pattern.postTypes ) &&
-					pattern.postTypes.includes( postType ) )
-			);
+			if ( ! pattern.postTypes ) {
+				return postType === 'page';
+			}
+			if ( Array.isArray( pattern.postTypes ) ) {
+				return pattern.postTypes.includes( postType );
+			}
+			return false;
 		} );
 	}, [ postType, blockPatternsWithPostContentBlockType ] );
 }
diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js
index 5dbc28ea85947..4c53660283ea8 100644
--- a/packages/edit-post/src/editor.js
+++ b/packages/edit-post/src/editor.js
@@ -51,7 +51,7 @@ function Editor( { postId, postType, settings, initialEdits, ...props } ) {
 			const { getEntityRecord, getPostType, getEntityRecords, canUser } =
 				select( coreStore );
 			const { getEditorSettings } = select( editorStore );
-			const { getBlockTypes } = select( blocksStore );
+			const { getBootstrappedBlockTypes } = select( blocksStore );
 			const isTemplate = [ 'wp_template', 'wp_template_part' ].includes(
 				postType
 			);
@@ -84,7 +84,7 @@ function Editor( { postId, postType, settings, initialEdits, ...props } ) {
 					'preferredStyleVariations'
 				),
 				hiddenBlockTypes: getHiddenBlockTypes(),
-				blockTypes: getBlockTypes(),
+				blockTypes: getBootstrappedBlockTypes(),
 				keepCaretInsideBlock: isFeatureActive( 'keepCaretInsideBlock' ),
 				template:
 					supportsTemplateMode && isViewable && canEditTemplate
diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js
index ffe55e50efab0..46f5f9e732813 100644
--- a/packages/edit-post/src/index.js
+++ b/packages/edit-post/src/index.js
@@ -1,20 +1,18 @@
 /**
  * WordPress dependencies
  */
-import { store as blocksStore } from '@wordpress/blocks';
 import {
-	registerCoreBlocks,
-	__experimentalRegisterExperimentalCoreBlocks,
-} from '@wordpress/block-library';
+	store as blocksStore,
+	setDefaultBlockName,
+	setFreeformContentHandlerName,
+	setUnregisteredTypeHandlerName,
+	setGroupingBlockName,
+} from '@wordpress/blocks';
 import deprecated from '@wordpress/deprecated';
 import { createRoot } from '@wordpress/element';
 import { dispatch, select } from '@wordpress/data';
 import { addFilter } from '@wordpress/hooks';
 import { store as preferencesStore } from '@wordpress/preferences';
-import {
-	registerLegacyWidgetBlock,
-	registerWidgetGroupBlock,
-} from '@wordpress/widgets';
 import {
 	privateApis as editorPrivateApis,
 	store as editorStore,
@@ -82,14 +80,12 @@ export function initializeEditor(
 		dispatch( editPostStore ).setIsListViewOpened( true );
 	}
 
-	registerCoreBlocks();
-	registerLegacyWidgetBlock( { inserter: false } );
-	registerWidgetGroupBlock( { inserter: false } );
-	if ( process.env.IS_GUTENBERG_PLUGIN ) {
-		__experimentalRegisterExperimentalCoreBlocks( {
-			enableFSEBlocks: settings.__unstableEnableFullSiteEditingBlocks,
-		} );
+	setDefaultBlockName( 'core/paragraph' );
+	if ( window.wp && window.wp.oldEditor ) {
+		setFreeformContentHandlerName( 'core/freeform' );
 	}
+	setUnregisteredTypeHandlerName( 'core/missing' );
+	setGroupingBlockName( 'core/group' );
 
 	/*
 	 * Prevent adding template part in the post editor.
diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js
index 19508f0a59f8e..8274c44323f89 100644
--- a/packages/edit-site/src/components/style-book/index.js
+++ b/packages/edit-site/src/components/style-book/index.js
@@ -14,7 +14,7 @@ import {
 import { __, sprintf } from '@wordpress/i18n';
 import {
 	getCategories,
-	getBlockTypes,
+	getBootstrappedBlockTypes,
 	getBlockFromExample,
 	createBlock,
 } from '@wordpress/blocks';
@@ -149,7 +149,7 @@ function getExamples() {
 		],
 	};
 
-	const otherExamples = getBlockTypes()
+	const otherExamples = getBootstrappedBlockTypes()
 		.filter( ( blockType ) => {
 			const { name, example, supports } = blockType;
 			return (
diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js
index 6397a31af120b..41564e57fdbf2 100644
--- a/packages/edit-site/src/store/actions.js
+++ b/packages/edit-site/src/store/actions.js
@@ -116,7 +116,7 @@ export const addTemplate =
 					'postType',
 					TEMPLATE_POST_TYPE,
 					newTemplate.id,
-					{ blocks: parse( template.content ) },
+					{ blocks: await parse( template.content ) },
 					{ undoIgnore: true }
 				);
 		}
diff --git a/packages/editor/src/components/local-autosave-monitor/index.js b/packages/editor/src/components/local-autosave-monitor/index.js
index 185497710f35f..f21254a86ffb3 100644
--- a/packages/editor/src/components/local-autosave-monitor/index.js
+++ b/packages/editor/src/components/local-autosave-monitor/index.js
@@ -112,13 +112,13 @@ function useAutosaveNotice() {
 				actions: [
 					{
 						label: __( 'Restore the backup' ),
-						onClick() {
+						async onClick() {
 							const {
 								content: editsContent,
 								...editsWithoutContent
 							} = edits;
 							editPost( editsWithoutContent );
-							resetEditorBlocks( parse( edits.content ) );
+							resetEditorBlocks( await parse( edits.content ) );
 							removeNotice( id );
 						},
 					},
diff --git a/packages/widgets/src/blocks/legacy-widget/block.json b/packages/widgets/src/blocks/legacy-widget/block.json
index 6b0c1e2a916fd..2bd2751703c5c 100644
--- a/packages/widgets/src/blocks/legacy-widget/block.json
+++ b/packages/widgets/src/blocks/legacy-widget/block.json
@@ -23,6 +23,5 @@
 		"html": false,
 		"customClassName": false,
 		"reusable": false
-	},
-	"editorStyle": "wp-block-legacy-widget-editor"
+	}
 }
diff --git a/packages/widgets/src/blocks/widget-group/block.json b/packages/widgets/src/blocks/widget-group/block.json
index c29e811554ac1..73fbcded608d2 100644
--- a/packages/widgets/src/blocks/widget-group/block.json
+++ b/packages/widgets/src/blocks/widget-group/block.json
@@ -13,6 +13,5 @@
 		"customClassName": true,
 		"reusable": false
 	},
-	"editorStyle": "wp-block-widget-group-editor",
 	"style": "wp-block-widget-group"
 }
diff --git a/test/performance/fixtures/perf-utils.ts b/test/performance/fixtures/perf-utils.ts
index d17eec2c935b1..d6a02f072c25d 100644
--- a/test/performance/fixtures/perf-utils.ts
+++ b/test/performance/fixtures/perf-utils.ts
@@ -132,10 +132,10 @@ export class PerfUtils {
 			() => window?.wp?.blocks && window?.wp?.data
 		);
 
-		return await this.page.evaluate( ( html: string ) => {
+		return await this.page.evaluate( async ( html: string ) => {
 			const { parse } = window.wp.blocks;
 			const { dispatch } = window.wp.data;
-			const blocks = parse( html );
+			const blocks = await parse( html );
 
 			blocks.forEach( ( block: any ) => {
 				if ( block.name === 'core/image' ) {
diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js
index d599980b951a9..d72acbc4e78e3 100644
--- a/tools/webpack/blocks.js
+++ b/tools/webpack/blocks.js
@@ -75,6 +75,25 @@ const createEntrypoints = () => {
 	}, {} );
 };
 
+const createEditorEntrypoints = () => {
+	const blockInitScriptPaths = fastGlob.sync(
+		'./packages/block-library/build-module/**/init.js'
+	);
+
+	const entrypoints = {};
+	for ( const scriptPath of blockInitScriptPaths ) {
+		const result = scriptPath.match(
+			/build-module\/(?<filename>.*)\/init\.js$/
+		);
+		if ( ! result ) {
+			continue;
+		}
+		entrypoints[ result.groups.filename + '/editor' ] = scriptPath;
+	}
+
+	return entrypoints;
+};
+
 module.exports = [
 	{
 		...baseConfig,
@@ -220,4 +239,17 @@ module.exports = [
 			} ),
 		].filter( Boolean ),
 	},
+	{
+		...baseConfig,
+		name: 'editor',
+		entry: createEditorEntrypoints(),
+		output: {
+			devtoolNamespace: 'wp',
+			filename: './build/block-library/blocks/[name].min.js',
+			path: join( __dirname, '..', '..' ),
+		},
+		plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ].filter(
+			Boolean
+		),
+	},
 ];