diff --git Gruntfile.js Gruntfile.js
index e470da5cd..663ac7963 100644
--- Gruntfile.js
+++ Gruntfile.js
@@ -49,7 +49,8 @@ module.exports = function( grunt ) {
 			'!bp-friends/css/blocks/friends.css',
 			'!bp-members/css/blocks/dynamic-members.css',
 			'!bp-groups/css/blocks/dynamic-groups.css',
-			'!bp-messages/css/blocks/sitewide-notices.css'
+			'!bp-messages/css/blocks/sitewide-notices.css',
+			'!bp-blogs/css/blocks/recent-posts.css'
 		],
 
 		autoprefixer = require('autoprefixer');
@@ -195,6 +196,15 @@ module.exports = function( grunt ) {
 				flatten: true,
 				src: ['bp-messages/sass/blocks/*.scss'],
 				dest: SOURCE_DIR + 'bp-messages/css/blocks/'
+			},
+			blogs_blocks: {
+				cwd: SOURCE_DIR,
+				extDot: 'last',
+				expand: true,
+				ext: '.css',
+				flatten: true,
+				src: ['bp-blogs/sass/blocks/*.scss'],
+				dest: SOURCE_DIR + 'bp-blogs/css/blocks/'
 			}
 		},
 		rtlcss: {
diff --git src/bp-blogs/bp-blogs-blocks.php src/bp-blogs/bp-blogs-blocks.php
new file mode 100644
index 000000000..47bf94596
--- /dev/null
+++ src/bp-blogs/bp-blogs-blocks.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * BP Blogs Blocks Functions.
+ *
+ * @package BuddyPress
+ * @subpackage BlogsBlocks
+ * @since 9.0.0
+ */
+
+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+/**
+ * Callback function to render the Recent Posts Block.
+ *
+ * @since 9.0.0
+ *
+ * @param array $attributes The block attributes.
+ * @return string           HTML output.
+ */
+function bp_blogs_render_recent_posts_block( $attributes = array() ) {
+	$block_args = wp_parse_args(
+		$attributes,
+		array(
+			'title'     => __( 'Recent Networkwide Posts', 'buddypress' ),
+			'maxPosts'  => 10,
+			'linkTitle' => false,
+		)
+	);
+
+	$classnames           = 'widget_bp_blogs_widget buddypress widget';
+	$wrapper_attributes   = get_block_wrapper_attributes( array( 'class' => $classnames ) );
+	$blogs_directory_link = bp_get_blogs_directory_permalink();
+	$max_posts            = (int) $block_args['maxPosts'];
+	$no_posts             = __( 'Sorry, there were no posts found.', 'buddypress' );
+
+	// Set the Block's title.
+	if ( true === $block_args['linkTitle'] ) {
+		$widget_content = sprintf(
+			'<h2 class="widget-title"><a href="%1$s">%2$s</a></h2>',
+			esc_url( $blogs_directory_link ),
+			esc_html( $block_args['title'] )
+		);
+	} else {
+		$widget_content = sprintf( '<h2 class="widget-title">%s</h2>', esc_html( $block_args['title'] ) );
+	}
+
+	$blog_activities = bp_activity_get(
+		array(
+			'max'      => $max_posts,
+			'per_page' => $max_posts,
+			'user_id'  => 0,
+			'scope'    => false,
+			'filter'   => array(
+				'object'     => false,
+				'primary_id' => false,
+				'action'     => 'new_blog_post',
+			),
+		)
+	);
+
+	$blog_activities = reset( $blog_activities );
+
+	if ( ! $blog_activities ) {
+		$widget_content .= sprintf( '<div class="widget-error">%s</div>', $no_posts );
+	} else {
+		// Avoid conflicts with other activity loops.
+		$reset_activities_template = null;
+		if ( ! empty( $GLOBALS['activities_template'] ) ) {
+			$reset_activities_template = $GLOBALS['activities_template'];
+		}
+
+		$GLOBALS['activities_template'] = new \stdClass();
+		$activities                     = array();
+
+		foreach ( $blog_activities as $blog_activity ) {
+			$activity_content                         = '';
+			$GLOBALS['activities_template']->activity = $blog_activity;
+
+			if ( $blog_activity->content ) {
+				/** This filter is documented in bp-activity/bp-activity-template.php. */
+				$activity_content = apply_filters_ref_array( 'bp_get_activity_content_body', array( $blog_activity->content, &$blog_activity ) );
+				$activity_content = sprintf(
+					'<div class="activity-inner">%s</div>',
+					$activity_content
+				);
+			}
+
+			/** This filter is documented in bp-activity/bp-activity-template.php. */
+			$actity_action = apply_filters_ref_array(
+				'bp_get_activity_action',
+				array(
+					bp_insert_activity_meta( $blog_activity->action ),
+					&$blog_activity,
+					array( 'no_timestamp' => false ),
+				)
+			);
+
+			$activities[] = sprintf(
+				'<li>
+					<div class="activity-content">
+						<div class="activity-header">%1$s</div>
+						%2$s
+					</div>
+				</li>',
+				$actity_action,
+				$activity_content
+			);
+		}
+
+		// Reset the global template loop.
+		$GLOBALS['activities_template'] = $reset_activities_template;
+
+		$widget_content .= sprintf(
+			'<ul class="activity-list item-list">
+				%s
+			</ul>',
+			implode( "\n", $activities )
+		);
+	}
+
+	// Adds a container to make sure the block is styled even when used into the Columns parent block.
+	$widget_content = sprintf( '<div class="bp-recent-posts-block-container">%s</div>', "\n" . $widget_content . "\n" );
+
+	// Only add a block wrapper if not loaded into a Widgets sidebar.
+	if ( ! did_action( 'dynamic_sidebar_before' ) ) {
+		return sprintf(
+			'<div %1$s>%2$s</div>',
+			$wrapper_attributes,
+			$widget_content
+		);
+	}
+
+	return $widget_content;
+}
diff --git src/bp-blogs/classes/class-bp-blogs-component.php src/bp-blogs/classes/class-bp-blogs-component.php
index fa4bae095..fb963935b 100644
--- src/bp-blogs/classes/class-bp-blogs-component.php
+++ src/bp-blogs/classes/class-bp-blogs-component.php
@@ -81,6 +81,11 @@ class BP_Blogs_Component extends BP_Component {
 			'autocomplete_all'      => defined( 'BP_MESSAGES_AUTOCOMPLETE_ALL' ),
 			'global_tables'         => $global_tables,
 			'meta_tables'           => $meta_tables,
+			'block_globals'         => array(
+				'bp/recent-posts' => array(
+					'widget_classnames' => array( 'widget_bp_blogs_widget', 'buddypress' ),
+				),
+			),
 		);
 
 		// Setup the globals.
@@ -136,6 +141,7 @@ class BP_Blogs_Component extends BP_Component {
 
 		if ( is_multisite() ) {
 			$includes[] = 'widgets';
+			$includes[] = 'blocks';
 		}
 
 		// Include the files.
@@ -389,6 +395,41 @@ class BP_Blogs_Component extends BP_Component {
 	 *                      description.
 	 */
 	public function blocks_init( $blocks = array() ) {
-		parent::blocks_init( array() );
+		$blocks = array();
+
+		if ( is_multisite() ) {
+			$blocks['bp/recent-posts'] = array(
+				'name'               => 'bp/recent-posts',
+				'editor_script'      => 'bp-recent-posts-block',
+				'editor_script_url'  => plugins_url( 'js/blocks/recent-posts.js', dirname( __FILE__ ) ),
+				'editor_script_deps' => array(
+					'wp-blocks',
+					'wp-element',
+					'wp-components',
+					'wp-i18n',
+					'wp-block-editor',
+					'bp-block-components',
+				),
+				'style'              => 'bp-recent-posts-block',
+				'style_url'          => plugins_url( 'css/blocks/recent-posts.css', dirname( __FILE__ ) ),
+				'attributes'         => array(
+					'title'     => array(
+						'type'    => 'string',
+						'default' => __( 'Recent Networkwide Posts', 'buddypress' ),
+					),
+					'maxPosts'  => array(
+						'type'    => 'number',
+						'default' => 10,
+					),
+					'linkTitle' => array(
+						'type'    => 'boolean',
+						'default' => false,
+					),
+				),
+				'render_callback'    => 'bp_blogs_render_recent_posts_block',
+			);
+		}
+
+		parent::blocks_init( $blocks );
 	}
 }
diff --git src/bp-blogs/css/blocks/recent-posts-rtl.css src/bp-blogs/css/blocks/recent-posts-rtl.css
new file mode 100644
index 000000000..694dc3536
--- /dev/null
+++ src/bp-blogs/css/blocks/recent-posts-rtl.css
@@ -0,0 +1,42 @@
+.bp-recent-posts-block-container a {
+	box-shadow: none;
+	text-decoration: none;
+}
+
+.bp-recent-posts-block-container ul.item-list {
+	list-style: none;
+	margin: 10px 0;
+}
+
+.bp-recent-posts-block-container ul.activity-list {
+	padding: 0;
+}
+
+.bp-recent-posts-block-container ul.activity-list blockquote {
+	margin: 0 0 1.5em;
+	overflow: visible;
+	padding: 0 0.75em 0.75em 0;
+}
+
+.bp-recent-posts-block-container ul.activity-list img {
+	margin-bottom: 0.5em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li {
+	border-bottom: 1px solid #ccc;
+	margin-bottom: 1em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li .activity-header p {
+	margin-bottom: 0.5em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li .activity-header p .time-since {
+	font-size: 80%;
+	color: #767676;
+	text-decoration: none;
+}
+
+.bp-recent-posts-block-container ul.activity-list li:last-child {
+	border-bottom: 0;
+}
diff --git src/bp-blogs/css/blocks/recent-posts.css src/bp-blogs/css/blocks/recent-posts.css
new file mode 100644
index 000000000..b6eb6adf0
--- /dev/null
+++ src/bp-blogs/css/blocks/recent-posts.css
@@ -0,0 +1,42 @@
+.bp-recent-posts-block-container a {
+	box-shadow: none;
+	text-decoration: none;
+}
+
+.bp-recent-posts-block-container ul.item-list {
+	list-style: none;
+	margin: 10px 0;
+}
+
+.bp-recent-posts-block-container ul.activity-list {
+	padding: 0;
+}
+
+.bp-recent-posts-block-container ul.activity-list blockquote {
+	margin: 0 0 1.5em;
+	overflow: visible;
+	padding: 0 0 0.75em 0.75em;
+}
+
+.bp-recent-posts-block-container ul.activity-list img {
+	margin-bottom: 0.5em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li {
+	border-bottom: 1px solid #ccc;
+	margin-bottom: 1em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li .activity-header p {
+	margin-bottom: 0.5em;
+}
+
+.bp-recent-posts-block-container ul.activity-list li .activity-header p .time-since {
+	font-size: 80%;
+	color: #767676;
+	text-decoration: none;
+}
+
+.bp-recent-posts-block-container ul.activity-list li:last-child {
+	border-bottom: 0;
+}
diff --git src/bp-blogs/js/blocks/recent-posts.js src/bp-blogs/js/blocks/recent-posts.js
new file mode 100644
index 000000000..69a2add7f
--- /dev/null
+++ src/bp-blogs/js/blocks/recent-posts.js
@@ -0,0 +1,284 @@
+// modules are defined as an array
+// [ module function, map of requires ]
+//
+// map of requires is short require name -> numeric require
+//
+// anything defined in a previous bundle is accessed via the
+// orig method which is the require for previous bundles
+parcelRequire = (function (modules, cache, entry, globalName) {
+  // Save the require from previous bundle to this closure if any
+  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
+  var nodeRequire = typeof require === 'function' && require;
+
+  function newRequire(name, jumped) {
+    if (!cache[name]) {
+      if (!modules[name]) {
+        // if we cannot find the module within our internal map or
+        // cache jump to the current global require ie. the last bundle
+        // that was added to the page.
+        var currentRequire = typeof parcelRequire === 'function' && parcelRequire;
+        if (!jumped && currentRequire) {
+          return currentRequire(name, true);
+        }
+
+        // If there are other bundles on this page the require from the
+        // previous one is saved to 'previousRequire'. Repeat this as
+        // many times as there are bundles until the module is found or
+        // we exhaust the require chain.
+        if (previousRequire) {
+          return previousRequire(name, true);
+        }
+
+        // Try the node require function if it exists.
+        if (nodeRequire && typeof name === 'string') {
+          return nodeRequire(name);
+        }
+
+        var err = new Error('Cannot find module \'' + name + '\'');
+        err.code = 'MODULE_NOT_FOUND';
+        throw err;
+      }
+
+      localRequire.resolve = resolve;
+      localRequire.cache = {};
+
+      var module = cache[name] = new newRequire.Module(name);
+
+      modules[name][0].call(module.exports, localRequire, module, module.exports, this);
+    }
+
+    return cache[name].exports;
+
+    function localRequire(x){
+      return newRequire(localRequire.resolve(x));
+    }
+
+    function resolve(x){
+      return modules[name][1][x] || x;
+    }
+  }
+
+  function Module(moduleName) {
+    this.id = moduleName;
+    this.bundle = newRequire;
+    this.exports = {};
+  }
+
+  newRequire.isParcelRequire = true;
+  newRequire.Module = Module;
+  newRequire.modules = modules;
+  newRequire.cache = cache;
+  newRequire.parent = previousRequire;
+  newRequire.register = function (id, exports) {
+    modules[id] = [function (require, module) {
+      module.exports = exports;
+    }, {}];
+  };
+
+  var error;
+  for (var i = 0; i < entry.length; i++) {
+    try {
+      newRequire(entry[i]);
+    } catch (e) {
+      // Save first error but execute all entries
+      if (!error) {
+        error = e;
+      }
+    }
+  }
+
+  if (entry.length) {
+    // Expose entry point to Node, AMD or browser globals
+    // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
+    var mainExports = newRequire(entry[entry.length - 1]);
+
+    // CommonJS
+    if (typeof exports === "object" && typeof module !== "undefined") {
+      module.exports = mainExports;
+
+    // RequireJS
+    } else if (typeof define === "function" && define.amd) {
+     define(function () {
+       return mainExports;
+     });
+
+    // <script>
+    } else if (globalName) {
+      this[globalName] = mainExports;
+    }
+  }
+
+  // Override the current require with this new one
+  parcelRequire = newRequire;
+
+  if (error) {
+    // throw error from earlier, _after updating parcelRequire_
+    throw error;
+  }
+
+  return newRequire;
+})({"Pfcj":[function(require,module,exports) {
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+/**
+ * WordPress dependencies.
+ */
+var _wp = wp,
+    InspectorControls = _wp.blockEditor.InspectorControls,
+    _wp$components = _wp.components,
+    Disabled = _wp$components.Disabled,
+    PanelBody = _wp$components.PanelBody,
+    RangeControl = _wp$components.RangeControl,
+    TextControl = _wp$components.TextControl,
+    ToggleControl = _wp$components.ToggleControl,
+    _wp$element = _wp.element,
+    Fragment = _wp$element.Fragment,
+    createElement = _wp$element.createElement,
+    __ = _wp.i18n.__;
+/**
+ * BuddyPress dependencies.
+ */
+
+var _bp = bp,
+    ServerSideRender = _bp.blockComponents.ServerSideRender;
+
+var editRecentPostsBlock = function editRecentPostsBlock(_ref) {
+  var attributes = _ref.attributes,
+      setAttributes = _ref.setAttributes;
+  var title = attributes.title,
+      maxPosts = attributes.maxPosts,
+      linkTitle = attributes.linkTitle;
+  return createElement(Fragment, null, createElement(InspectorControls, null, createElement(PanelBody, {
+    title: __('Settings', 'buddypress'),
+    initialOpen: true
+  }, createElement(TextControl, {
+    label: __('Title', 'buddypress'),
+    value: title,
+    onChange: function onChange(text) {
+      setAttributes({
+        title: text
+      });
+    }
+  }), createElement(RangeControl, {
+    label: __('Max posts to show', 'buddypress'),
+    value: maxPosts,
+    onChange: function onChange(value) {
+      return setAttributes({
+        maxPosts: value
+      });
+    },
+    min: 1,
+    max: 10,
+    required: true
+  }), createElement(ToggleControl, {
+    label: __('Link block title to Blogs directory', 'buddypress'),
+    checked: !!linkTitle,
+    onChange: function onChange() {
+      setAttributes({
+        linkTitle: !linkTitle
+      });
+    }
+  }))), createElement(Disabled, null, createElement(ServerSideRender, {
+    block: "bp/recent-posts",
+    attributes: attributes
+  })));
+};
+
+var _default = editRecentPostsBlock;
+exports.default = _default;
+},{}],"D8sC":[function(require,module,exports) {
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+
+/**
+ * WordPress dependencies.
+ */
+var _wp = wp,
+    createBlock = _wp.blocks.createBlock;
+/**
+ * Transforms Legacy Widget to Recent Posts Block.
+ *
+ * @type {Object}
+ */
+
+var transforms = {
+  from: [{
+    type: 'block',
+    blocks: ['core/legacy-widget'],
+    isMatch: function isMatch(_ref) {
+      var idBase = _ref.idBase,
+          instance = _ref.instance;
+
+      if (!(instance !== null && instance !== void 0 && instance.raw)) {
+        return false;
+      }
+
+      return idBase === 'bp_blogs_recent_posts_widget';
+    },
+    transform: function transform(_ref2) {
+      var instance = _ref2.instance;
+      return createBlock('bp/recent-posts', {
+        title: instance.raw.title,
+        maxPosts: instance.raw.max_posts,
+        linkTitle: instance.raw.link_title
+      });
+    }
+  }]
+};
+var _default = transforms;
+exports.default = _default;
+},{}],"PMBS":[function(require,module,exports) {
+"use strict";
+
+var _edit = _interopRequireDefault(require("./recent-posts/edit"));
+
+var _transforms = _interopRequireDefault(require("./recent-posts/transforms"));
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * WordPress dependencies.
+ */
+var _wp = wp,
+    registerBlockType = _wp.blocks.registerBlockType,
+    __ = _wp.i18n.__;
+/**
+ * Internal dependencies.
+ */
+
+registerBlockType('bp/recent-posts', {
+  title: __('Recent Networkwide Posts', 'buddypress'),
+  description: __('A list of recently published posts from across your network.', 'buddypress'),
+  icon: {
+    background: '#fff',
+    foreground: '#d84800',
+    src: 'wordpress'
+  },
+  category: 'buddypress',
+  attributes: {
+    title: {
+      type: 'string',
+      default: __('Recent Networkwide Posts', 'buddypress')
+    },
+    maxPosts: {
+      type: 'number',
+      default: 10
+    },
+    linkTitle: {
+      type: 'boolean',
+      default: false
+    }
+  },
+  edit: _edit.default,
+  transforms: _transforms.default
+});
+},{"./recent-posts/edit":"Pfcj","./recent-posts/transforms":"D8sC"}]},{},["PMBS"], null)
\ No newline at end of file
diff --git src/bp-blogs/sass/blocks/recent-posts.scss src/bp-blogs/sass/blocks/recent-posts.scss
new file mode 100644
index 000000000..efc8ced7f
--- /dev/null
+++ src/bp-blogs/sass/blocks/recent-posts.scss
@@ -0,0 +1,46 @@
+.bp-recent-posts-block-container {
+
+	a {
+		box-shadow: none;
+		text-decoration: none;
+	}
+
+	ul.item-list {
+		list-style: none;
+		margin: 10px 0;
+	}
+
+	ul.activity-list {
+
+		padding: 0;
+
+		blockquote {
+			margin: 0 0 1.5em;
+			overflow: visible;
+			padding: 0 0 0.75em 0.75em;
+		}
+
+		img {
+			margin-bottom: 0.5em;
+		}
+
+		li {
+			border-bottom: 1px solid #ccc;
+			margin-bottom: 1em;
+
+			.activity-header p {
+				margin-bottom: 0.5em;
+
+				.time-since {
+					font-size: 80%;
+					color: #767676;
+					text-decoration: none;
+				}
+			}
+
+			&:last-child {
+				border-bottom: 0;
+			}
+		}
+	}
+}
diff --git src/bp-core/classes/class-bp-core.php src/bp-core/classes/class-bp-core.php
index 0a010c2a9..2a8e56faf 100644
--- src/bp-core/classes/class-bp-core.php
+++ src/bp-core/classes/class-bp-core.php
@@ -296,8 +296,8 @@ class BP_Core extends BP_Component {
 				'block_globals' => array(
 					'bp/login-form' => array(
 						'widget_classnames' => array( 'widget_bp_core_login_widget', 'buddypress' ),
-					)
-				)
+					),
+				),
 			)
 		);
 	}
diff --git src/js/bp-blogs/js/blocks/.babelrc src/js/bp-blogs/js/blocks/.babelrc
new file mode 100644
index 000000000..ab258a69c
--- /dev/null
+++ src/js/bp-blogs/js/blocks/.babelrc
@@ -0,0 +1,3 @@
+{
+	"presets": ["@wordpress/default"]
+}
diff --git src/js/bp-blogs/js/blocks/recent-posts.js src/js/bp-blogs/js/blocks/recent-posts.js
new file mode 100644
index 000000000..5c5cc2094
--- /dev/null
+++ src/js/bp-blogs/js/blocks/recent-posts.js
@@ -0,0 +1,44 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		registerBlockType,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * Internal dependencies.
+ */
+import editRecentPostsBlock from './recent-posts/edit';
+import transforms from './recent-posts/transforms';
+
+registerBlockType( 'bp/recent-posts', {
+	title: __( 'Recent Networkwide Posts', 'buddypress' ),
+	description: __( 'A list of recently published posts from across your network.', 'buddypress' ),
+	icon: {
+		background: '#fff',
+		foreground: '#d84800',
+		src: 'wordpress',
+	},
+	category: 'buddypress',
+	attributes: {
+		title: {
+			type: 'string',
+			default: __( 'Recent Networkwide Posts', 'buddypress' ),
+		},
+		maxPosts: {
+			type: 'number',
+			default: 10
+		},
+		linkTitle: {
+			type: 'boolean',
+			default: false,
+		},
+	},
+	edit: editRecentPostsBlock,
+	transforms: transforms,
+} );
diff --git src/js/bp-blogs/js/blocks/recent-posts/edit.js src/js/bp-blogs/js/blocks/recent-posts/edit.js
new file mode 100644
index 000000000..921087ac0
--- /dev/null
+++ src/js/bp-blogs/js/blocks/recent-posts/edit.js
@@ -0,0 +1,73 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blockEditor: {
+		InspectorControls,
+	},
+	components: {
+		Disabled,
+		PanelBody,
+		RangeControl,
+		TextControl,
+		ToggleControl,
+	},
+	element: {
+		Fragment,
+		createElement,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * BuddyPress dependencies.
+ */
+const {
+	blockComponents: {
+		ServerSideRender,
+	},
+} = bp;
+
+const editRecentPostsBlock = ( { attributes, setAttributes } ) => {
+	const { title, maxPosts, linkTitle } = attributes;
+
+	return (
+		<Fragment>
+			<InspectorControls>
+				<PanelBody title={ __( 'Settings', 'buddypress' ) } initialOpen={ true }>
+					<TextControl
+						label={ __( 'Title', 'buddypress' ) }
+						value={ title }
+						onChange={ ( text ) => {
+							setAttributes( { title: text } );
+						} }
+					/>
+					<RangeControl
+						label={ __( 'Max posts to show', 'buddypress' ) }
+						value={ maxPosts }
+						onChange={ ( value ) =>
+							setAttributes( { maxPosts: value } )
+						}
+						min={ 1 }
+						max={ 10 }
+						required
+					/>
+					<ToggleControl
+						label={ __( 'Link block title to Blogs directory', 'buddypress' ) }
+						checked={ !! linkTitle }
+						onChange={ () => {
+							setAttributes( { linkTitle: ! linkTitle } );
+						} }
+					/>
+				</PanelBody>
+			</InspectorControls>
+			<Disabled>
+				<ServerSideRender block="bp/recent-posts" attributes={ attributes } />
+			</Disabled>
+		</Fragment>
+	);
+};
+
+export default editRecentPostsBlock;
diff --git src/js/bp-blogs/js/blocks/recent-posts/transforms.js src/js/bp-blogs/js/blocks/recent-posts/transforms.js
new file mode 100644
index 000000000..04d227fcf
--- /dev/null
+++ src/js/bp-blogs/js/blocks/recent-posts/transforms.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		createBlock,
+	},
+} = wp;
+
+/**
+ * Transforms Legacy Widget to Recent Posts Block.
+ *
+ * @type {Object}
+ */
+const transforms = {
+	from: [
+		{
+			type: 'block',
+			blocks: [ 'core/legacy-widget' ],
+			isMatch: ( { idBase, instance } ) => {
+				if ( ! instance?.raw ) {
+					return false;
+				}
+
+				return idBase === 'bp_blogs_recent_posts_widget';
+			},
+			transform: ( { instance } ) => {
+				return createBlock( 'bp/recent-posts', {
+					title: instance.raw.title,
+					maxPosts: instance.raw.max_posts,
+					linkTitle: instance.raw.link_title,
+				} );
+			},
+		},
+	],
+};
+
+export default transforms;
