Index: src/bp-core/bp-core-template.php
===================================================================
--- src/bp-core/bp-core-template.php
+++ src/bp-core/bp-core-template.php
@@ -1845,12 +1845,16 @@
 /** Components ****************************************************************/
 
 /**
- * Check whether a given component has been activated by the admin.
+ * Check whether a given component (or feature of a component) is active.
+ *
+ * @since BuddyPress (1.2.0) See r2539.
+ * @since BuddyPress (2.3.0) Added $feature as a parameter.
  *
  * @param string $component The component name.
- * @return bool True if the component is active, otherwise false.
+ * @param string $feature   The feature name.
+ * @return bool
  */
-function bp_is_active( $component = '' ) {
+function bp_is_active( $component = '', $feature = '' ) {
 	$retval = false;
 
 	// Default to the current component if none is passed
@@ -1861,6 +1865,17 @@
 	// Is component in either the active or required components arrays
 	if ( isset( buddypress()->active_components[ $component ] ) || isset( buddypress()->required_components[ $component ] ) ) {
 		$retval = true;
+
+		if ( ! empty( $feature ) ) {
+			/**
+			 * Filters whether or not a given feature for a component is active.
+			 *
+			 * @since BuddyPress (2.3.0)
+			 *
+			 * @param bool $retval Default is boolean true.
+			 */
+			$retval = apply_filters( "bp_is_{$component}_{$feature}_active", $retval );
+		}
 	}
 
 	/**
Index: src/bp-messages/bp-messages-functions.php
===================================================================
--- src/bp-messages/bp-messages-functions.php
+++ src/bp-messages/bp-messages-functions.php
@@ -366,6 +366,22 @@
 	return BP_Messages_Thread::is_valid( $thread_id );
 }
 
+/**
+ * Get the thread ID from a message ID.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param  int $message_id ID of the message.
+ * @return int The ID of the thread if found, otherwise 0.
+ */
+function messages_get_message_thread_id( $message_id = 0 ) {
+	global $wpdb;
+
+	$bp = buddypress();
+
+	return (int) $wpdb->get_var( $wpdb->prepare( "SELECT thread_id FROM {$bp->messages->table_name_messages} WHERE id = %d", $message_id ) );
+}
+
 /** Messages Meta *******************************************************/
 
 /**
Index: src/bp-messages/bp-messages-loader.php
===================================================================
--- src/bp-messages/bp-messages-loader.php
+++ src/bp-messages/bp-messages-loader.php
@@ -66,6 +66,11 @@
 			'widgets',
 		);
 
+		// Conditional includes
+		if ( bp_is_active( 'messages', 'star' ) ) {
+			$includes[] = 'star';
+		}
+
 		parent::includes( $includes );
 	}
 
@@ -164,6 +169,18 @@
 			'user_has_access' => bp_core_can_edit_settings()
 		);
 
+		if ( bp_is_active( 'messages', 'star' ) ) {
+			$sub_nav[] = array(
+				'name'            => __( 'Starred', 'buddypress' ),
+				'slug'            => bp_get_messages_starred_slug(),
+				'parent_url'      => $messages_link,
+				'parent_slug'     => $this->slug,
+				'screen_function' => 'bp_messages_star_screen',
+				'position'        => 11,
+				'user_has_access' => bp_core_can_edit_settings()
+			);
+		}
+
 		$sub_nav[] = array(
 			'name'            => __( 'Sent', 'buddypress' ),
 			'slug'            => 'sentbox',
@@ -241,6 +258,16 @@
 				'href'   => trailingslashit( $messages_link . 'inbox' )
 			);
 
+			// Starred
+			if ( bp_is_active( 'messages', 'star' ) ) {
+				$wp_admin_nav[] = array(
+					'parent' => 'my-account-' . $this->id,
+					'id'     => 'my-account-' . $this->id . '-starred',
+					'title'  => __( 'Starred', 'buddypress' ),
+					'href'   => trailingslashit( $messages_link . bp_get_messages_starred_slug() )
+				);
+			}
+
 			// Sent Messages
 			$wp_admin_nav[] = array(
 				'parent' => 'my-account-' . $this->id,
Index: src/bp-messages/bp-messages-star.php
new file mode 100644
===================================================================
--- /dev/null
+++ src/bp-messages/bp-messages-star.php
@@ -0,0 +1,490 @@
+<?php
+/**
+ * Functions related to starring private messages.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+
+/** UTILITY **************************************************************/
+
+/**
+ * Return the starred messages slug. Defaults to 'starred'.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @return string
+ */
+function bp_get_messages_starred_slug() {
+	/**
+	 * Filters the starred message slug.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @param string
+	 */
+	return sanitize_title( apply_filters( 'bp_get_messages_starred_slug', 'starred' ) );
+}
+
+/**
+ * Function to determine if a message ID is starred.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param  int $mid     The message ID. Please note that this isn't the message thread ID.
+ * @param  int $user_id The user ID
+ * @return bool
+ */
+function bp_messages_is_message_starred( $mid = 0, $user_id = 0 ) {
+	if ( empty( $user_id ) ) {
+		$user_id = bp_loggedin_user_id();
+	}
+
+	if ( empty( $mid ) ) {
+		return false;
+	}
+
+	$starred = array_flip( (array) bp_messages_get_meta( $mid, 'starred_by_user', false ) );
+
+	if ( isset( $starred[$user_id] ) ) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+/**
+ * Output the link or raw URL for starring or unstarring a message.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param array $args See bp_get_the_message_star_action_link() for full documentation.
+ */
+function bp_the_message_star_action_link( $args = array() ) {
+	echo bp_get_the_message_star_action_link( $args );
+}
+	/**
+	 * Return the link or raw URL for starring or unstarring a message.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @param array $args {
+	 *     Array of arguments.
+	 *     @type int    $user_id       The user ID. Defaults to the logged-in user ID.
+	 *     @type int    $thread_id     The message thread ID. Default: 0. If not zero, this takes precedence over
+	 *                                 $message_id.
+	 *     @type int    $message_id    The individual message ID. If on a single thread page, defaults to the
+	 *                                 current message ID in the message loop.
+	 *     @type bool   $url_only      Whether to return the URL only. If false, returns link with markup.
+	 *                                 Default: false.
+	 *     @type string $text_unstar   Link text for the 'unstar' action. Only applicable if $url_only is false.
+	 *     @type string $text_star     Link text for the 'star' action. Only applicable if $url_only is false.
+	 *     @type string $title_unstar  Link title for the 'unstar' action. Only applicable if $url_only is false.
+	 *     @type string $title_star    Link title for the 'star' action. Only applicable if $url_only is false.
+	 *     @type string $title_unstar_thread Link title for the 'unstar' action when displayed in a thread loop.
+	 *                                       Only applicable if $message_id is set and if $url_only is false.
+	 *     @type string $title_star_thread   Link title for the 'star' action when displayed in a thread loop.
+	 *                                       Only applicable if $message_id is set and if $url_only is false.
+	 }
+	 * @return string
+	 */
+	function bp_get_the_message_star_action_link( $args = array() ) {
+		$r = bp_parse_args( $args, array(
+			'user_id'      => bp_loggedin_user_id(),
+			'thread_id'    => 0,
+			'message_id'   => (int) bp_get_the_thread_message_id(),
+			'url_only'     => false,
+			'text_unstar'  => __( 'Unstar', 'buddypress' ),
+			'text_star'    => __( 'Star', 'buddypress' ),
+			'title_unstar' => __( 'Starred', 'buddypress' ),
+			'title_star'   => __( 'Not starred', 'buddypress' ),
+			'title_unstar_thread' => __( 'Remove all starred messages in this thread', 'buddypress' ),
+			'title_star_thread'   => __( 'Star the first message in this thread', 'buddypress' ),
+		), 'messages_star_action_link' );
+
+		$retval = $bulk_attr = '';
+
+		if ( 0 === $r['user_id'] ) {
+			return $retval;
+		}
+
+		// get user domain
+		if ( $r['user_id'] == bp_loggedin_user_id() ) {
+			$user_domain = bp_loggedin_user_domain();
+		} elseif ( $r['user_id'] == bp_displayed_user_domain() ) {
+			$user_domain = bp_displayed_user_domain();
+		} else {
+			$user_domain = bp_core_get_user_domain( $user_id );
+		}
+
+		// thread ID
+		if ( (int) $r['thread_id'] > 0 ) {
+			// see if we're in the loop
+			if ( bp_get_message_thread_id() == $r['thread_id'] ) {
+				// grab all message ids
+				$mids = wp_list_pluck( $GLOBALS['messages_template']->thread->messages, 'id' );
+
+				// make sure order is ASC
+				// order is DESC when used in the thread loop by default
+				$mids = array_reverse( $mids );
+
+			// pull up the thread
+			} else {
+				$thread = new BP_Messages_thread( $r['thread_id'] );
+				$mids = wp_list_pluck( $thread->messages, 'id' );
+			}
+
+			$is_starred = false;
+			$message_id = 0;
+			foreach ( $mids as $mid ) {
+				// try to find the first msg that is starred in a thread
+				if ( true === bp_messages_is_message_starred( $mid ) ) {
+					$is_starred = true;
+					$message_id = $mid;
+					break;
+				}
+			}
+
+			// no star, so default to first message in thread
+			if ( empty( $message_id ) ) {
+				$message_id = $mids[0];
+			}
+
+			// nonce
+			$nonce = wp_create_nonce( "bp-messages-star-{$message_id}" );
+
+			if ( $is_starred ) {
+				$action = 'unstar';
+				$bulk_attr = ' data-star-bulk="1"';
+				$retval = $user_domain . bp_get_messages_slug() . '/unstar/' . $message_id . '/' . $nonce . '/all/';
+			} else {
+				$action = 'star';
+				$retval = $user_domain . bp_get_messages_slug() . '/star/' . $message_id . '/' . $nonce . '/';
+			}
+
+			$title = $r["title_{$action}_thread"];
+
+		// message ID
+		} else {
+			$message_id = (int) $r['message_id'];
+			$is_starred = bp_messages_is_message_starred( $message_id );
+			$nonce      = wp_create_nonce( "bp-messages-star-{$message_id}" );
+
+			if ( $is_starred ) {
+				$action = 'unstar';
+				$retval = $user_domain . bp_get_messages_slug() . '/unstar/' . $message_id . '/' . $nonce . '/';
+			} else {
+				$action = 'star';
+				$retval = $user_domain . bp_get_messages_slug() . '/star/' . $message_id . '/' . $nonce . '/';
+			}
+
+			$title = $r["title_{$action}"];
+		}
+
+		/**
+		 * Filters the star action URL for starring / unstarring a message.
+		 *
+		 * @since BuddyPress (2.3.0)
+		 *
+		 * @param string $retval URL for starring / unstarring a message.
+		 * @param array  $r      Parsed link arguments. See $args in bp_get_the_message_star_action_link().
+		 */
+		$retval = esc_url( apply_filters( 'bp_get_the_message_star_action_urlonly', $retval, $r ) );
+		if ( true === (bool) $r['url_only'] ) {
+			return $retval;
+		}
+
+		/**
+		 * Filters the star action link, including markup.
+		 *
+		 * @since BuddyPress (2.3.0)
+		 *
+		 * @param string $retval Link for starring / unstarring a message, including markup.
+		 * @param array  $r      Parsed link arguments. See $args in bp_get_the_message_star_action_link().
+		 */
+		return apply_filters( 'bp_get_the_message_star_action_link', '<a title="' . esc_attr( $title ) . '" class="message-action-' . $action . '" data-star-status="' . $action .'" data-star-nonce="' . $nonce . '"' . $bulk_attr . ' data-message-id="' . esc_attr( (int) $message_id ) . '" href="' . $retval . '"><span class="icon"></span> <span class="bp-screen-reader-text">' . $r['text_' . $action] . '</span></a>', $r );
+	}
+
+/**
+ * Save or delete star message meta according to a message's star status.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param array $args {
+ *     Array of arguments.
+ *     @type string $action     The star action. Either 'star' or 'unstar'. Default: 'star'.
+ *     @type int    $thread_id  The message thread ID. Default: 0. If not zero, this takes precedence over
+ *                              $message_id.
+ *     @type int    $message_id The indivudal message ID to star or unstar.  Default: 0.
+ *     @type int    $user_id    The user ID. Defaults to the logged-in user ID.
+ *     @type bool   $bulk       Whether to mark all messages in a thread as a certain action. Only relevant
+ *                              when $action is 'unstar' at the moment. Default: false.
+ * }
+ * @return bool
+ */
+function bp_messages_star_set_action( $args = array() ) {
+	$r = wp_parse_args( $args, array(
+		'action'     => 'star',
+		'thread_id'  => 0,
+		'message_id' => 0,
+		'user_id'    => bp_loggedin_user_id(),
+		'bulk'       => false
+	) );
+
+	// Set thread ID
+	if ( ! empty( $r['thread_id'] ) ) {
+		$thread_id = (int) $r['thread_id'];
+	} else {
+		$thread_id = messages_get_message_thread_id( $r['message_id'] );
+	}
+	if ( empty( $thread_id ) ) {
+		return false;
+	}
+
+	// Check if user has access to thread
+	if( ! messages_check_thread_access( $thread_id, $r['user_id'] ) ) {
+		return false;
+	}
+
+	$is_starred = bp_messages_is_message_starred( $r['message_id'], $r['user_id'] );
+
+	// star
+	if ( 'star' == $r['action'] ) {
+		if ( true === $is_starred ) {
+			return true;
+		} else {
+			bp_messages_add_meta( $r['message_id'], 'starred_by_user', $r['user_id'] );
+			return true;
+		}
+	// unstar
+	} else {
+		// unstar one message
+		if ( false === $r['bulk'] ) {
+			if ( false === $is_starred ) {
+				return true;
+			} else {
+				bp_messages_delete_meta( $r['message_id'], 'starred_by_user', $r['user_id'] );
+				return true;
+			}
+
+		// unstar all messages in a thread
+		} else {
+			$thread = new BP_Messages_Thread( $thread_id );
+			$mids = wp_list_pluck( $thread->messages, 'id' );
+
+			foreach ( $mids as $mid ) {
+				if ( true === bp_messages_is_message_starred( $mid, $r['user_id'] ) ) {
+					bp_messages_delete_meta( $mid, 'starred_by_user', $r['user_id'] );
+				}
+			}
+
+			return true;
+		}
+	}
+}
+
+/** SCREENS **************************************************************/
+
+/**
+ * Screen handler to display a user's "Starred" private messages page.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_screen() {
+	add_action( 'bp_template_content', 'bp_messages_star_content' );
+
+	/**
+	 * Fires right before the loading of the "Starred" messages box.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 */
+	do_action( 'bp_messages_screen_star' );
+
+	bp_core_load_template( 'members/single/plugins' );
+}
+
+/**
+ * Screen content callback to display a user's "Starred" messages page.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_content() {
+	// add our message thread filter
+	add_filter( 'bp_after_has_message_threads_parse_args', 'bp_messages_filter_starred_message_threads' );
+
+	// load the message loop template part
+	bp_get_template_part( 'members/single/messages/messages-loop' );
+
+	// remove our filter
+	remove_filter( 'bp_after_has_message_threads_parse_args', 'bp_messages_filter_starred_message_threads' );
+}
+
+/**
+ * Filter message threads by those starred by the logged-in user.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param  array $r Current message thread arguments.
+ * @return array
+ */
+function bp_messages_filter_starred_message_threads( $r = array() ) {
+	$r['user_id'] = 0;
+	$r['meta_query'] = array( array(
+		'key'   => 'starred_by_user',
+		'value' => bp_loggedin_user_id()
+	) );
+
+	return $r;
+}
+
+/** ACTIONS **************************************************************/
+
+/**
+ * Action handler to set a message's star status for those not using JS.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_action_handler() {
+	if ( ! bp_is_user_messages() ) {
+		return;
+	}
+
+	if ( false === ( bp_is_current_action( 'unstar' ) || bp_is_current_action( 'star' ) ) ) {
+		return;
+	}
+
+	if ( ! wp_verify_nonce( bp_action_variable( 1 ), 'bp-messages-star-' . bp_action_variable( 0 ) ) ) {
+		wp_die( "Oops!  That's a no-no!" );
+	}
+
+	// mark the star
+	bp_messages_star_set_action( array(
+		'action'     => bp_current_action(),
+		'message_id' => bp_action_variable(),
+		'bulk'       => (bool) bp_action_variable( 2 )
+	) );
+
+	// redirect back to previous screen
+	$redirect = wp_get_referer() ? wp_get_referer() : bp_loggedin_user_domain() . bp_get_messages_slug();
+	bp_core_redirect( $redirect );
+	die();
+}
+add_action( 'bp_actions', 'bp_messages_star_action_handler' );
+
+/**
+ * Bulk manage handler to set the star status for multiple messages.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_bulk_manage_handler() {
+	if ( empty( $_POST['messages_bulk_nonce' ] ) ) {
+		return;
+	}
+
+	// Check the nonce.
+	if ( ! wp_verify_nonce( $_POST['messages_bulk_nonce'], 'messages_bulk_nonce' ) ) {
+		return;
+	}
+
+	$action  = ! empty( $_POST['messages_bulk_action'] ) ? $_POST['messages_bulk_action'] : '';
+	$threads = ! empty( $_POST['message_ids'] ) ? $_POST['message_ids'] : '';
+	$threads = wp_parse_id_list( $threads );
+
+	// Bail if action doesn't match our star actions or no IDs.
+	if ( false === in_array( $action, array( 'star', 'unstar' ), true ) || empty( $threads ) ) {
+		return;
+	}
+
+	// It's star time!
+	switch ( $action ) {
+		case 'star' :
+			$count = count( $threads );
+
+			// if we're starring a thread, we only star the first message in the thread
+			foreach ( $threads as $thread ) {
+				$thread = new BP_Messages_thread( $thread );
+				$mids = wp_list_pluck( $thread->messages, 'id' );
+
+				bp_messages_star_set_action( array(
+					'action'     => 'star',
+					'message_id' => $mids[0],
+				) );
+			}
+
+			bp_core_add_message( sprintf( _n( '1 message was successfully starred', '%s messages were successfully starred', $count, 'buddypress' ), $count ) );
+			break;
+
+		case 'unstar' :
+			$count = count( $threads );
+
+			foreach ( $threads as $thread ) {
+				bp_messages_star_set_action( array(
+					'action'    => 'unstar',
+					'thread_id' => $thread,
+					'bulk'      => true
+				) );
+			}
+
+			bp_core_add_message( sprintf( _n( '1 message was successfully unstarred', '%s messages were successfully unstarred', $count, 'buddypress' ), $count ) );
+			break;
+	}
+
+	// Redirect back to message box.
+	bp_core_redirect( bp_displayed_user_domain() . bp_get_messages_slug() . '/' . bp_current_action() . '/' );
+	die();
+}
+add_action( 'bp_actions', 'bp_messages_star_bulk_manage_handler', 5 );
+
+/** HOOKS ****************************************************************/
+
+/**
+ * Enqueues the dashicons font.
+ *
+ * The dashicons font is used for the star / unstar icon.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_enqueue_scripts() {
+	if ( ! bp_is_user_messages() ) {
+		return;
+	}
+
+	wp_enqueue_style( 'dashicons' );
+}
+add_action( 'wp_enqueue_scripts', 'bp_messages_star_enqueue_scripts' );
+
+/**
+ * Add the "Add star" and "Remove star" options to the bulk management list.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_messages_star_bulk_management_dropdown() {
+?>
+
+	<option value="star"><?php _e( 'Add star', 'buddypress' ); ?></option>
+	<option value="unstar"><?php _e( 'Remove star', 'buddypress' ); ?></option>
+
+<?php
+}
+add_action( 'bp_messages_bulk_management_dropdown', 'bp_messages_star_bulk_management_dropdown', 1 );
+
+/**
+ * Add CSS class for the current message depending on starred status.
+ *
+ * @since BuddyPress (2.3.0)
+ *
+ * @param  array $retval Current CSS classes
+ * @return array
+ */
+function bp_messages_star_message_css_class( $retval = array() ) {
+	if ( true === bp_messages_is_message_starred( bp_get_the_thread_message_id() ) ) {
+		$status = 'starred';
+	} else {
+		$status = 'not-starred';
+	}
+
+	// add css class based on star status for the current message
+	$retval[] = "message-{$status}";
+
+	return $retval;
+}
+add_filter( 'bp_get_the_thread_message_css_class', 'bp_messages_star_message_css_class' );
Index: src/bp-messages/bp-messages-template.php
===================================================================
--- src/bp-messages/bp-messages-template.php
+++ src/bp-messages/bp-messages-template.php
@@ -1290,6 +1290,14 @@
 		<option value="read"><?php _e( 'Mark read', 'buddypress' ); ?></option>
 		<option value="unread"><?php _e( 'Mark unread', 'buddypress' ); ?></option>
 		<option value="delete"><?php _e( 'Delete', 'buddypress' ); ?></option>
+		<?php
+			/**
+			 * Action to add additional options to the messages bulk management dropdown.
+			 *
+			 * @since BuddyPress (2.3.0)
+			 */
+			do_action( 'bp_messages_bulk_management_dropdown' );
+		?>
 	</select>
 	<input type="submit" id="messages-bulk-manage" class="button action" value="<?php esc_attr_e( 'Apply', 'buddypress' ); ?>">
 	<?php
Index: src/bp-templates/bp-legacy/buddypress-functions.php
===================================================================
--- src/bp-templates/bp-legacy/buddypress-functions.php
+++ src/bp-templates/bp-legacy/buddypress-functions.php
@@ -177,6 +177,11 @@
 			'messages_send_reply'           => 'bp_legacy_theme_ajax_messages_send_reply',
 		);
 
+		// Conditional actions
+		if ( bp_is_active( 'messages', 'star' ) ) {
+			$actions['messages_star'] = 'bp_legacy_theme_ajax_messages_star_handler';
+		}
+
 		/**
 		 * Register all of these AJAX handlers
 		 *
@@ -307,6 +312,23 @@
 			// Enqueue script
 			wp_enqueue_script( $asset['handle'] . '-password-verify', $asset['location'], $dependencies, $this->version);
 		}
+
+		// Star private messages
+		if ( bp_is_active( 'messages', 'star' ) && bp_is_user_messages() ) {
+			wp_localize_script( $asset['handle'], 'BP_PM_Star', array(
+				'strings' => array(
+					'text_unstar'  => __( 'Unstar', 'buddypress' ),
+					'text_star'    => __( 'Star', 'buddypress' ),
+					'title_unstar' => __( 'Starred', 'buddypress' ),
+					'title_star'   => __( 'Not starred', 'buddypress' ),
+					'title_unstar_thread' => __( 'Remove all starred messages in this thread', 'buddypress' ),
+					'title_star_thread'   => __( 'Star the first message in this thread', 'buddypress' ),
+				),
+				'is_single_thread' => (int) bp_is_messages_conversation(),
+				'star_counter'     => 0,
+				'unstar_counter'   => 0
+			) );
+		}
 	}
 
 	/**
@@ -1680,3 +1702,28 @@
 
 	exit;
 }
+
+/**
+ * AJAX callback to set a message's star status.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+function bp_legacy_theme_ajax_messages_star_handler() {
+	if ( false === bp_is_active( 'messages', 'star' ) || empty( $_POST['message_id'] ) ) {
+		return;
+	}
+
+	check_ajax_referer( 'bp-messages-star-' . (int) $_POST['message_id'], 'nonce' );
+
+	if ( true === bp_messages_star_set_action( array(
+		'action'     => $_POST['star_status'],
+		'message_id' => (int) $_POST['message_id'],
+		'bulk'       => ! empty( $_POST['bulk'] ) ? true : false
+	 ) ) ) {
+		echo '1';
+		die();
+	}
+
+	echo '-1';
+	die();
+}
Index: src/bp-templates/bp-legacy/buddypress/members/single/messages/messages-loop.php
===================================================================
--- src/bp-templates/bp-legacy/buddypress/members/single/messages/messages-loop.php
+++ src/bp-templates/bp-legacy/buddypress/members/single/messages/messages-loop.php
@@ -48,6 +48,11 @@
 					<th scope="col" class="thread-checkbox bulk-select-all"><label class="bp-screen-reader-text" for="select-all-messages"><?php _e( 'Select all', 'buddypress' ); ?></label><input id="select-all-messages" type="checkbox"></th>
 					<th scope="col" class="thread-from"><?php _e( 'From', 'buddypress' ); ?></th>
 					<th scope="col" class="thread-info"><?php _e( 'Subject', 'buddypress' ); ?></th>
+
+					<?php if ( bp_is_active( 'messages', 'star' ) ) : ?>
+						<th scope="col" class="thread-star"><span class="message-action-star"><span class="icon"></span> <span class="screen-reader-text"><?php _e( 'Star', 'buddypress' ); ?></span></span></th>
+					<?php endif; ?>
+
 					<th scope="col" class="thread-options"><?php _e( 'Actions', 'buddypress' ); ?></th>
 				</tr>
 			</thead>
@@ -91,6 +96,12 @@
 						 */
 						do_action( 'bp_messages_inbox_list_item' ); ?>
 
+						<?php if ( bp_is_active( 'messages', 'star' ) ) : ?>
+							<td class="thread-star">
+								<?php bp_the_message_star_action_link( array( 'thread_id' => bp_get_message_thread_id() ) ); ?>
+							</td>
+						<?php endif; ?>
+
 						<td class="thread-options">
 							<?php if ( bp_message_thread_has_unread() ) : ?>
 								<a class="read" href="<?php bp_the_message_thread_mark_read_url();?>"><?php _e( 'Read', 'buddypress' ); ?></a>
Index: src/bp-templates/bp-legacy/buddypress/members/single/messages/single.php
===================================================================
--- src/bp-templates/bp-legacy/buddypress/members/single/messages/single.php
+++ src/bp-templates/bp-legacy/buddypress/members/single/messages/single.php
@@ -69,6 +69,12 @@
 
 					<span class="activity"><?php bp_the_thread_message_time_since(); ?></span>
 
+					<?php if ( bp_is_active( 'messages', 'star' ) ) : ?>
+						<div class="message-star-actions">
+							<?php bp_the_message_star_action_link(); ?>
+						</div>
+					<?php endif; ?>
+
 					<?php
 
 					/** This action is documented in bp-templates/bp-legacy/buddypress-functions.php */
Index: src/bp-templates/bp-legacy/css/buddypress.css
===================================================================
--- src/bp-templates/bp-legacy/css/buddypress.css
+++ src/bp-templates/bp-legacy/css/buddypress.css
@@ -1445,6 +1445,39 @@
 	margin: 0 20px;
 }
 
