diff --git Gruntfile.js Gruntfile.js
index 663ac7963..e7a348f6d 100644
--- Gruntfile.js
+++ Gruntfile.js
@@ -50,7 +50,9 @@ module.exports = function( grunt ) {
 			'!bp-members/css/blocks/dynamic-members.css',
 			'!bp-groups/css/blocks/dynamic-groups.css',
 			'!bp-messages/css/blocks/sitewide-notices.css',
-			'!bp-blogs/css/blocks/recent-posts.css'
+			'!bp-blogs/css/blocks/recent-posts.css',
+			'!bp-members/css/blocks/active-members.css',
+			'!bp-members/css/blocks/online-members.css'
 		],
 
 		autoprefixer = require('autoprefixer');
diff --git src/bp-members/bp-members-blocks.php src/bp-members/bp-members-blocks.php
index 467c51ff9..ae9cd93bf 100644
--- src/bp-members/bp-members-blocks.php
+++ src/bp-members/bp-members-blocks.php
@@ -555,3 +555,170 @@ function bp_members_render_dynamic_members_block( $attributes = array() ) {
 
 	return $widget_content;
 }
+
+/**
+ * Common function to render the Recently Active & Online Members Blocks.
+ *
+ * @since 9.0.0
+ *
+ * @param array $block_args {
+ *    Optional. An array of Block arguments.
+ *
+ *    @type string $title      The title of the Block.
+ *    @type int    $maxMembers The maximum number of members to show. Defaults to `0`.
+ *    @type string $noMembers  The string to output when there are no members to show.
+ *    @type string $classname  The name of the CSS class to use.
+ *    @type string $type       The type of filter to perform. Possible values are `online`, `active`,
+ *                             `newest`, `alphabetical`, `random` or `popular`.
+ * }
+ * @return string HTML output.
+ */
+function bp_members_render_members_avatars_block( $block_args = array() ) {
+	$args = bp_parse_args(
+		$block_args,
+		array(
+			'title'      => '',
+			'maxMembers' => 0,
+			'noMembers'  => '',
+			'classname'  => '',
+			'type'       => 'active',
+		),
+		''
+	);
+
+	$title              = $args['title'];
+	$max_members        = (int) $args['maxMembers'];
+	$no_members         = $args['noMembers'];
+	$classname          = sanitize_key( $args['classname'] );
+	$wrapper_attributes = get_block_wrapper_attributes(
+		array(
+			'class' => sprintf( '%s buddypress widget', $classname ),
+		)
+	);
+	$type               = sanitize_key( $args['type'] );
+
+	if ( $title ) {
+		$widget_content = sprintf( '<h2 class="widget-title">%s</h2>', esc_html( $title ) );
+	} else {
+		$widget_content = '';
+	}
+
+	// Query Users.
+	$query = bp_core_get_users(
+		array(
+			'user_id'         => 0,
+			'type'            => $type,
+			'per_page'        => $max_members,
+			'max'             => $max_members,
+			'populate_extras' => true,
+			'search_terms'    => false,
+		)
+	);
+
+	// Build the output for online members.
+	if ( isset( $query['total'] ) && 1 <= (int) $query['total'] ) {
+		$members        = $query['users'];
+		$member_avatars = array();
+
+		foreach ( $members as $member ) {
+			$member_avatars[] = sprintf(
+				'<div class="item-avatar">
+					<a href="%1$s" class="bp-tooltip" data-bp-tooltip="%2$s">
+						<img loading="lazy" src="%3$s" class="avatar user-%4$s-avatar avatar-50 photo" width="50" height="50" alt="%5$s">
+					</a>
+				</div>',
+				esc_url( bp_core_get_user_domain( $member->ID, $member->user_nicename, $member->user_login ) ),
+				esc_html( $member->display_name ),
+				bp_core_fetch_avatar(
+					array(
+						'item_id' => $member->ID,
+						'html'    => false,
+					)
+				),
+				esc_attr( $member->ID ),
+				esc_html(
+					sprintf(
+						/* translators: %s: member name */
+						__( 'Profile picture of %s', 'buddypress' ),
+						$member->display_name
+					)
+				)
+			);
+		}
+
+		$widget_content .= sprintf(
+			'<div class="avatar-block">
+				%s
+			</div>',
+			implode( "\n", $member_avatars )
+		);
+	} else {
+		$widget_content .= sprintf(
+			'<div class="widget-error">
+				%s
+			</div>',
+			esc_html( $no_members )
+		);
+	}
+
+	// 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;
+}
+
+/**
+ * Callback function to render the Online Members Block.
+ *
+ * @since 9.0.0
+ *
+ * @param array $attributes The block attributes.
+ * @return string           HTML output.
+ */
+function bp_members_render_online_members_block( $attributes = array() ) {
+	$block_args = bp_parse_args(
+		$attributes,
+		array(
+			'title'      => __( 'Who\'s Online', 'buddypress' ),
+			'maxMembers' => 15,
+			'noMembers'  => __( 'There are no users currently online', 'buddypress' ),
+			'classname'  => 'widget_bp_core_whos_online_widget',
+		),
+		'members_widget_settings'
+	);
+
+	$block_args['type'] = 'online';
+
+	return bp_members_render_members_avatars_block( $block_args );
+}
+
+/**
+ * Callback function to render the Recently Active Members Block.
+ *
+ * @since 9.0.0
+ *
+ * @param array $attributes The block attributes.
+ * @return string           HTML output.
+ */
+function bp_members_render_active_members_block( $attributes = array() ) {
+	$block_args = bp_parse_args(
+		$attributes,
+		array(
+			'title'      => __( 'Recently Active Members', 'buddypress' ),
+			'maxMembers' => 15,
+			'noMembers'  => __( 'There are no recently active members', 'buddypress' ),
+			'classname'  => 'widget_bp_core_recently_active_widget',
+		),
+		'recently_active_members_widget_settings'
+	);
+
+	$block_args['type'] = 'active';
+
+	return bp_members_render_members_avatars_block( $block_args );
+}
diff --git src/bp-members/classes/class-bp-members-component.php src/bp-members/classes/class-bp-members-component.php
index efa93300f..aa27c337b 100644
--- src/bp-members/classes/class-bp-members-component.php
+++ src/bp-members/classes/class-bp-members-component.php
@@ -200,7 +200,13 @@ class BP_Members_Component extends BP_Component {
 			'block_globals'         => array(
 				'bp/dynamic-members' => array(
 					'widget_classnames' => array( 'widget_bp_core_members_widget', 'buddypress' ),
-				)
+				),
+				'bp/online-members' => array(
+					'widget_classnames' => array( 'widget_bp_core_whos_online_widget', 'buddypress' ),
+				),
+				'bp/active-members' => array(
+					'widget_classnames' => array( 'widget_bp_core_recently_active_widget', 'buddypress' ),
+				),
 			),
 		);
 
@@ -862,6 +868,58 @@ class BP_Members_Component extends BP_Component {
 					),
 					'render_callback'    => 'bp_members_render_dynamic_members_block',
 				),
+				'bp/online-members'  => array(
+					'name'               => 'bp/online-members',
+					'editor_script'      => 'bp-online-members-block',
+					'editor_script_url'  => plugins_url( 'js/blocks/online-members.js', dirname( __FILE__ ) ),
+					'editor_script_deps' => array(
+						'wp-blocks',
+						'wp-element',
+						'wp-components',
+						'wp-i18n',
+						'wp-block-editor',
+						'bp-block-components',
+					),
+					'editor_style'       => 'bp-online-members-block',
+					'editor_style_url'   => plugins_url( 'css/blocks/online-members.css', dirname( __FILE__ ) ),
+					'attributes'         => array(
+						'title'      => array(
+							'type'    => 'string',
+							'default' => __( 'Who\'s Online', 'buddypress' ),
+						),
+						'maxMembers' => array(
+							'type'    => 'number',
+							'default' => 15,
+						),
+					),
+					'render_callback'    => 'bp_members_render_online_members_block',
+				),
+				'bp/active-members'  => array(
+					'name'               => 'bp/active-members',
+					'editor_script'      => 'bp-active-members-block',
+					'editor_script_url'  => plugins_url( 'js/blocks/active-members.js', dirname( __FILE__ ) ),
+					'editor_script_deps' => array(
+						'wp-blocks',
+						'wp-element',
+						'wp-components',
+						'wp-i18n',
+						'wp-block-editor',
+						'bp-block-components',
+					),
+					'editor_style'       => 'bp-active-members-block',
+					'editor_style_url'   => plugins_url( 'css/blocks/active-members.css', dirname( __FILE__ ) ),
+					'attributes'         => array(
+						'title'      => array(
+							'type'    => 'string',
+							'default' => __( 'Recently Active Members', 'buddypress' ),
+						),
+						'maxMembers' => array(
+							'type'    => 'number',
+							'default' => 15,
+						),
+					),
+					'render_callback'    => 'bp_members_render_active_members_block',
+				),
 			)
 		);
 	}