+.message-metadata {
+	position: relative;
+}
+.message-star-actions {
+	position: absolute;
+	right: 0;
+	top: 0;
+}
+a.message-action-star,
+a.message-action-unstar {
+	text-decoration: none;
+	outline: none;
+}
+a.message-action-star {
+	opacity: .7;
+}
+a.message-action-star:hover {
+	opacity: 1;
+}
+.message-action-star span.icon:before,
+.message-action-unstar span.icon:before {
+	font-family: dashicons;
+	font-size: 18px;
+}
+.message-action-star span.icon:before {
+	color: #aaa;
+	content: "\f154";
+}
+.message-action-unstar span.icon:before {
+	color: #FCDD77;
+	content: "\f155";
+}
+
 /*--------------------------------------------------------------
 3.10 - Extended Profiles
 --------------------------------------------------------------*/
Index: src/bp-templates/bp-legacy/js/buddypress.js
===================================================================
--- src/bp-templates/bp-legacy/js/buddypress.js
+++ src/bp-templates/bp-legacy/js/buddypress.js
@@ -1578,6 +1578,84 @@
 		jq('#messages-bulk-manage').attr('disabled', jq(this).val().length <= 0);
 	});
 
+	/* Star action function */
+	starAction = function() {
+		var link = jq(this);
+
+		jq.post( ajaxurl, {
+			action: 'messages_star',
+			'message_id': link.data('message-id'),
+			'star_status': link.data('star-status'),
+			'nonce': link.data('star-nonce'),
+			'bulk': link.data('star-bulk')
+		},
+		function(response) {
+			if ( 1 == response ) {
+				if ( 'unstar' === link.data('star-status') ) {
+					link.data('star-status', 'star');
+					link.removeClass('message-action-unstar').addClass('message-action-star');
+					link.find('.screen-reader-text').text( BP_PM_Star.strings.text_star );
+
+					if ( 1 == BP_PM_Star.is_single_thread ) {
+						link.prop('title', BP_PM_Star.strings.title_star );
+					} else {
+						link.prop('title', BP_PM_Star.strings.title_star_thread );
+					}
+
+				} else {
+					link.data('star-status', 'unstar');
+					link.removeClass('message-action-star').addClass('message-action-unstar');
+					link.find('.screen-reader-text').text(BP_PM_Star.strings.text_unstar);
+
+					if ( 1 == BP_PM_Star.is_single_thread ) {
+						link.prop('title', BP_PM_Star.strings.title_unstar );
+					} else {
+						link.prop('title', BP_PM_Star.strings.title_unstar_thread );
+					}
+				}
+			}
+		});
+		return false;
+	}
+
+	/* Star actions */
+	jq('#message-threads').on('click', 'td.thread-star a', starAction );
+	jq('#message-thread').on('click', '.message-star-actions a', starAction );
+
+	/* Star bulk manage - Show only the valid action based on the starred item. */
+	jq('#message-threads td.bulk-select-check :checkbox').on('change', function() {
+		var box = jq(this),
+			star = box.closest('tr').find('.thread-star a');
+
+		if ( box.prop("checked") ) {
+			if( 'unstar' == star.data('star-status') ) {
+				BP_PM_Star.star_counter++;
+			} else {
+				BP_PM_Star.unstar_counter++;
+			}
+		} else {
+			if( 'unstar' == star.data('star-status') ) {
+				BP_PM_Star.star_counter--;
+			} else {
+				BP_PM_Star.unstar_counter--;
+			}
+		}
+
+		if ( BP_PM_Star.star_counter > 0 && BP_PM_Star.unstar_counter == 0 ) {
+			jq('option[value="star"]').hide();
+		} else {
+			jq('option[value="star"]').show();
+		}
+
+		if ( BP_PM_Star.unstar_counter > 0 && BP_PM_Star.star_counter == 0 ) {
+			jq('option[value="unstar"]').hide();
+		} else {
+			jq('option[value="unstar"]').show();
+		}
+	});
+
+	/** Notifications **********************************************/
+
 	/* Selecting/Deselecting all notifications */
 	jq('#select-all-notifications').click(function(event) {
 		if( this.checked ) {
Index: tests/phpunit/testcases/messages/star.php
new file mode 100644
===================================================================
--- /dev/null
+++ tests/phpunit/testcases/messages/star.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * @group messages
+ * @group star
+ */
+class BP_Tests_Messages_Star_ extends BP_UnitTestCase {
+
+	/**
+	 * @group bp_messages_is_message_starred
+	 * @group bp_messages_star_set_action
+	 */
+	public function test_is_message_starred() {
+		$u1 = $this->factory->user->create();
+		$u2 = $this->factory->user->create();
+
+		// create the thread
+		$t1 = $this->factory->message->create( array(
+			'sender_id'  => $u1,
+			'recipients' => array( $u2 ),
+			'subject'    => 'This is a knive',
+		) );
+
+		// create a reply
+		$this->factory->message->create( array(
+			'thread_id'  => $t1,
+			'sender_id'  => $u2,
+			'recipients' => array( $u1 ),
+			'content'    => "That's a spoon",
+		) );
+
+		// grab the message ids as individual variables
+		list( $m1, $m2 ) = $this->get_message_ids( $t1 );
+
+		// star the second message
+		$star = bp_messages_star_set_action( array(
+			'user_id'    => $u1,
+			'message_id' => $m2,
+		) );
+
+		// assert that star is set
+		$this->assertTrue( $star );
+		$this->assertTrue( bp_messages_is_message_starred( $m2, $u1 ) );
+
+		// unstar the second message
+		$unstar = bp_messages_star_set_action( array(
+			'user_id'    => $u1,
+			'message_id' => $m2,
+			'action'     => 'unstar'
+		) );
+
+		// assert that star is removed
+		$this->assertTrue( $unstar );
+		$this->assertFalse( bp_messages_is_message_starred( $m2, $u1 ) );
+	}
+
+	/**
+	 * @group bp_messages_star_set_action
+	 * @group bulk
+	 */
+	public function test_star_set_action_bulk_unstar() {
+		$u1 = $this->factory->user->create();
+		$u2 = $this->factory->user->create();
+
+		// create the thread
+		$t1 = $this->factory->message->create( array(
+			'sender_id'  => $u1,
+			'recipients' => array( $u2 ),
+			'subject'    => 'This is a knive',
+		) );
+
+		// create a reply
+		$this->factory->message->create( array(
+			'thread_id'  => $t1,
+			'sender_id'  => $u2,
+			'recipients' => array( $u1 ),
+			'content'    => "That's a spoon",
+		) );
+
+		// grab the message ids as individual variables
+		list( $m1, $m2 ) = $this->get_message_ids( $t1 );
+
+		// star all messages
+		bp_messages_star_set_action( array(
+			'user_id'    => $u1,
+			'message_id' => $m1,
+		) );
+		bp_messages_star_set_action( array(
+			'user_id'    => $u1,
+			'message_id' => $m2,
+		) );
+
+		// assert that stars are set
+		$this->assertTrue( bp_messages_is_message_starred( $m1, $u1 ) );
+		$this->assertTrue( bp_messages_is_message_starred( $m2, $u1 ) );
+
+		// unstar all messages
+		bp_messages_star_set_action( array(
+			'user_id'    => $u1,
+			'thread_id'  => $t1,
+			'action'     => 'unstar',
+			'bulk'       => true
+		) );
+
+		// assert that star is removed
+		$this->assertFalse( bp_messages_is_message_starred( $m1, $u1 ) );
+		$this->assertFalse( bp_messages_is_message_starred( $m2, $u1 ) );
+	}
+
+	/**
+	 * Helper method to grab the message IDs from a message thread.
+	 *
+	 * @param int $thread_id The message thread ID
+	 * @return array
+	 */
+	protected function get_message_ids( $thread_id = 0 ) {
+		$thread = new BP_Messages_Thread( $thread_id );
+		return wp_list_pluck( $thread->messages, 'id' );
+	}
+}
\ No newline at end of file