diff --git src/bp-members/css/blocks/active-members.css src/bp-members/css/blocks/active-members.css
new file mode 100644
index 000000000..ccf5bf1b9
--- /dev/null
+++ src/bp-members/css/blocks/active-members.css
@@ -0,0 +1,8 @@
+[data-type="bp/active-members"] .avatar-block {
+	display: flex;
+	flex-flow: row wrap;
+}
+
+[data-type="bp/active-members"] .avatar-block img {
+	margin: 0.5em;
+}
diff --git src/bp-members/css/blocks/online-members.css src/bp-members/css/blocks/online-members.css
new file mode 100644
index 000000000..44cc4783e
--- /dev/null
+++ src/bp-members/css/blocks/online-members.css
@@ -0,0 +1,8 @@
+[data-type="bp/online-members"] .avatar-block {
+	display: flex;
+	flex-flow: row wrap;
+}
+
+[data-type="bp/online-members"] .avatar-block img {
+	margin: 0.5em;
+}
diff --git src/bp-members/js/blocks/active-members.js src/bp-members/js/blocks/active-members.js
new file mode 100644
index 000000000..1ff2415e1
--- /dev/null
+++ src/bp-members/js/blocks/active-members.js
@@ -0,0 +1,269 @@
+// 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;
+})({"TOWc":[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,
+    _wp$element = _wp.element,
+    Fragment = _wp$element.Fragment,
+    createElement = _wp$element.createElement,
+    __ = _wp.i18n.__;
+/**
+ * BuddyPress dependencies.
+ */
+
+var _bp = bp,
+    ServerSideRender = _bp.blockComponents.ServerSideRender;
+
+var editActiveMembersBlock = function editActiveMembersBlock(_ref) {
+  var attributes = _ref.attributes,
+      setAttributes = _ref.setAttributes;
+  var title = attributes.title,
+      maxMembers = attributes.maxMembers;
+  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 members to show', 'buddypress'),
+    value: maxMembers,
+    onChange: function onChange(value) {
+      return setAttributes({
+        maxMembers: value
+      });
+    },
+    min: 1,
+    max: 15,
+    required: true
+  }))), createElement(Disabled, null, createElement(ServerSideRender, {
+    block: "bp/active-members",
+    attributes: attributes
+  })));
+};
+
+var _default = editActiveMembersBlock;
+exports.default = _default;
+},{}],"y7A5":[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 Active Members 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_core_recently_active_widget';
+    },
+    transform: function transform(_ref2) {
+      var instance = _ref2.instance;
+      return createBlock('bp/active-members', {
+        title: instance.raw.title,
+        maxMembers: instance.raw.max_members
+      });
+    }
+  }]
+};
+var _default = transforms;
+exports.default = _default;
+},{}],"dkrW":[function(require,module,exports) {
+"use strict";
+
+var _edit = _interopRequireDefault(require("./active-members/edit"));
+
+var _transforms = _interopRequireDefault(require("./active-members/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/active-members', {
+  title: __('Recently Active Members', 'buddypress'),
+  description: __('Profile photos of recently active members.', 'buddypress'),
+  icon: {
+    background: '#fff',
+    foreground: '#d84800',
+    src: 'groups'
+  },
+  category: 'buddypress',
+  attributes: {
+    title: {
+      type: 'string',
+      default: __('Recently Active Members', 'buddypress')
+    },
+    maxMembers: {
+      type: 'number',
+      default: 15
+    }
+  },
+  edit: _edit.default,
+  transforms: _transforms.default
+});
+},{"./active-members/edit":"TOWc","./active-members/transforms":"y7A5"}]},{},["dkrW"], null)
\ No newline at end of file
diff --git src/bp-members/js/blocks/online-members.js src/bp-members/js/blocks/online-members.js
new file mode 100644
index 000000000..21fbf528f
--- /dev/null
+++ src/bp-members/js/blocks/online-members.js
@@ -0,0 +1,269 @@
+// 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;
+})({"l6vy":[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,
+    _wp$element = _wp.element,
+    Fragment = _wp$element.Fragment,
+    createElement = _wp$element.createElement,
+    __ = _wp.i18n.__;
+/**
+ * BuddyPress dependencies.
+ */
+
+var _bp = bp,
+    ServerSideRender = _bp.blockComponents.ServerSideRender;
+
+var editOnlineMembersBlock = function editOnlineMembersBlock(_ref) {
+  var attributes = _ref.attributes,
+      setAttributes = _ref.setAttributes;
+  var title = attributes.title,
+      maxMembers = attributes.maxMembers;
+  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 members to show', 'buddypress'),
+    value: maxMembers,
+    onChange: function onChange(value) {
+      return setAttributes({
+        maxMembers: value
+      });
+    },
+    min: 1,
+    max: 15,
+    required: true
+  }))), createElement(Disabled, null, createElement(ServerSideRender, {
+    block: "bp/online-members",
+    attributes: attributes
+  })));
+};
+
+var _default = editOnlineMembersBlock;
+exports.default = _default;
+},{}],"VbKe":[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 Online Members 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_core_whos_online_widget';
+    },
+    transform: function transform(_ref2) {
+      var instance = _ref2.instance;
+      return createBlock('bp/online-members', {
+        title: instance.raw.title,
+        maxMembers: instance.raw.max_members
+      });
+    }
+  }]
+};
+var _default = transforms;
+exports.default = _default;
+},{}],"to6R":[function(require,module,exports) {
+"use strict";
+
+var _edit = _interopRequireDefault(require("./online-members/edit"));
+
+var _transforms = _interopRequireDefault(require("./online-members/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/online-members', {
+  title: __('Online Members', 'buddypress'),
+  description: __('Profile photos of online users.', 'buddypress'),
+  icon: {
+    background: '#fff',
+    foreground: '#d84800',
+    src: 'groups'
+  },
+  category: 'buddypress',
+  attributes: {
+    title: {
+      type: 'string',
+      default: __('Who\'s Online', 'buddypress')
+    },
+    maxMembers: {
+      type: 'number',
+      default: 15
+    }
+  },
+  edit: _edit.default,
+  transforms: _transforms.default
+});
+},{"./online-members/edit":"l6vy","./online-members/transforms":"VbKe"}]},{},["to6R"], null)
\ No newline at end of file
diff --git src/bp-members/sass/blocks/active-members.scss src/bp-members/sass/blocks/active-members.scss
new file mode 100644
index 000000000..f8c031ba9
--- /dev/null
+++ src/bp-members/sass/blocks/active-members.scss
@@ -0,0 +1,12 @@
+// BP Who's online widget
+[data-type="bp/active-members"] {
+
+	.avatar-block {
+		display: flex;
+		flex-flow: row wrap;
+
+		img {
+			margin: 0.5em;
+		}
+	}
+}
diff --git src/bp-members/sass/blocks/online-members.scss src/bp-members/sass/blocks/online-members.scss
new file mode 100644
index 000000000..913b90c16
--- /dev/null
+++ src/bp-members/sass/blocks/online-members.scss
@@ -0,0 +1,12 @@
+// BP Who's online widget
+[data-type="bp/online-members"] {
+
+	.avatar-block {
+		display: flex;
+		flex-flow: row wrap;
+
+		img {
+			margin: 0.5em;
+		}
+	}
+}
diff --git src/js/bp-members/js/blocks/active-members.js src/js/bp-members/js/blocks/active-members.js
new file mode 100644
index 000000000..ca63e845a
--- /dev/null
+++ src/js/bp-members/js/blocks/active-members.js
@@ -0,0 +1,40 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		registerBlockType,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * Internal dependencies.
+ */
+import editActiveMembersBlock from './active-members/edit';
+import transforms from './active-members/transforms';
+
+registerBlockType( 'bp/active-members', {
+	title: __( 'Recently Active Members', 'buddypress' ),
+	description: __( 'Profile photos of recently active members.', 'buddypress' ),
+	icon: {
+		background: '#fff',
+		foreground: '#d84800',
+		src: 'groups',
+	},
+	category: 'buddypress',
+	attributes: {
+		title: {
+			type: 'string',
+			default: __( 'Recently Active Members', 'buddypress' ),
+		},
+		maxMembers: {
+			type: 'number',
+			default: 15
+		},
+	},
+	edit: editActiveMembersBlock,
+	transforms: transforms,
+} );
diff --git src/js/bp-members/js/blocks/active-members/edit.js src/js/bp-members/js/blocks/active-members/edit.js
new file mode 100644
index 000000000..8b3722b22
--- /dev/null
+++ src/js/bp-members/js/blocks/active-members/edit.js
@@ -0,0 +1,65 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blockEditor: {
+		InspectorControls,
+	},
+	components: {
+		Disabled,
+		PanelBody,
+		RangeControl,
+		TextControl,
+	},
+	element: {
+		Fragment,
+		createElement,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * BuddyPress dependencies.
+ */
+const {
+	blockComponents: {
+		ServerSideRender,
+	},
+} = bp;
+
+const editActiveMembersBlock = ( { attributes, setAttributes } ) => {
+	const { title, maxMembers } = attributes;
+
+	return (
+		<Fragment>
+			<InspectorControls>
+				<PanelBody title={ __( 'Settings', 'buddypress' ) } initialOpen={ true }>
+					<TextControl
+						label={ __( 'Title', 'buddypress' ) }
+						value={ title }
+						onChange={ ( text ) => {
+							setAttributes( { title: text } );
+						} }
+					/>
+					<RangeControl
+						label={ __( 'Max members to show', 'buddypress' ) }
+						value={ maxMembers }
+						onChange={ ( value ) =>
+							setAttributes( { maxMembers: value } )
+						}
+						min={ 1 }
+						max={ 15 }
+						required
+					/>
+				</PanelBody>
+			</InspectorControls>
+			<Disabled>
+				<ServerSideRender block="bp/active-members" attributes={ attributes } />
+			</Disabled>
+		</Fragment>
+	);
+};
+
+export default editActiveMembersBlock;
diff --git src/js/bp-members/js/blocks/active-members/transforms.js src/js/bp-members/js/blocks/active-members/transforms.js
new file mode 100644
index 000000000..575c6b989
--- /dev/null
+++ src/js/bp-members/js/blocks/active-members/transforms.js
@@ -0,0 +1,37 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		createBlock,
+	},
+} = wp;
+
+/**
+ * Transforms Legacy Widget to Active Members Block.
+ *
+ * @type {Object}
+ */
+const transforms = {
+	from: [
+		{
+			type: 'block',
+			blocks: [ 'core/legacy-widget' ],
+			isMatch: ( { idBase, instance } ) => {
+				if ( ! instance?.raw ) {
+					return false;
+				}
+
+				return idBase === 'bp_core_recently_active_widget';
+			},
+			transform: ( { instance } ) => {
+				return createBlock( 'bp/active-members', {
+					title: instance.raw.title,
+					maxMembers: instance.raw.max_members,
+				} );
+			},
+		},
+	],
+};
+
+export default transforms;
diff --git src/js/bp-members/js/blocks/online-members.js src/js/bp-members/js/blocks/online-members.js
new file mode 100644
index 000000000..653f39c1f
--- /dev/null
+++ src/js/bp-members/js/blocks/online-members.js
@@ -0,0 +1,40 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		registerBlockType,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * Internal dependencies.
+ */
+import editOnlineMembersBlock from './online-members/edit';
+import transforms from './online-members/transforms';
+
+registerBlockType( 'bp/online-members', {
+	title: __( 'Online Members', 'buddypress' ),
+	description: __( 'Profile photos of online users.', 'buddypress' ),
+	icon: {
+		background: '#fff',
+		foreground: '#d84800',
+		src: 'groups',
+	},
+	category: 'buddypress',
+	attributes: {
+		title: {
+			type: 'string',
+			default: __( 'Who\'s Online', 'buddypress' ),
+		},
+		maxMembers: {
+			type: 'number',
+			default: 15
+		},
+	},
+	edit: editOnlineMembersBlock,
+	transforms: transforms,
+} );
diff --git src/js/bp-members/js/blocks/online-members/edit.js src/js/bp-members/js/blocks/online-members/edit.js
new file mode 100644
index 000000000..d2157a33f
--- /dev/null
+++ src/js/bp-members/js/blocks/online-members/edit.js
@@ -0,0 +1,65 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blockEditor: {
+		InspectorControls,
+	},
+	components: {
+		Disabled,
+		PanelBody,
+		RangeControl,
+		TextControl,
+	},
+	element: {
+		Fragment,
+		createElement,
+	},
+	i18n: {
+		__,
+	},
+} = wp;
+
+/**
+ * BuddyPress dependencies.
+ */
+const {
+	blockComponents: {
+		ServerSideRender,
+	},
+} = bp;
+
+const editOnlineMembersBlock = ( { attributes, setAttributes } ) => {
+	const { title, maxMembers } = attributes;
+
+	return (
+		<Fragment>
+			<InspectorControls>
+				<PanelBody title={ __( 'Settings', 'buddypress' ) } initialOpen={ true }>
+					<TextControl
+						label={ __( 'Title', 'buddypress' ) }
+						value={ title }
+						onChange={ ( text ) => {
+							setAttributes( { title: text } );
+						} }
+					/>
+					<RangeControl
+						label={ __( 'Max members to show', 'buddypress' ) }
+						value={ maxMembers }
+						onChange={ ( value ) =>
+							setAttributes( { maxMembers: value } )
+						}
+						min={ 1 }
+						max={ 15 }
+						required
+					/>
+				</PanelBody>
+			</InspectorControls>
+			<Disabled>
+				<ServerSideRender block="bp/online-members" attributes={ attributes } />
+			</Disabled>
+		</Fragment>
+	);
+};
+
+export default editOnlineMembersBlock;
diff --git src/js/bp-members/js/blocks/online-members/transforms.js src/js/bp-members/js/blocks/online-members/transforms.js
new file mode 100644
index 000000000..13d9fc1f2
--- /dev/null
+++ src/js/bp-members/js/blocks/online-members/transforms.js
@@ -0,0 +1,37 @@
+/**
+ * WordPress dependencies.
+ */
+const {
+	blocks: {
+		createBlock,
+	},
+} = wp;
+
+/**
+ * Transforms Legacy Widget to Online Members Block.
+ *
+ * @type {Object}
+ */
+const transforms = {
+	from: [
+		{
+			type: 'block',
+			blocks: [ 'core/legacy-widget' ],
+			isMatch: ( { idBase, instance } ) => {
+				if ( ! instance?.raw ) {
+					return false;
+				}
+
+				return idBase === 'bp_core_whos_online_widget';
+			},
+			transform: ( { instance } ) => {
+				return createBlock( 'bp/online-members', {
+					title: instance.raw.title,
+					maxMembers: instance.raw.max_members,
+				} );
+			},
+		},
+	],
+};
+
+export default transforms;
