diff --git src/bp-activity/bp-activity-template.php src/bp-activity/bp-activity-template.php
index 1ea141a..890b8b4 100644
--- src/bp-activity/bp-activity-template.php
+++ src/bp-activity/bp-activity-template.php
@@ -113,28 +113,32 @@ function bp_activity_directory_permalink() {
  *
  * @since BuddyPress (1.0.0)
  */
-class BP_Activity_Template {
-	var $current_activity = -1;
-	var $activity_count;
-	var $total_activity_count;
-	var $activities;
-	var $activity;
-
-	var $in_the_loop;
+class BP_Activity_Template extends BP_Template_Loop {
 
 	/**
-	 * URL parameter key for activity pagination. Default: 'acpage'.
+	 * Default arguments for Activity loop.
 	 *
-	 * @since BuddyPress (2.1.0)
-	 * @var string
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
 	 */
-	var $pag_arg;
-
-	var $pag_page;
-	var $pag_num;
-	var $pag_links;
-
-	var $full_name;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'activity',
+			'item_plural'       => 'activities',
+			'item_total_count'  => 'total_activity_count',
+			'item_count'        => 'activity_count',
+			'current_item'      => 'current_activity',
+			'hook_prefix'       => 'activity',
+		),
+		'params' => array(
+			'page_arg'          => 'acpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -164,7 +168,6 @@ class BP_Activity_Template {
 	 * }
 	 */
 	function __construct( $args ) {
-		global $bp;
 
 		// Backward compatibility with old method of passing arguments
 		if ( !is_array( $args ) || func_num_args() > 1 ) {
@@ -211,12 +214,21 @@ class BP_Activity_Template {
 			'spam'              => 'ham_only',
 			'update_meta_cache' => true,
 		);
+
 		$r = wp_parse_args( $args, $defaults );
-		extract( $r );
 
-		$this->pag_arg  = $r['page_arg'];
-		$this->pag_page = isset( $_REQUEST[ $this->pag_arg ] ) ? intval( $_REQUEST[ $this->pag_arg ] ) : $page;
-		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+		$this->set_loop( array( 'params' => $r ) );
+	}
+
+	/**
+	 * Get Activities.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
+		$bp = buddypress();
 
 		// Check if blog/forum replies are disabled
 		$this->disable_blogforum_replies = isset( $bp->site_options['bp-disable-blogforum-comments'] ) ? $bp->site_options['bp-disable-blogforum-comments'] : false;
@@ -224,195 +236,52 @@ class BP_Activity_Template {
 		// Get an array of the logged in user's favorite activities
 		$this->my_favs = maybe_unserialize( bp_get_user_meta( bp_loggedin_user_id(), 'bp_favorite_activities', true ) );
 
+		// Set Displayed user fullname
+		$this->full_name = bp_get_displayed_user_fullname();
+
 		// Fetch specific activity items based on ID's
-		if ( !empty( $include ) ) {
-			$this->activities = bp_activity_get_specific( array(
-				'activity_ids'      => explode( ',', $include ),
-				'max'               => $max,
-				'count_total'       => $count_total,
-				'page'              => $this->pag_page,
-				'per_page'          => $this->pag_num,
-				'sort'              => $sort,
-				'display_comments'  => $display_comments,
-				'show_hidden'       => $show_hidden,
-				'spam'              => $spam,
-				'update_meta_cache' => $update_meta_cache,
+		if ( ! empty( $this->args['params']['include'] ) ) {
+			$activities = bp_activity_get_specific( array(
+				'activity_ids'      => explode( ',', $this->args['params']['include'] ),
+				'max'               => $this->args['params']['max'],
+				'count_total'       => $this->args['params']['count_total'],
+				'page'              => $this->page,
+				'per_page'          => $this->per_page,
+				'sort'              => $this->args['params']['sort'],
+				'display_comments'  => $this->args['params']['display_comments'],
+				'show_hidden'       => $this->args['params']['show_hidden'],
+				'spam'              => $this->args['params']['spam'],
+				'update_meta_cache' => $this->args['params']['update_meta_cache'],
 			) );
 
 		// Fetch all activity items
 		} else {
-			$this->activities = bp_activity_get( array(
-				'display_comments'  => $display_comments,
-				'max'               => $max,
-				'count_total'       => $count_total,
-				'per_page'          => $this->pag_num,
-				'page'              => $this->pag_page,
-				'sort'              => $sort,
-				'search_terms'      => $search_terms,
-				'meta_query'        => $meta_query,
-				'date_query'        => $date_query,
-				'filter_query'      => $filter_query,
-				'filter'            => $filter,
-				'scope'             => $scope,
-				'show_hidden'       => $show_hidden,
-				'exclude'           => $exclude,
-				'in'                => $in,
-				'spam'              => $spam,
-				'update_meta_cache' => $update_meta_cache,
-			) );
+			$activities = bp_activity_get( $this->args['params'] );
 		}
 
-		// The total_activity_count property will be set only if a
-		// 'count_total' query has taken place
-		if ( ! is_null( $this->activities['total'] ) ) {
-			if ( ! $max || $max >= (int) $this->activities['total'] ) {
-				$this->total_activity_count = (int) $this->activities['total'];
-			} else {
-				$this->total_activity_count = (int) $max;
-			}
-		}
-
-		$this->has_more_items = $this->activities['has_more_items'];
-
-		$this->activities = $this->activities['activities'];
-
-		if ( $max ) {
-			if ( $max >= count($this->activities) ) {
-				$this->activity_count = count( $this->activities );
-			} else {
-				$this->activity_count = (int) $max;
-			}
-		} else {
-			$this->activity_count = count( $this->activities );
-		}
-
-		$this->full_name = bp_get_displayed_user_fullname();
+		$this->has_more_items = $activities['has_more_items'];
 
 		// Fetch parent content for activity comments so we do not have to query in the loop
-		foreach ( (array) $this->activities as $activity ) {
+		foreach ( (array) $activities['activities'] as $activity ) {
 			if ( 'activity_comment' != $activity->type )
 				continue;
 
 			$parent_ids[] = $activity->item_id;
 		}
 
-		if ( !empty( $parent_ids ) )
+		if ( ! empty( $parent_ids ) ) {
 			$activity_parents = bp_activity_get_specific( array( 'activity_ids' => $parent_ids ) );
-
-		if ( !empty( $activity_parents['activities'] ) ) {
-			foreach( $activity_parents['activities'] as $parent )
-				$this->activity_parents[$parent->id] = $parent;
-
-			unset( $activity_parents );
-		}
-
-		if ( (int) $this->total_activity_count && (int) $this->pag_num ) {
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $page_arg, '%#%' ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_activity_count / (int) $this->pag_num ),
-				'current'   => (int) $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Activity pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Activity pagination next text', 'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => array(),
-			) );
-		}
-	}
-
-	/**
-	 * Whether there are activity items available in the loop.
-	 *
-	 * @see bp_has_activities()
-	 *
-	 * @return bool True if there are items in the loop, otherwise false.
-	 */
-	function has_activities() {
-		if ( $this->activity_count )
-			return true;
-
-		return false;
-	}
-
-	/**
-	 * Set up the next activity item and iterate index.
-	 *
-	 * @return object The next activity item to iterate over.
-	 */
-	function next_activity() {
-		$this->current_activity++;
-		$this->activity = $this->activities[$this->current_activity];
-
-		return $this->activity;
-	}
-
-	/**
-	 * Rewind the posts and reset post index.
-	 */
-	function rewind_activities() {
-		$this->current_activity = -1;
-		if ( $this->activity_count > 0 ) {
-			$this->activity = $this->activities[0];
 		}
-	}
 
-	/**
-	 * Whether there are activity items left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_activities()} as part of the while loop
-	 * that controls iteration inside the activities loop, eg:
-	 *     while ( bp_activities() ) { ...
-	 *
-	 * @see bp_activities()
-	 *
-	 * @return bool True if there are more activity items to show,
-	 *              otherwise false.
-	 */
-	function user_activities() {
-		if ( $this->current_activity + 1 < $this->activity_count ) {
-			return true;
-		} elseif ( $this->current_activity + 1 == $this->activity_count ) {
+		if ( ! empty( $activity_parents['activities'] ) ) {
+			foreach ( $activity_parents['activities'] as $parent ) {
+				$this->activity_parents[ $parent->id ] = $parent;
+			}
 
-			/**
-			 * Fires right before the rewinding of activity posts.
-			 *
-			 * @since BuddyPress (1.1.0)
-			 */
-			do_action( 'activity_loop_end' );
-			// Do some cleaning up after the loop
-			$this->rewind_activities();
+			unset( $activity_parents );
 		}
 
-		$this->in_the_loop = false;
-		return false;
-	}
-
-	/**
-	 * Set up the current activity item inside the loop.
-	 *
-	 * Used by {@link bp_the_activity()} to set up the current activity item
-	 * data while looping, so that template tags used during that iteration
-	 * make reference to the current activity item.
-	 *
-	 * @see bp_the_activity()
-	 */
-	function the_activity() {
-
-		$this->in_the_loop = true;
-		$this->activity    = $this->next_activity();
-
-		if ( is_array( $this->activity ) )
-			$this->activity = (object) $this->activity;
-
-		if ( $this->current_activity == 0 ) { // loop has just started
-
-			/**
-			 * Fires if the current activity item is the first in the activity loop.
-			 *
-			 * @since BuddyPress (1.1.0)
-			 */
-			do_action('activity_loop_start');
-		}
+		return $activities;
 	}
 }
 
@@ -694,7 +563,7 @@ function bp_has_activities( $args = '' ) {
 	 * @param string $activities_template Current activities template being used.
 	 * @param array  $template_args Array of arguments passed into the BP_Activity_Template class.
 	 */
-	return apply_filters( 'bp_has_activities', $activities_template->has_activities(), $activities_template, $template_args );
+	return apply_filters( 'bp_has_activities', $activities_template->has_items(), $activities_template, $template_args );
 }
 
 /**
@@ -703,13 +572,13 @@ function bp_has_activities( $args = '' ) {
  * @since BuddyPress (1.0.0)
  *
  * @global object $activities_template {@link BP_Activity_Template}
- * @uses BP_Activity_Template::user_activities() {@link BP_Activity_Template::user_activities()}
+ * @uses BP_Activity_Template::items() {@link BP_Activity_Template::items()}
  *
  * @return bool Returns true when activities are found.
  */
 function bp_activities() {
 	global $activities_template;
-	return $activities_template->user_activities();
+	return $activities_template->items();
 }
 
 /**
@@ -724,7 +593,7 @@ function bp_activities() {
  */
 function bp_the_activity() {
 	global $activities_template;
-	return $activities_template->the_activity();
+	return $activities_template->the_item();
 }
 
 /**
@@ -739,7 +608,7 @@ function bp_activity_load_more_link() {
 		global $activities_template;
 
 		$link = bp_get_requested_url();
-		$link = add_query_arg( $activities_template->pag_arg, $activities_template->pag_page + 1, $link );
+		$link = add_query_arg( $activities_template->page_arg, $activities_template->page + 1, $link );
 
 		/**
 		 * Filters the Load More link URL.
@@ -757,7 +626,7 @@ function bp_activity_load_more_link() {
  * @since BuddyPress (1.0.0)
  *
  * @global object $activities_template {@link BP_Activity_Template}
- * @uses BP_Activity_Template::the_activity() {@link BP_Activity_Template::the_activity()}
+ * @uses BP_Activity_Template::the_item() {@link BP_Activity_Template::the_item()}
  */
 function bp_activity_pagination_count() {
 	echo bp_get_activity_pagination_count();
@@ -776,9 +645,9 @@ function bp_activity_pagination_count() {
 	function bp_get_activity_pagination_count() {
 		global $activities_template;
 
-		$start_num = intval( ( $activities_template->pag_page - 1 ) * $activities_template->pag_num ) + 1;
+		$start_num = intval( ( $activities_template->page - 1 ) * $activities_template->per_page ) + 1;
 		$from_num  = bp_core_number_format( $start_num );
-		$to_num    = bp_core_number_format( ( $start_num + ( $activities_template->pag_num - 1 ) > $activities_template->total_activity_count ) ? $activities_template->total_activity_count : $start_num + ( $activities_template->pag_num - 1 ) );
+		$to_num    = bp_core_number_format( ( $start_num + ( $activities_template->per_page - 1 ) > $activities_template->total_activity_count ) ? $activities_template->total_activity_count : $start_num + ( $activities_template->per_page - 1 ) );
 		$total     = bp_core_number_format( $activities_template->total_activity_count );
 
 		return sprintf( _n( 'Viewing 1 item', 'Viewing %1$s - %2$s of %3$s items', $total, 'buddypress' ), $from_num, $to_num, $total );
@@ -837,7 +706,7 @@ function bp_activity_has_more_items() {
 		$remaining_pages = 0;
 
 		if ( ! empty( $activities_template->pag_page ) ) {
-			$remaining_pages = floor( ( $activities_template->total_activity_count - 1 ) / ( $activities_template->pag_num * $activities_template->pag_page ) );
+			$remaining_pages = floor( ( $activities_template->total_activity_count - 1 ) / ( $activities_template->per_page * $activities_template->page ) );
 		}
 
 		$has_more_items = (int) $remaining_pages > 0;
@@ -916,9 +785,9 @@ function bp_activity_per_page() {
 		 *
 		 * @since BuddyPress (1.2.0)
 		 *
-		 * @param int $pag_num How many post should be displayed for pagination.
+		 * @param int $per_page How many post should be displayed for pagination.
 		 */
-		return apply_filters( 'bp_get_activity_per_page', (int) $activities_template->pag_num );
+		return apply_filters( 'bp_get_activity_per_page', (int) $activities_template->per_page );
 	}
 
 /**
diff --git src/bp-blogs/bp-blogs-template.php src/bp-blogs/bp-blogs-template.php
index 21fdb7c..50f9f46 100644
--- src/bp-blogs/bp-blogs-template.php
+++ src/bp-blogs/bp-blogs-template.php
@@ -106,79 +106,32 @@ function bp_blogs_directory_permalink() {
  *
  * Responsible for loading a group of blogs into a loop for display.
  */
-class BP_Blogs_Template {
+class BP_Blogs_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Blogs loop.
 	 *
-	 * @access public
-	 * @var int
-	 */
-	public $current_blog = -1;
-
-	/**
-	 * The number of blogs returned by the paged query.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $blog_count = 0;
-
-	/**
-	 * Array of blogs located by the query..
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @access public
+	 * @access protected
 	 * @var array
 	 */
-	public $blogs = array();
-
-	/**
-	 * The blog object currently being iterated on.
-	 *
-	 * @access public
-	 * @var object
-	 */
-	public $blog;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
-	 *
-	 * @access public
-	 * @var bool
-	 */
-	public $in_the_loop = false;
-
-	/**
-	 * The page number being requested.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	public $pag_page = 1;
-
-	/**
-	 * The number of items being requested per page.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	public $pag_num = 20;
-
-	/**
-	 * An HTML string containing pagination links.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	public $pag_links = '';
-
-	/**
-	 * The total number of blogs matching the query parameters.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $total_blog_count = 0;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'blog',
+			'item_plural'       => 'blogs',
+			'item_total_count'  => 'total_blog_count',
+			'item_count'        => 'blog_count',
+			'current_item'      => 'current_blog',
+			'hook_prefix'       => 'blog',
+		),
+		'params' => array(
+			'page_arg'          => 'bpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -198,149 +151,49 @@ class BP_Blogs_Template {
 	 * @param array $include_blog_ids Array of blog IDs to include.
 	 */
 	public function __construct( $type, $page, $per_page, $max, $user_id, $search_terms, $page_arg = 'bpage', $update_meta_cache = true, $include_blog_ids = false ) {
+		$params = array( 'params' => array(
+			'type'              => $type,
+			'page'              => $page,
+			'per_page'          => $per_page,
+			'max'               => $max,
+			'user_id'           => $user_id,
+			'search_terms'      => $search_terms,
+			'page_arg'          => $page_arg,
+			'update_meta_cache' => $update_meta_cache,
+			'include_blog_ids'  => $include_blog_ids,
+		) );
 
-		$this->pag_page = isset( $_REQUEST[ $page_arg ] ) ? intval( $_REQUEST[ $page_arg ] ) : $page;
-		$this->pag_num  = isset( $_REQUEST['num']       ) ? intval( $_REQUEST['num']       ) : $per_page;
-
-		// Backwards compatibility support for blogs by first letter
-		if ( ! empty( $_REQUEST['letter'] ) ) {
-			$this->blogs = BP_Blogs_Blog::get_by_letter( $_REQUEST['letter'], $this->pag_num, $this->pag_page );
-
-		// Typical blogs query
-		} else {
-			$this->blogs = bp_blogs_get_blogs( array(
-				'type'              => $type,
-				'per_page'          => $this->pag_num,
-				'page'              => $this->pag_page,
-				'user_id'           => $user_id,
-				'search_terms'      => $search_terms,
-				'update_meta_cache' => $update_meta_cache,
-				'include_blog_ids'  => $include_blog_ids,
-			) );
-		}
-
-		// Set the total blog count
-		if ( empty( $max ) || ( $max >= (int) $this->blogs['total'] ) ) {
-			$this->total_blog_count = (int) $this->blogs['total'];
-		} else {
-			$this->total_blog_count = (int) $max;
-		}
-
-		// Set the blogs array (to loop through later
-		$this->blogs = $this->blogs['blogs'];
-
-		// Get the current blog count to compare maximum against
-		$blog_count = count( $this->blogs );
-
-		// Set the current blog count
-		if ( empty( $max ) || ( $max >= (int) $blog_count ) ) {
-			$this->blog_count = (int) $blog_count;
-		} else {
-			$this->blog_count = (int) $max;
-		}
-
-		// Build pagination links based on total blogs and current page number
-		if ( ! empty( $this->total_blog_count ) && ! empty( $this->pag_num ) ) {
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $page_arg, '%#%' ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_blog_count / (int) $this->pag_num ),
-				'current'   => (int) $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Blog pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Blog pagination next text',     'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => array(),
-			) );
-		}
-	}
-
-	/**
-	 * Whether there are blogs available in the loop.
-	 *
-	 * @see bp_has_blogs()
-	 *
-	 * @return bool True if there are items in the loop, otherwise false.
-	 */
-	public function has_blogs() {
-		return (bool) ! empty( $this->blog_count );
-	}
-
-	/**
-	 * Set up the next blog and iterate index.
-	 *
-	 * @return object The next blog to iterate over.
-	 */
-	public function next_blog() {
-		$this->current_blog++;
-		$this->blog = $this->blogs[ $this->current_blog ];
-
-		return $this->blog;
-	}
-
-	/**
-	 * Rewind the blogs and reset blog index.
-	 */
-	public function rewind_blogs() {
-		$this->current_blog = -1;
-		if ( $this->blog_count > 0 ) {
-			$this->blog = $this->blogs[0];
-		}
+		$this->set_loop( $params );
 	}
 
 	/**
-	 * Whether there are blogs left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_blogs()} as part of the while loop
-	 * that controls iteration inside the blogs loop, eg:
-	 *     while ( bp_blogs() ) { ...
+	 * Get Blogs.
 	 *
-	 * @see bp_blogs()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are more blogs to show, otherwise false.
+	 * @access public
 	 */
-	public function blogs() {
-		if ( ( $this->current_blog + 1 ) < $this->blog_count ) {
-			return true;
-		} elseif ( ( $this->current_blog + 1 ) === $this->blog_count ) {
+	public function get_items() {
+		// Backwards compatibility support for blogs by first letter
+		if ( ! empty( $_REQUEST['letter'] ) ) {
+			$blogs = BP_Blogs_Blog::get_by_letter( $_REQUEST['letter'], $this->per_page, $this->page );
 
-			/**
-			 * Fires right before the rewinding of blogs listing after all are shown.
-			 *
-			 * @since BuddyPress (1.5.0)
-			 */
-			do_action( 'blog_loop_end' );
-			// Do some cleaning up after the loop
-			$this->rewind_blogs();
+		// Typical blogs query
+		} else {
+			$blogs = bp_blogs_get_blogs( $this->args['params'] );
 		}
 
-		$this->in_the_loop = false;
-		return false;
+		return $blogs;
 	}
 
 	/**
-	 * Set up the current blog inside the loop.
-	 *
-	 * Used by {@link bp_the_blog()} to set up the current blog data while
-	 * looping, so that template tags used during that iteration make
-	 * reference to the current blog.
+	 * Rewind Blogs is an alias of rewind_items()
 	 *
-	 * @see bp_the_blog()
+	 * @access public
 	 */
-	public function the_blog() {
-
-		$this->in_the_loop = true;
-		$this->blog        = $this->next_blog();
-
-		// loop has just started
-		if ( 0 === $this->current_blog ) {
-
-			/**
-			 * Fires if on the first blog in the loop.
-			 *
-			 * @since BuddyPress (1.5.0)
-			 */
-			do_action( 'blog_loop_start' );
-		}
+	public function rewind_blogs() {
+		_doing_it_wrong( __FUNCTION__, __( 'BP_Blogs_Template->rewind_blogs() should not be used directly, please use bp_rewind_blogs()', 'buddypress' ), '2.3.0' );
+		$this->rewind_items();
 	}
 }
 
@@ -350,7 +203,7 @@ class BP_Blogs_Template {
 function bp_rewind_blogs() {
 	global $blogs_template;
 
-	$blogs_template->rewind_blogs();
+	$blogs_template->rewind_items();
 }
 
 /**
@@ -429,7 +282,7 @@ function bp_has_blogs( $args = '' ) {
 	 * @param BP_Blogs_Template $blogs_template Current blogs template object.
 	 * @param array             $r              Parsed arguments used in blogs template query.
 	 */
-	return apply_filters( 'bp_has_blogs', $blogs_template->has_blogs(), $blogs_template, $r );
+	return apply_filters( 'bp_has_blogs', $blogs_template->has_items(), $blogs_template, $r );
 }
 
 /**
@@ -442,7 +295,7 @@ function bp_has_blogs( $args = '' ) {
 function bp_blogs() {
 	global $blogs_template;
 
-	return $blogs_template->blogs();
+	return $blogs_template->items();
 }
 
 /**
@@ -455,7 +308,7 @@ function bp_blogs() {
 function bp_the_blog() {
 	global $blogs_template;
 
-	return $blogs_template->the_blog();
+	return $blogs_template->the_item();
 }
 
 /**
@@ -466,9 +319,9 @@ function bp_the_blog() {
 function bp_blogs_pagination_count() {
 	global $blogs_template;
 
-	$start_num = intval( ( $blogs_template->pag_page - 1 ) * $blogs_template->pag_num ) + 1;
+	$start_num = intval( ( $blogs_template->page - 1 ) * $blogs_template->per_page ) + 1;
 	$from_num  = bp_core_number_format( $start_num );
-	$to_num    = bp_core_number_format( ( $start_num + ( $blogs_template->pag_num - 1 ) > $blogs_template->total_blog_count ) ? $blogs_template->total_blog_count : $start_num + ( $blogs_template->pag_num - 1 ) );
+	$to_num    = bp_core_number_format( ( $start_num + ( $blogs_template->per_page - 1 ) > $blogs_template->total_blog_count ) ? $blogs_template->total_blog_count : $start_num + ( $blogs_template->per_page - 1 ) );
 	$total     = bp_core_number_format( $blogs_template->total_blog_count );
 
 	echo sprintf( _n( 'Viewing 1 site', 'Viewing %1$s - %2$s of %3$s sites', $total, 'buddypress' ), $from_num, $to_num, $total );
diff --git src/bp-core/bp-core-classes.php src/bp-core/bp-core-classes.php
index f47f0b8..0babf2b 100644
--- src/bp-core/bp-core-classes.php
+++ src/bp-core/bp-core-classes.php
@@ -2978,4 +2978,302 @@ abstract class BP_Recursive_Query {
          * @return bool
          */
         abstract protected function is_first_order_clause( $query );
-}
\ No newline at end of file
+}
+
+/**
+ * Base class for components template loops.
+ *
+ * Each public method can be overriden from a custom template class
+ * For instance, you can customize the default paginate links within
+ * your class.
+ * @see  BP_Groups_Template->set_pag_links() for an example of use
+ *
+ * the abstract method get_items() is required. This is where you define
+ * an associative array having your custom 'item_plural' and 'total' keys
+ *
+ * @since 2.3.0
+ */
+abstract class BP_Template_Loop {
+
+	/**
+	 * Default arguments common to all components loop.
+	 *
+	 * You can add the custom item default args within the custom template loop class
+	 * @see  BP_Groups_Template->default_args for an example of use.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array $default_args {
+	 *     Array describing the default argument for the item loop.
+	 *     @type array $labels list of custom var names for the loop
+	 *           - @type string $item the singular item var name
+	 *           for instance 'group' for the groups loop
+	 *           - @type string $item_plural the plural item var name
+	 *           for instance 'groups' for the groups loop
+	 *           - @type string $item_total_count the total count var name
+	 *           for instance 'total_group_count' for the groups loop
+	 *           - @type string $item_count the current items count var name
+	 *           for instance 'group_count' for the groups loop
+	 *           - @type string $current_item the current item var name
+	 *           for instance 'current_group' for the groups loop
+	 *           - @type string $hook_prefix the hook prefix to use in loop start
+	 *           and loop end actions for instance 'activity' for the activity loop
+	 *     @type array $params list of default arguments for the loop
+	 *           - @type string $page_arg the var name to get the page value.
+	 *           for instance 'grpage' for the groups loop
+	 *           - @type int    $page default value for the page argument
+	 *           - @type int    $per_page default value for the per page argument
+	 *           - @type int    $max default value for the max argument
+	 * }
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'item',
+			'item_plural'       => 'items',
+			'item_total_count'  => 'total_item_count',
+			'item_count'        => 'item_count',
+			'current_item'      => 'current_item',
+			'hook_prefix'       => 'item',
+		),
+		'params' => array(
+			'page_arg'          => 'page',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
+
+	/**
+	 * Holds the arguments to customize the loop labels and the component's items query.
+	 *
+	 * This includes `$default_args`, as well as the user-supplied values.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $args = array();
+
+	/**
+	 * A flag for whether the loop is currently being iterated.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 * @var bool
+	 */
+	public $in_the_loop;
+
+	/**
+	 * An HTML string containing pagination links.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 * @var string
+	 */
+	public $pag_links;
+
+	/**
+	 * Set the loop
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 * @param array $args component's loop arguments
+	 */
+	public function set_loop( $args = array() ) {
+		$this->args = wp_parse_args( $args, $this->default_args );
+
+		// If not set the hook prefix defaults to the singular item name
+		if ( empty( $this->args['labels']['hook_prefix'] ) ) {
+			$this->args['labels']['hook_prefix'] = $this->args['labels']['item'];
+		}
+
+		$this->{$this->args['labels']['current_item']} = -1;
+
+		// Setting loop parameters
+		if ( ! empty( $this->args['params'] ) ) {
+			foreach ( (array) $this->args['params'] as $key => $value ) {
+				$this->{$key} = $value;
+			}
+
+			if ( isset( $this->page_arg ) && ! empty( $_REQUEST[ $this->page_arg ] ) ) {
+				$this->page = intval( $_REQUEST[ $this->page_arg ] );
+			}
+
+			if ( ! empty( $_REQUEST['num'] ) ) {
+				$this->per_page = intval( $_REQUEST['num'] );
+			}
+		}
+
+		// Setup the Items to loop through
+		$items = $this->get_items();
+
+		if ( ! isset( $items[ $this->args['labels']['item_plural'] ] ) ) {
+			return false;
+		}
+
+		// Set items
+		$this->{$this->args['labels']['item_plural']} = $items[ $this->args['labels']['item_plural'] ];
+
+		if ( empty( $this->{$this->args['labels']['item_plural']} ) ) {
+			$this->{$this->args['labels']['item_count']}       = 0;
+			$this->{$this->args['labels']['item_total_count']} = 0;
+
+		} else {
+			// Set the total items count
+			$this->{$this->args['labels']['item_total_count']} = (int) $items['total'];
+
+			if ( ! empty( $this->max ) && ( $this->max < (int) $items['total'] ) ) {
+				$this->{$this->args['labels']['item_total_count']} = (int) $this->max;
+			}
+
+			// Set current items count
+			$this->{$this->args['labels']['item_count']} = count( $this->{$this->args['labels']['item_plural']} );
+
+			if ( ! empty( $this->max ) && ( $this->max < (int) $this->{$this->args['labels']['item_count']} ) ) {
+				$this->{$this->args['labels']['item_count']} = (int) $this->max;
+			}
+		}
+
+		if ( ! empty( $this->{$this->args['labels']['item_total_count']} ) && ! empty( $this->per_page ) && ! empty( $this->page ) ) {
+			$this->set_pag_links();
+		}
+	}
+
+	/**
+	 * Get the component's items
+	 *
+	 * This method needs to be set within the custom Template class
+	 * @see  BP_Blogs_Template->get_items() for an example of use
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	abstract public function get_items();
+
+	/**
+	 * Set the pagination links
+	 *
+	 * Each item loop can override this and sets its own pagination links
+	 * within its custom template class.
+	 * @see  BP_Groups_Template->set_pag_links() for an example of use
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function set_pag_links() {
+		$this->pag_links = paginate_links( array(
+			'base'      => add_query_arg( $this->page_arg, '%#%' ),
+			'format'    => '',
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
+			'prev_text' => _x( '&larr;', 'pagination previous text', 'buddypress' ),
+			'next_text' => _x( '&rarr;', 'pagination next text',     'buddypress' ),
+			'mid_size'  => 1,
+			'add_args'  => array(),
+		) );
+	}
+
+	/**
+	 * Whether there are Items available in the loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @return bool True if there are items in the loop, otherwise false.
+	 */
+	public function has_items() {
+		if ( ! empty( $this->{$this->args['labels']['item_count']} ) ) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Set up the next item and iterate index.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @return object The next item to iterate over.
+	 */
+	public function next_item() {
+
+		$this->{$this->args['labels']['current_item']}++;
+
+		$this->{$this->args['labels']['item']} = $this->{$this->args['labels']['item_plural']}[ $this->{$this->args['labels']['current_item']} ];
+
+		return $this->{$this->args['labels']['item']};
+	}
+
+	/**
+	 * Rewind the items and reset items index.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 */
+	public function rewind_items() {
+
+		$this->{$this->args['labels']['current_item']} = -1;
+
+		if ( $this->{$this->args['labels']['item_count']} > 0 ) {
+			$this->{$this->args['labels']['item']} = $this->{$this->args['labels']['item_plural']}[0];
+		}
+	}
+
+	/**
+	 * Whether there are items left in the loop to iterate over.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @return bool True if there are more items to show,
+	 *         otherwise false.
+	 */
+	public function items() {
+
+		if ( $this->{$this->args['labels']['current_item']} + 1 < $this->{$this->args['labels']['item_count']} ) {
+			return true;
+
+		} elseif ( $this->{$this->args['labels']['current_item']} + 1 == $this->{$this->args['labels']['item_count']} ) {
+			/**
+			 * Inform when the item loop ends. Dynamically built using the hook_prefix label
+			 * For instance the messages box loop is using 'messages_box' as its item hook_prefix,
+			 * as a result the action will be 'messages_box_loop_end'
+			 */
+			do_action( $this->args['labels']['hook_prefix'] . '_loop_end' );
+
+			$this->rewind_items();
+		}
+
+		$this->in_the_loop = false;
+		return false;
+	}
+
+	/**
+	 * Set up the current item inside the loop.
+	 *
+	 * Each item loop can override this to append custom vars to the item
+	 * within its custom template class.
+	 * @see  BP_Groups_Invite_Template->the_item() for an example of use
+	 *
+	 * @since BuddyPress (2.3.0)
+	 */
+	public function the_item() {
+		$this->in_the_loop  = true;
+		$this->{$this->args['labels']['item']} = $this->next_item();
+
+		// loop has just started
+		if ( 0 === $this->{$this->args['labels']['current_item']} ) {
+			/**
+			 * Inform when the item loop starts. Dynamically built using the hook_prefix label
+			 * For instance the groups loop is using 'group' as its hook_prefix, as a result
+			 * the action will be 'group_loop_start'
+			 */
+			do_action( $this->args['labels']['hook_prefix'] . '_loop_start' );
+		}
+	}
+}
diff --git src/bp-groups/bp-groups-template.php src/bp-groups/bp-groups-template.php
index 4684804..88a6561 100644
--- src/bp-groups/bp-groups-template.php
+++ src/bp-groups/bp-groups-template.php
@@ -63,109 +63,37 @@ function bp_groups_directory_permalink() {
 	function bp_get_groups_directory_permalink() {
 		return apply_filters( 'bp_get_groups_directory_permalink', trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() ) );
 	}
-
 /**
  * The main Groups template loop class.
  *
  * Responsible for loading a group of groups into a loop for display.
  */
-class BP_Groups_Template {
+class BP_Groups_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Groups loop.
 	 *
-	 * @access public
-	 * @var int
-	 */
-	var $current_group = -1;
-
-	/**
-	 * The number of groups returned by the paged query.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	var $group_count;
-
-	/**
-	 * Array of groups located by the query.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @access public
+	 * @access protected
 	 * @var array
 	 */
-	var $groups;
-
-	/**
-	 * The group object currently being iterated on.
-	 *
-	 * @access public
-	 * @var object
-	 */
-	var $group;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
-	 *
-	 * @access public
-	 * @var bool
-	 */
-	var $in_the_loop;
-
-	/**
-	 * The page number being requested.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	var $pag_page;
-
-	/**
-	 * The number of items being requested per page.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	var $pag_num;
-
-	/**
-	 * An HTML string containing pagination links.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	var $pag_links;
-
-	/**
-	 * The total number of groups matching the query parameters.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	var $total_group_count;
-
-	/**
-	 * Whether the template loop is for a single group page.
-	 *
-	 * @access public
-	 * @var bool
-	 */
-	var $single_group = false;
-
-	/**
-	 * Field to sort by.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	var $sort_by;
-
-	/**
-	 * Sort order.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	var $order;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'group',
+			'item_plural'       => 'groups',
+			'item_total_count'  => 'total_group_count',
+			'item_count'        => 'group_count',
+			'current_item'      => 'current_group',
+			'hook_prefix'       => 'group',
+		),
+		'params' => array(
+			'page_arg'          => 'grpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -180,8 +108,7 @@ class BP_Groups_Template {
 	 *     @type int $page Default: 1.
 	 * }
 	 */
-	function __construct( $args = array() ){
-
+	public function __construct( $args = array() ) {
 		// Backward compatibility with old method of passing arguments
 		if ( ! is_array( $args ) || func_num_args() > 1 ) {
 			_deprecated_argument( __METHOD__, '1.7', sprintf( __( 'Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'buddypress' ), __METHOD__, __FILE__ ) );
@@ -206,34 +133,43 @@ class BP_Groups_Template {
 		}
 
 		$defaults = array(
-			'type'            => 'active',
-			'page'            => 1,
-			'per_page'        => 20,
-			'max'             => false,
-			'show_hidden'     => false,
-			'page_arg'        => 'grpage',
-			'user_id'         => 0,
-			'slug'            => false,
-			'include'         => false,
-			'exclude'         => false,
-			'search_terms'    => '',
-			'meta_query'      => false,
-			'populate_extras' => true,
+			'type'              => 'active',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => false,
+			'show_hidden'       => false,
+			'page_arg'          => 'grpage',
+			'user_id'           => 0,
+			'slug'              => false,
+			'include'           => false,
+			'exclude'           => false,
+			'search_terms'      => '',
+			'meta_query'        => false,
+			'populate_extras'   => true,
 			'update_meta_cache' => true,
 		);
 
 		$r = wp_parse_args( $args, $defaults );
-		extract( $r );
 
-		$this->pag_page = isset( $_REQUEST[$page_arg] ) ? intval( $_REQUEST[$page_arg] ) : $page;
-		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+		$this->set_loop( array( 'params' => $r ) );
+	}
 
-		if ( bp_current_user_can( 'bp_moderate' ) || ( is_user_logged_in() && $user_id == bp_loggedin_user_id() ) )
-			$show_hidden = true;
+	/**
+	 * Get Groups.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
+		if ( bp_current_user_can( 'bp_moderate' ) || ( is_user_logged_in() && $this->user_id == bp_loggedin_user_id() ) ) {
+			$this->args['params']['show_hidden'] = true;
+		}
+
+		if ( 'invites' == $this->type ) {
+			return groups_get_invites_for_user( $this->user_id, $this->per_page, $this->page, $this->exclude );
 
-		if ( 'invites' == $type ) {
-			$this->groups = groups_get_invites_for_user( $user_id, $this->pag_num, $this->pag_page, $exclude );
-		} else if ( 'single-group' == $type ) {
+		} else if ( 'single-group' == $this->type ) {
 			$this->single_group = true;
 
 			if ( groups_get_current_group() ) {
@@ -241,182 +177,60 @@ class BP_Groups_Template {
 
 			} else {
 				$group = groups_get_group( array(
-					'group_id'        => BP_Groups_Group::get_id_from_slug( $r['slug'] ),
-					'populate_extras' => $r['populate_extras'],
+					'group_id'        => BP_Groups_Group::get_id_from_slug( $this->slug ),
+					'populate_extras' => $this->populate_extras,
 				) );
 			}
 
-			// backwards compatibility - the 'group_id' variable is not part of the
-			// BP_Groups_Group object, but we add it here for devs doing checks against it
-			//
-			// @see https://buddypress.trac.wordpress.org/changeset/3540
-			//
-			// this is subject to removal in a future release; devs should check against
-			// $group->id instead
-			$group->group_id = $group->id;
-
-			$this->groups = array( $group );
-
-		} else {
-			$this->groups = groups_get_groups( array(
-				'type'              => $type,
-				'order'             => $order,
-				'orderby'           => $orderby,
-				'per_page'          => $this->pag_num,
-				'page'              => $this->pag_page,
-				'user_id'           => $user_id,
-				'search_terms'      => $search_terms,
-				'meta_query'        => $meta_query,
-				'include'           => $include,
-				'exclude'           => $exclude,
-				'populate_extras'   => $populate_extras,
-				'update_meta_cache' => $update_meta_cache,
-				'show_hidden'       => $show_hidden
-			) );
-		}
-
-		if ( 'invites' == $type ) {
-			$this->total_group_count = (int) $this->groups['total'];
-			$this->group_count       = (int) $this->groups['total'];
-			$this->groups            = $this->groups['groups'];
-		} else if ( 'single-group' == $type ) {
 			if ( empty( $group->id ) ) {
-				$this->total_group_count = 0;
-				$this->group_count       = 0;
-			} else {
-				$this->total_group_count = 1;
-				$this->group_count       = 1;
-			}
-		} else {
-			if ( empty( $max ) || $max >= (int) $this->groups['total'] ) {
-				$this->total_group_count = (int) $this->groups['total'];
-			} else {
-				$this->total_group_count = (int) $max;
-			}
-
-			$this->groups = $this->groups['groups'];
-
-			if ( !empty( $max ) ) {
-				if ( $max >= count( $this->groups ) ) {
-					$this->group_count = count( $this->groups );
-				} else {
-					$this->group_count = (int) $max;
-				}
+				return array( 'groups' => array(), 'total' => 0 );
 			} else {
-				$this->group_count = count( $this->groups );
+				return array( 'groups' => array( $group ), 'total' => 1 );
 			}
-		}
-
-		// Build pagination links
-		if ( (int) $this->total_group_count && (int) $this->pag_num ) {
-			$pag_args = array(
-				$page_arg => '%#%'
-			);
 
-			if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
-				$base = remove_query_arg( 's', wp_get_referer() );
-			} else {
-				$base = '';
-			}
-
-			$add_args = array(
-				'num'     => $this->pag_num,
-				'sortby'  => $this->sort_by,
-				'order'   => $this->order,
-			);
-
-			if ( ! empty( $search_terms ) ) {
-				$add_args['s'] = urlencode( $search_terms );
-			}
-
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $pag_args, $base ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_group_count / (int) $this->pag_num ),
-				'current'   => $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Group pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Group pagination next text', 'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => $add_args,
-			) );
+		} else {
+			return groups_get_groups( $this->args['params'] );
 		}
 	}
 
 	/**
-	 * Whether there are groups available in the loop.
+	 * Set Groups pagination.
 	 *
-	 * @see bp_has_groups()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are items in the loop, otherwise false.
-	 */
-	function has_groups() {
-		if ( $this->group_count )
-			return true;
-
-		return false;
-	}
-
-	/**
-	 * Set up the next group and iterate index.
-	 *
-	 * @return object The next group to iterate over.
-	 */
-	function next_group() {
-		$this->current_group++;
-		$this->group = $this->groups[$this->current_group];
-
-		return $this->group;
-	}
-
-	/**
-	 * Rewind the groups and reset member index.
+	 * @access public
 	 */
-	function rewind_groups() {
-		$this->current_group = -1;
-		if ( $this->group_count > 0 ) {
-			$this->group = $this->groups[0];
-		}
-	}
+	public function set_pag_links() {
+		$pag_args = array(
+			$this->page_arg => '%#%'
+		);
 
-	/**
-	 * Whether there are groups left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_groups()} as part of the while loop
-	 * that controls iteration inside the groups loop, eg:
-	 *     while ( bp_groups() ) { ...
-	 *
-	 * @see bp_groups()
-	 *
-	 * @return bool True if there are more groups to show, otherwise false.
-	 */
-	function groups() {
-		if ( $this->current_group + 1 < $this->group_count ) {
-			return true;
-		} elseif ( $this->current_group + 1 == $this->group_count ) {
-			do_action('group_loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_groups();
+		if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
+			$base = remove_query_arg( 's', wp_get_referer() );
+		} else {
+			$base = '';
 		}
 
-		$this->in_the_loop = false;
-		return false;
-	}
+		$add_args = array(
+			'num'     => $this->per_page,
+			'sortby'  => $this->orderby,
+			'order'   => $this->order,
+		);
 
-	/**
-	 * Set up the current group inside the loop.
-	 *
-	 * Used by {@link bp_the_group()} to set up the current group data
-	 * while looping, so that template tags used during that iteration make
-	 * reference to the current member.
-	 *
-	 * @see bp_the_group()
-	 */
-	function the_group() {
-		$this->in_the_loop = true;
-		$this->group       = $this->next_group();
+		if ( ! empty( $this->search_terms ) ) {
+			$add_args['s'] = urlencode( $this->search_terms );
+		}
 
-		if ( 0 == $this->current_group ) // loop has just started
-			do_action('group_loop_start');
+		$this->pag_links = paginate_links( array(
+			'base'      => add_query_arg( $pag_args, $base ),
+			'format'    => '',
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
+			'prev_text' => _x( '&larr;', 'Group pagination previous text', 'buddypress' ),
+			'next_text' => _x( '&rarr;', 'Group pagination next text', 'buddypress' ),
+			'mid_size'  => 1,
+			'add_args'  => $add_args,
+		) );
 	}
 }
 
@@ -539,7 +353,7 @@ function bp_has_groups( $args = '' ) {
 	) );
 
 	// Filter and return whether or not the groups loop has groups in it
-	return apply_filters( 'bp_has_groups', $groups_template->has_groups(), $groups_template, $r );
+	return apply_filters( 'bp_has_groups', $groups_template->has_items(), $groups_template, $r );
 }
 
 /**
@@ -549,7 +363,7 @@ function bp_has_groups( $args = '' ) {
  */
 function bp_groups() {
 	global $groups_template;
-	return $groups_template->groups();
+	return $groups_template->items();
 }
 
 /**
@@ -559,7 +373,7 @@ function bp_groups() {
  */
 function bp_the_group() {
 	global $groups_template;
-	return $groups_template->the_group();
+	return $groups_template->the_item();
 }
 
 /**
@@ -1575,9 +1389,9 @@ function bp_groups_pagination_count() {
 	function bp_get_groups_pagination_count() {
 		global $groups_template;
 
-		$start_num = intval( ( $groups_template->pag_page - 1 ) * $groups_template->pag_num ) + 1;
+		$start_num = intval( ( $groups_template->page - 1 ) * $groups_template->per_page ) + 1;
 		$from_num  = bp_core_number_format( $start_num );
-		$to_num    = bp_core_number_format( ( $start_num + ( $groups_template->pag_num - 1 ) > $groups_template->total_group_count ) ? $groups_template->total_group_count : $start_num + ( $groups_template->pag_num - 1 ) );
+		$to_num    = bp_core_number_format( ( $start_num + ( $groups_template->per_page - 1 ) > $groups_template->total_group_count ) ? $groups_template->total_group_count : $start_num + ( $groups_template->per_page - 1 ) );
 		$total     = bp_core_number_format( $groups_template->total_group_count );
 
 		return apply_filters( 'bp_get_groups_pagination_count', sprintf( _n( 'Viewing 1 group', 'Viewing %1$s - %2$s of %3$s groups', $total, 'buddypress' ), $from_num, $to_num, $total ), $from_num, $to_num, $total );
@@ -2931,18 +2745,32 @@ function bp_total_group_count_for_user( $user_id = 0 ) {
  * Group Members Template Tags
  **/
 
-class BP_Groups_Group_Members_Template {
-	var $current_member = -1;
-	var $member_count;
-	var $members;
-	var $member;
-
-	var $in_the_loop;
+class BP_Groups_Group_Members_Template extends BP_Template_Loop {
 
-	var $pag_page;
-	var $pag_num;
-	var $pag_links;
-	var $total_group_count;
+	/**
+	 * Default arguments for Group Members loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'member',
+			'item_plural'       => 'members',
+			'item_total_count'  => 'total_member_count',
+			'item_count'        => 'member_count',
+			'current_item'      => 'current_member',
+			'hook_prefix'       => 'group_member',
+		),
+		'params' => array(
+			'page_arg'          => 'mlpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor.
@@ -2988,6 +2816,7 @@ class BP_Groups_Group_Members_Template {
 			'group_id'            => bp_get_current_group_id(),
 			'page'                => 1,
 			'per_page'            => 20,
+			'page_arg'            => 'mlpage',
 			'max'                 => false,
 			'exclude'             => false,
 			'exclude_admins_mods' => 1,
@@ -2997,103 +2826,58 @@ class BP_Groups_Group_Members_Template {
 			'type'                => 'last_joined',
 		) );
 
-		// @todo No
-		extract( $r );
-
-		$this->pag_page = isset( $_REQUEST['mlpage'] ) ? intval( $_REQUEST['mlpage'] ) : $r['page'];
-		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $per_page;
+		$this->set_loop( array( 'params' => $r ) );
+	}
 
+	/**
+	 * Get Group members.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
 		/**
 		 * Check the current group is the same as the supplied group ID.
 		 * It can differ when using {@link bp_group_has_members()} outside the Groups screens.
 		 */
 		$current_group = groups_get_current_group();
 		if ( ! $current_group || $current_group && $current_group->id !== bp_get_current_group_id() ) {
-			$current_group = groups_get_group( array( 'group_id' => $r['group_id'] ) );
+			$current_group = groups_get_group( array( 'group_id' => $this->args['params']['group_id'] ) );
 		}
 
-		// Assemble the base URL for pagination
-		$base_url = trailingslashit( bp_get_group_permalink( $current_group ) . bp_current_action() );
-		if ( bp_action_variable() ) {
-			$base_url = trailingslashit( $base_url . bp_action_variable() );
-		}
-
-		$members_args = $r;
-
-		$members_args['page']     = $this->pag_page;
-		$members_args['per_page'] = $this->pag_num;
+		$this->current_group = $current_group;
 
-		$this->members = groups_get_group_members( $members_args );
+		$group_members = groups_get_group_members( $this->args['params'] );
 
-		if ( !$max || $max >= (int) $this->members['count'] )
-			$this->total_member_count = (int) $this->members['count'];
-		else
-			$this->total_member_count = (int) $max;
-
-		$this->members = $this->members['members'];
+		// Make sure to return an array using the keys the abastract class is waiting for
+		return array( 'members' => $group_members['members'], 'total' => $group_members['count'] );
+	}
 
-		if ( $max ) {
-			if ( $max >= count($this->members) )
-				$this->member_count = count($this->members);
-			else
-				$this->member_count = (int) $max;
-		} else {
-			$this->member_count = count($this->members);
+	/**
+	 * Set Group members pagination.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function set_pag_links() {
+		// Assemble the base URL for pagination
+		$base_url = trailingslashit( bp_get_group_permalink( $this->current_group ) . bp_current_action() );
+		if ( bp_action_variable() ) {
+			$base_url = trailingslashit( $base_url . bp_action_variable() );
 		}
 
 		$this->pag_links = paginate_links( array(
 			'base'      => add_query_arg( array( 'mlpage' => '%#%' ), $base_url ),
 			'format'    => '',
-			'total'     => ! empty( $this->pag_num ) ? ceil( $this->total_member_count / $this->pag_num ) : $this->total_member_count,
-			'current'   => $this->pag_page,
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
 			'prev_text' => '&larr;',
 			'next_text' => '&rarr;',
 			'mid_size'  => 1,
 			'add_args'  => array(),
-		));
-	}
-
-	function has_members() {
-		if ( $this->member_count )
-			return true;
-
-		return false;
-	}
-
-	function next_member() {
-		$this->current_member++;
-		$this->member = $this->members[$this->current_member];
-
-		return $this->member;
-	}
-
-	function rewind_members() {
-		$this->current_member = -1;
-		if ( $this->member_count > 0 ) {
-			$this->member = $this->members[0];
-		}
-	}
-
-	function members() {
-		if ( $this->current_member + 1 < $this->member_count ) {
-			return true;
-		} elseif ( $this->current_member + 1 == $this->member_count ) {
-			do_action('loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_members();
-		}
-
-		$this->in_the_loop = false;
-		return false;
-	}
-
-	function the_member() {
-
-		$this->in_the_loop = true;
-		$this->member      = $this->next_member();
-
-		if ( 0 == $this->current_member ) // loop has just started
-			do_action('loop_start');
+		) );
 	}
 }
 
@@ -3149,19 +2933,19 @@ function bp_group_has_members( $args = '' ) {
 	}
 
 	$members_template = new BP_Groups_Group_Members_Template( $r );
-	return apply_filters( 'bp_group_has_members', $members_template->has_members(), $members_template );
+	return apply_filters( 'bp_group_has_members', $members_template->has_items(), $members_template );
 }
 
 function bp_group_members() {
 	global $members_template;
 
-	return $members_template->members();
+	return $members_template->items();
 }
 
 function bp_group_the_member() {
 	global $members_template;
 
-	return $members_template->the_member();
+	return $members_template->the_item();
 }
 
 /**
@@ -3344,7 +3128,7 @@ function bp_group_member_id() {
 function bp_group_member_needs_pagination() {
 	global $members_template;
 
-	if ( $members_template->total_member_count > $members_template->pag_num )
+	if ( $members_template->total_member_count > $members_template->per_page )
 		return true;
 
 	return false;
@@ -3372,9 +3156,9 @@ function bp_group_member_pagination_count() {
 	function bp_get_group_member_pagination_count() {
 		global $members_template;
 
-		$start_num = intval( ( $members_template->pag_page - 1 ) * $members_template->pag_num ) + 1;
+		$start_num = intval( ( $members_template->page - 1 ) * $members_template->per_page ) + 1;
 		$from_num = bp_core_number_format( $start_num );
-		$to_num = bp_core_number_format( ( $start_num + ( $members_template->pag_num - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->pag_num - 1 ) );
+		$to_num = bp_core_number_format( ( $start_num + ( $members_template->per_page - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->per_page - 1 ) );
 		$total = bp_core_number_format( $members_template->total_member_count );
 
 		return apply_filters( 'bp_get_group_member_pagination_count', sprintf( _n( 'Viewing 1 member', 'Viewing %1$s - %2$s of %3$s members', $total, 'buddypress' ), $from_num, $to_num, $total ), $from_num, $to_num, $total );
@@ -3981,18 +3765,32 @@ function bp_custom_group_fields() {
  * Membership Requests Template Tags
  **/
 
-class BP_Groups_Membership_Requests_Template {
-	var $current_request = -1;
-	var $request_count;
-	var $requests;
-	var $request;
+class BP_Groups_Membership_Requests_Template extends BP_Template_Loop {
 
-	var $in_the_loop;
-
-	var $pag_page;
-	var $pag_num;
-	var $pag_links;
-	var $total_request_count;
+	/**
+	 * Default arguments for Group Membership requests loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'request',
+			'item_plural'       => 'requests',
+			'item_total_count'  => 'total_request_count',
+			'item_count'        => 'request_count',
+			'current_item'      => 'current_request',
+			'hook_prefix'       => 'group_request',
+		),
+		'params' => array(
+			'page_arg'          => 'mrpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -4028,103 +3826,47 @@ class BP_Groups_Membership_Requests_Template {
 			'page'     => 1,
 			'max'      => false,
 			'type'     => 'first_joined',
+			'page_arg' => 'mrpage',
 		) );
 
-		$this->pag_page = isset( $_REQUEST['mrpage'] ) ? intval( $_REQUEST['mrpage'] ) : $r['page'];
-		$this->pag_num  = isset( $_REQUEST['num'] ) ? intval( $_REQUEST['num'] ) : $r['per_page'];
+		$this->set_loop( array( 'params' => $r ) );
+	}
 
+	/**
+	 * Get Group Membership requests.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
 		$mquery = new BP_Group_Member_Query( array(
-			'group_id' => $r['group_id'],
-			'type'     => $r['type'],
-			'per_page' => $this->pag_num,
-			'page'     => $this->pag_page,
+			'group_id' => $this->args['params']['group_id'],
+			'type'     => $this->args['params']['type'],
+			'per_page' => $this->per_page,
+			'page'     => $this->page,
 
 			// These filters ensure we only get pending requests
 			'is_confirmed' => false,
 			'inviter_id'   => 0,
 		) );
 
-		$this->requests      = array_values( $mquery->results );
-		$this->request_count = count( $this->requests );
+		// Make sure to return an array using the keys the abastract class is waiting for
+		$requests = array( 'requests' => array_values( $mquery->results ), 'total' => $mquery->total_users );
 
 		// Compatibility with legacy format of request data objects
-		foreach ( $this->requests as $rk => $rv ) {
+		foreach ( $requests['requests'] as $rk => $rv ) {
 			// For legacy reasons, the 'id' property of each
 			// request must match the membership id, not the ID of
 			// the user (as it's returned by BP_Group_Member_Query)
-			$this->requests[ $rk ]->user_id = $rv->ID;
-			$this->requests[ $rk ]->id      = $rv->membership_id;
+			$requests['requests'][ $rk ]->user_id = $rv->ID;
+			$requests['requests'][ $rk ]->id      = $rv->membership_id;
 
 			// Miscellaneous values
-			$this->requests[ $rk ]->group_id   = $r['group_id'];
-		}
-
-		if ( !$r['max'] || $r['max'] >= (int) $mquery->total_users )
-			$this->total_request_count = (int) $mquery->total_users;
-		else
-			$this->total_request_count = (int) $r['max'];
-
-		if ( $r['max'] ) {
-			if ( $r['max'] >= count($this->requests) )
-				$this->request_count = count($this->requests);
-			else
-				$this->request_count = (int) $r['max'];
-		} else {
-			$this->request_count = count($this->requests);
-		}
-
-		$this->pag_links = paginate_links( array(
-			'base'      => add_query_arg( 'mrpage', '%#%' ),
-			'format'    => '',
-			'total'     => ceil( $this->total_request_count / $this->pag_num ),
-			'current'   => $this->pag_page,
-			'prev_text' => '&larr;',
-			'next_text' => '&rarr;',
-			'mid_size'  => 1,
-			'add_args'  => array(),
-		) );
-	}
-
-	function has_requests() {
-		if ( $this->request_count )
-			return true;
-
-		return false;
-	}
-
-	function next_request() {
-		$this->current_request++;
-		$this->request = $this->requests[$this->current_request];
-
-		return $this->request;
-	}
-
-	function rewind_requests() {
-		$this->current_request = -1;
-
-		if ( $this->request_count > 0 )
-			$this->request = $this->requests[0];
-	}
-
-	function requests() {
-		if ( $this->current_request + 1 < $this->request_count ) {
-			return true;
-		} elseif ( $this->current_request + 1 == $this->request_count ) {
-			do_action('group_request_loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_requests();
+			$requests['requests'][ $rk ]->group_id   = $this->args['params']['group_id'];
 		}
 
-		$this->in_the_loop = false;
-		return false;
-	}
-
-	function the_request() {
-		$this->in_the_loop = true;
-		$this->request     = $this->next_request();
-
-		if ( 0 == $this->current_request ) // loop has just started
-			do_action('group_request_loop_start');
+		return $requests;
 	}
 }
 
@@ -4152,19 +3894,19 @@ function bp_group_has_membership_requests( $args = '' ) {
 	$r = wp_parse_args( $args, $defaults );
 
 	$requests_template = new BP_Groups_Membership_Requests_Template( $r );
-	return apply_filters( 'bp_group_has_membership_requests', $requests_template->has_requests(), $requests_template );
+	return apply_filters( 'bp_group_has_membership_requests', $requests_template->has_items(), $requests_template );
 }
 
 function bp_group_membership_requests() {
 	global $requests_template;
 
-	return $requests_template->requests();
+	return $requests_template->items();
 }
 
 function bp_group_the_membership_request() {
 	global $requests_template;
 
-	return $requests_template->the_request();
+	return $requests_template->the_item();
 }
 
 function bp_group_request_user_avatar_thumb() {
@@ -4250,9 +3992,9 @@ function bp_group_requests_pagination_count() {
 	function bp_get_group_requests_pagination_count() {
 		global $requests_template;
 
-		$start_num = intval( ( $requests_template->pag_page - 1 ) * $requests_template->pag_num ) + 1;
+		$start_num = intval( ( $requests_template->page - 1 ) * $requests_template->per_page ) + 1;
 		$from_num  = bp_core_number_format( $start_num );
-		$to_num    = bp_core_number_format( ( $start_num + ( $requests_template->pag_num - 1 ) > $requests_template->total_request_count ) ? $requests_template->total_request_count : $start_num + ( $requests_template->pag_num - 1 ) );
+		$to_num    = bp_core_number_format( ( $start_num + ( $requests_template->per_page - 1 ) > $requests_template->total_request_count ) ? $requests_template->total_request_count : $start_num + ( $requests_template->per_page - 1 ) );
 		$total     = bp_core_number_format( $requests_template->total_request_count );
 
 		return apply_filters( 'bp_get_group_requests_pagination_count', sprintf( _n( 'Viewing 1 request', 'Viewing %1$s - %2$s of %3$s requests', $total, 'buddypress' ), $from_num, $to_num, $total ), $from_num, $to_num, $total );
@@ -4262,19 +4004,46 @@ function bp_group_requests_pagination_count() {
  * Invite Friends Template Tags
  **/
 
-class BP_Groups_Invite_Template {
-	var $current_invite = -1;
-	var $invite_count;
-	var $invites;
-	var $invite;
+class BP_Groups_Invite_Template extends BP_Template_Loop {
 
-	var $in_the_loop;
-
-	var $pag_page;
-	var $pag_num;
-	var $pag_links;
-	var $total_invite_count;
+	/**
+	 * Default arguments for Groups Invite loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'invite',
+			'item_plural'       => 'invites',
+			'item_total_count'  => 'total_invite_count',
+			'item_count'        => 'invite_count',
+			'current_item'      => 'current_invite',
+			'hook_prefix'       => 'group_invite',
+		),
+		'params' => array(
+			'page_arg'          => 'invitepage',
+			'page'              => 1,
+			'per_page'          => 10,
+			'max'               => 0,
+		)
+	);
 
+	/**
+	 * Constructor.
+	 *
+	 *  @param array $args {
+	 *     An array of optional arguments.
+	 *     @type int $user_id ID of the user
+	 *     @type int $group_id ID of the group
+	 *     @type int $page Page of results to be queried. Default: 1.
+	 *     @type int $per_page Number of items to return per page of
+	 *           results. Default: 10.
+	 *     @type string page_arg Optional.
+	 * }
+	 */
 	public function __construct( $args = array() ) {
 
 		// Backward compatibility with old method of passing arguments
@@ -4295,82 +4064,55 @@ class BP_Groups_Invite_Template {
 			'group_id' => bp_get_current_group_id(),
 			'page'     => 1,
 			'per_page' => 10,
+			'page_arg' => 'invitepage',
 		) );
 
-		$this->pag_num  = intval( $r['per_page'] );
-		$this->pag_page = isset( $_REQUEST['invitepage'] ) ? intval( $_REQUEST['invitepage'] ) : $r['page'];
+		$this->set_loop( array( 'params' => $r ) );
+	}
 
+	/**
+	 * Get Groups Invite.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
 		$iquery = new BP_Group_Member_Query( array(
-			'group_id' => $r['group_id'],
+			'group_id' => $this->args['params']['group_id'],
 			'type'     => 'first_joined',
-			'per_page' => $this->pag_num,
-			'page'     => $this->pag_page,
+			'per_page' => $this->per_page,
+			'page'     => $this->page,
 
 			// These filters ensure we get only pending invites
 			'is_confirmed' => false,
-			'inviter_id'   => $r['user_id'],
+			'inviter_id'   => $this->args['params']['user_id'],
 		) );
-		$this->invite_data = $iquery->results;
-
-		$this->total_invite_count = $iquery->total_users;
-		$this->invites		  = array_values( wp_list_pluck( $this->invite_data, 'ID' ) );
-		$this->invite_count       = count( $this->invites );
-
-		// If per_page is set to 0 (show all results), don't generate
-		// pag_links
-		if ( ! empty( $this->pag_num ) ) {
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( 'invitepage', '%#%' ),
-				'format'    => '',
-				'total'     => ceil( $this->total_invite_count / $this->pag_num ),
-				'current'   => $this->pag_page,
-				'prev_text' => '&larr;',
-				'next_text' => '&rarr;',
-				'mid_size'  => 1,
-				'add_args'  => array(),
-			) );
-		} else {
-			$this->pag_links = '';
-		}
-	}
-
-	function has_invites() {
-		if ( $this->invite_count )
-			return true;
-
-		return false;
-	}
-
-	function next_invite() {
-		$this->current_invite++;
-		$this->invite = $this->invites[$this->current_invite];
 
-		return $this->invite;
-	}
+		// This will be use in BP_Groups_Invite_Template->the_invite()
+		$this->invite_data = $iquery->results;
 
-	function rewind_invites() {
-		$this->current_invite = -1;
-		if ( $this->invite_count > 0 )
-			$this->invite = $this->invites[0];
+		return array( 'invites' => array_values( wp_list_pluck( $this->invite_data, 'ID' ) ), 'total' => $iquery->total_users );
 	}
 
-	function invites() {
-		if ( $this->current_invite + 1 < $this->invite_count ) {
-			return true;
-		} elseif ( $this->current_invite + 1 == $this->invite_count ) {
-			do_action('loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_invites();
-		}
-
-		$this->in_the_loop = false;
-		return false;
+	/**
+	 * Override parent method as we need to do custom work.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @uses  BP_Groups_Invite_Template->the_invite()
+	 */
+	public function the_item() {
+		$this->the_invite();
 	}
 
-	function the_invite() {
+	/**
+	 * Set up the current invite inside the loop.
+	 */
+	public function the_invite() {
 		global $group_id;
 		$this->in_the_loop      = true;
-		$user_id                = $this->next_invite();
+		$user_id                = $this->next_item();
 
 		$this->invite           = new stdClass;
 		$this->invite->user     = $this->invite_data[ $user_id ];
@@ -4395,16 +4137,13 @@ class BP_Groups_Invite_Template {
 			$this->invite->user->total_groups = sprintf( _n( '%d group', '%d groups', $total_groups, 'buddypress' ), $total_groups );
 		}
 
-		if ( bp_is_active( 'friends' ) ) {
-			$this->invite->user->total_friends = BP_Friends_Friendship::total_friend_count( $user_id );
-		}
-
 		$this->invite->user->total_blogs = null;
 
 		$this->invite->group_id = $group_id; // Globaled in bp_group_has_invites()
 
-		if ( 0 == $this->current_invite ) // loop has just started
-			do_action('loop_start');
+		if ( 0 == $this->current_invite ) {
+			do_action( 'group_invite_loop_start' );
+		}
 	}
 }
 
@@ -4436,19 +4175,19 @@ function bp_group_has_invites( $args = '' ) {
 	}
 
 	$invites_template = new BP_Groups_Invite_Template( $r );
-	return apply_filters( 'bp_group_has_invites', $invites_template->has_invites(), $invites_template );
+	return apply_filters( 'bp_group_has_invites', $invites_template->has_items(), $invites_template );
 }
 
 function bp_group_invites() {
 	global $invites_template;
 
-	return $invites_template->invites();
+	return $invites_template->items();
 }
 
 function bp_group_the_invite() {
 	global $invites_template;
 
-	return $invites_template->the_invite();
+	return $invites_template->the_item();
 }
 
 function bp_group_invite_item_id() {
@@ -4542,9 +4281,9 @@ function bp_group_invite_pagination_count() {
 	function bp_get_group_invite_pagination_count() {
 		global $invites_template;
 
-		$start_num = intval( ( $invites_template->pag_page - 1 ) * $invites_template->pag_num ) + 1;
+		$start_num = intval( ( $invites_template->page - 1 ) * $invites_template->per_page ) + 1;
 		$from_num  = bp_core_number_format( $start_num );
-		$to_num    = bp_core_number_format( ( $start_num + ( $invites_template->pag_num - 1 ) > $invites_template->total_invite_count ) ? $invites_template->total_invite_count : $start_num + ( $invites_template->pag_num - 1 ) );
+		$to_num    = bp_core_number_format( ( $start_num + ( $invites_template->per_page - 1 ) > $invites_template->total_invite_count ) ? $invites_template->total_invite_count : $start_num + ( $invites_template->per_page - 1 ) );
 		$total     = bp_core_number_format( $invites_template->total_invite_count );
 
 		return apply_filters( 'bp_get_groups_pagination_count', sprintf( _n( 'Viewing 1 invitation', 'Viewing %1$s - %2$s of %3$s invitations', $total, 'buddypress' ), $from_num, $to_num, $total ), $from_num, $to_num, $total );
diff --git src/bp-members/bp-members-template.php src/bp-members/bp-members-template.php
index feb679f..c95d85f 100644
--- src/bp-members/bp-members-template.php
+++ src/bp-members/bp-members-template.php
@@ -180,79 +180,32 @@ function bp_activate_slug() {
  *
  * Responsible for loading a group of members into a loop for display.
  */
-class BP_Core_Members_Template {
+class BP_Core_Members_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Members loop.
 	 *
-	 * @access public
-	 * @var int
-	 */
-	var $current_member = -1;
-
-	/**
-	 * The number of members returned by the paged query.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @access public
-	 * @var int
-	 */
-	var $member_count;
-
-	/**
-	 * Array of members located by the query.
-	 *
-	 * @access public
+	 * @access protected
 	 * @var array
 	 */
-	var $members;
-
-	/**
-	 * The member object currently being iterated on.
-	 *
-	 * @access public
-	 * @var object
-	 */
-	var $member;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
-	 *
-	 * @access public
-	 * @var bool
-	 */
-	var $in_the_loop;
-
-	/**
-	 * The page number being requested.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	var $pag_page;
-
-	/**
-	 * The number of items being requested per page.
-	 *
-	 * @access public
-	 * @var public
-	 */
-	var $pag_num;
-
-	/**
-	 * An HTML string containing pagination links.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	var $pag_links;
-
-	/**
-	 * The total number of members matching the query parameters.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	var $total_member_count;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'member',
+			'item_plural'       => 'members',
+			'item_total_count'  => 'total_member_count',
+			'item_count'        => 'member_count',
+			'current_item'      => 'current_member',
+			'hook_prefix'       => 'member',
+		),
+		'params' => array(
+			'page_arg'          => 'upage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -275,159 +228,98 @@ class BP_Core_Members_Template {
 	 * @param array|string $member_type     Array or comma-separated string of member types to limit results to.
 	 */
 	function __construct( $type, $page_number, $per_page, $max, $user_id, $search_terms, $include, $populate_extras, $exclude, $meta_key, $meta_value, $page_arg = 'upage', $member_type = '' ) {
+		$params = array( 'params' => array(
+			'type'            => $type,
+			'per_page'        => $per_page,
+			'page'            => $page_number,
+			'user_id'         => $user_id,
+			'include'         => $include,
+			'search_terms'    => $search_terms,
+			'populate_extras' => $populate_extras,
+			'exclude'         => $exclude,
+			'meta_key'        => $meta_key,
+			'meta_value'      => $meta_value,
+			'member_type'     => $member_type,
+			'page_arg'        => $page_arg,
+			'max'             => $max,
+		) );
 
-		$this->pag_page = !empty( $_REQUEST[$page_arg] ) ? intval( $_REQUEST[$page_arg] ) : (int) $page_number;
-		$this->pag_num  = !empty( $_REQUEST['num'] )   ? intval( $_REQUEST['num'] )   : (int) $per_page;
-		$this->type     = $type;
-
-		if ( !empty( $_REQUEST['letter'] ) )
-			$this->members = BP_Core_User::get_users_by_letter( $_REQUEST['letter'], $this->pag_num, $this->pag_page, $populate_extras, $exclude );
-		else
-			$this->members = bp_core_get_users( array( 'type' => $this->type, 'per_page' => $this->pag_num, 'page' => $this->pag_page, 'user_id' => $user_id, 'include' => $include, 'search_terms' => $search_terms, 'populate_extras' => $populate_extras, 'exclude' => $exclude, 'meta_key' => $meta_key, 'meta_value' => $meta_value, 'member_type' => $member_type ) );
-
-		if ( !$max || $max >= (int) $this->members['total'] )
-			$this->total_member_count = (int) $this->members['total'];
-		else
-			$this->total_member_count = (int) $max;
-
-		$this->members = $this->members['users'];
-
-		if ( $max ) {
-			if ( $max >= count( $this->members ) ) {
-				$this->member_count = count( $this->members );
-			} else {
-				$this->member_count = (int) $max;
-			}
-		} else {
-			$this->member_count = count( $this->members );
-		}
-
-		if ( (int) $this->total_member_count && (int) $this->pag_num ) {
-			$pag_args = array(
-				$page_arg => '%#%',
-			);
-
-			if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
-				$base = remove_query_arg( 's', wp_get_referer() );
-			} else {
-				$base = '';
-			}
-
-			/**
-			 * Defaults to an empty array to make sure paginate_links()
-			 * won't add the $page_arg to the links which would break
-			 * pagination in case javascript is disabled.
-			 */
-			$add_args = array();
-
-			if ( ! empty( $search_terms ) ) {
-				$add_args['s'] = urlencode( $search_terms );
-			}
-
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $pag_args, $base ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_member_count / (int) $this->pag_num ),
-				'current'   => (int) $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Member pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Member pagination next text', 'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => $add_args,
-			) );
-		}
+		$this->set_loop( $params );
 	}
 
 	/**
-	 * Whether there are members available in the loop.
+	 * Get Members.
 	 *
-	 * @see bp_has_members()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are items in the loop, otherwise false.
+	 * @access public
 	 */
-	function has_members() {
-		if ( $this->member_count )
-			return true;
+	public function get_items() {
+		if ( ! empty( $_REQUEST['letter'] ) ) {
+			$members = BP_Core_User::get_users_by_letter(
+				$_REQUEST['letter'],
+				$this->args['params']['per_page'],
+				$this->args['params']['page'],
+				$this->args['params']['populate_extras'],
+				$this->args['params']['exclude']
+			);
+
+		} else {
+			$members = bp_core_get_users( $this->args['params'] );
+		}
 
-		return false;
+		return array( 'members' => $members['users'] , 'total' => $members['total'] );
 	}
 
 	/**
-	 * Set up the next member and iterate index.
+	 * Set Members pagination.
 	 *
-	 * @return object The next member to iterate over.
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
 	 */
-	function next_member() {
-		$this->current_member++;
-		$this->member = $this->members[$this->current_member];
-
-		return $this->member;
-	}
+	public function set_pag_links() {
+		$pag_args = array(
+			$this->page_arg => '%#%'
+		);
 
-	/**
-	 * Rewind the members and reset member index.
-	 */
-	function rewind_members() {
-		$this->current_member = -1;
-		if ( $this->member_count > 0 ) {
-			$this->member = $this->members[0];
+		if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
+			$base = remove_query_arg( 's', wp_get_referer() );
+		} else {
+			$base = '';
 		}
-	}
 
-	/**
-	 * Whether there are members left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_members()} as part of the while loop
-	 * that controls iteration inside the members loop, eg:
-	 *     while ( bp_members() ) { ...
-	 *
-	 * @see bp_members()
-	 *
-	 * @return bool True if there are more members to show, otherwise false.
-	 */
-	function members() {
-		if ( $this->current_member + 1 < $this->member_count ) {
-			return true;
-		} elseif ( $this->current_member + 1 == $this->member_count ) {
+		/**
+		 * Defaults to an empty array to make sure paginate_links()
+		 * won't add the $page_arg to the links which would break
+		 * pagination in case javascript is disabled.
+		 */
+		$add_args = array();
 
-			/**
-			 * Fires right before the rewinding of members listing.
-			 *
-			 * @since BuddyPress (1.5.0)
-			 */
-			do_action('member_loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_members();
+		if ( ! empty( $this->search_terms ) ) {
+			$add_args['s'] = urlencode( $this->search_terms );
 		}
 
-		$this->in_the_loop = false;
-		return false;
+		$this->pag_links = paginate_links( array(
+			'base'      => add_query_arg( $pag_args, $base ),
+			'format'    => '',
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
+			'prev_text' => _x( '&larr;', 'Member pagination previous text', 'buddypress' ),
+			'next_text' => _x( '&rarr;', 'Member pagination next text', 'buddypress' ),
+			'mid_size'  => 1,
+			'add_args'  => $add_args,
+		) );
 	}
 
 	/**
-	 * Set up the current member inside the loop.
-	 *
-	 * Used by {@link bp_the_member()} to set up the current member data
-	 * while looping, so that template tags used during that iteration make
-	 * reference to the current member.
+	 * Rewind Members is an alias of rewind_items()
 	 *
-	 * @see bp_the_member()
+	 * @access public
 	 */
-	function the_member() {
-
-		$this->in_the_loop = true;
-		$this->member      = $this->next_member();
-
-		// loop has just started
-		if ( 0 == $this->current_member ) {
-
-			/**
-			 * Fires if the current member is the first in the loop.
-			 *
-			 * @since BuddyPress (1.5.0)
-			 */
-			do_action( 'member_loop_start' );
-		}
-
+	public function rewind_members() {
+		_doing_it_wrong( __FUNCTION__, __( 'BP_Core_Members_Template->rewind_members() should not be used directly, please use bp_rewind_members()', 'buddypress' ), '2.3.0' );
+		$this->rewind_items();
 	}
 }
 
@@ -437,7 +329,7 @@ class BP_Core_Members_Template {
 function bp_rewind_members() {
 	global $members_template;
 
-	return $members_template->rewind_members();
+	return $members_template->rewind_items();
 }
 
 /**
@@ -559,7 +451,7 @@ function bp_has_members( $args = '' ) {
 	 * @param bool  $value            Whether or not there are members to iterate over.
 	 * @param array $members_template Populated $members_template global.
 	 */
-	return apply_filters( 'bp_has_members', $members_template->has_members(), $members_template );
+	return apply_filters( 'bp_has_members', $members_template->has_items(), $members_template );
 }
 
 /**
@@ -569,7 +461,7 @@ function bp_has_members( $args = '' ) {
  */
 function bp_the_member() {
 	global $members_template;
-	return $members_template->the_member();
+	return $members_template->the_item();
 }
 
 /**
@@ -579,7 +471,7 @@ function bp_the_member() {
  */
 function bp_members() {
 	global $members_template;
-	return $members_template->members();
+	return $members_template->items();
 }
 
 /**
@@ -596,22 +488,24 @@ function bp_members_pagination_count() {
 	function bp_get_members_pagination_count() {
 		global $members_template;
 
-		if ( empty( $members_template->type ) )
+		if ( empty( $members_template->type ) ) {
 			$members_template->type = '';
+		}
 
-		$start_num = intval( ( $members_template->pag_page - 1 ) * $members_template->pag_num ) + 1;
+		$start_num = intval( ( $members_template->page - 1 ) * $members_template->per_page ) + 1;
 		$from_num  = bp_core_number_format( $start_num );
-		$to_num    = bp_core_number_format( ( $start_num + ( $members_template->pag_num - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->pag_num - 1 ) );
+		$to_num    = bp_core_number_format( ( $start_num + ( $members_template->per_page - 1 ) > $members_template->total_member_count ) ? $members_template->total_member_count : $start_num + ( $members_template->per_page - 1 ) );
 		$total     = bp_core_number_format( $members_template->total_member_count );
 
-		if ( 'active' == $members_template->type )
+		if ( 'active' == $members_template->type ){
 			$pag = sprintf( _n( 'Viewing 1 active member', 'Viewing %1$s - %2$s of %3$s active members', $members_template->total_member_count, 'buddypress' ), $from_num, $to_num, $total );
-		else if ( 'popular' == $members_template->type )
+		} else if ( 'popular' == $members_template->type ) {
 			$pag = sprintf( _n( 'Viewing 1 member with friends', 'Viewing %1$s - %2$s of %3$s members with friends', $members_template->total_member_count, 'buddypress' ), $from_num, $to_num, $total );
-		else if ( 'online' == $members_template->type )
+		} else if ( 'online' == $members_template->type ) {
 			$pag = sprintf( _n( 'Viewing 1 online member', 'Viewing %1$s - %2$s of %3$s online members', $members_template->total_member_count, 'buddypress' ), $from_num, $to_num, $total );
-		else
+		} else {
 			$pag = sprintf( _n( 'Viewing 1 member', 'Viewing %1$s - %2$s of %3$s members', $members_template->total_member_count, 'buddypress' ), $from_num, $to_num, $total );
+		}
 
 		/**
 		 * Filters the members pagination count.
diff --git src/bp-messages/bp-messages-template.php src/bp-messages/bp-messages-template.php
index fe9e1bd..4ab9c7d 100644
--- src/bp-messages/bp-messages-template.php
+++ src/bp-messages/bp-messages-template.php
@@ -13,103 +13,32 @@ if ( !defined( 'ABSPATH' ) ) exit;
 /**
  * Message Box Template Class
  */
-class BP_Messages_Box_Template {
+class BP_Messages_Box_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Messages Box loop.
 	 *
-	 * @access public
-	 * @var int
-	 */
-	public $current_thread = -1;
-
-	/**
-	 * The number of threads returned by the paged query.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @access public
-	 * @var int
-	 */
-	public $current_thread_count = 0;
-
-	/**
-	 * Total number of threads matching the query params.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $total_thread_count = 0;
-
-	/**
-	 * Array of threads located by the query.
-	 *
-	 * @access public
+	 * @access protected
 	 * @var array
 	 */
-	public $threads = array();
-
-	/**
-	 * The thread object currently being iterated on.
-	 *
-	 * @access public
-	 * @var object
-	 */
-	public $thread = false;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
-	 *
-	 * @access public
-	 * @var bool
-	 */
-	public $in_the_loop = false;
-
-	/**
-	 * User ID of the current inbox.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $user_id = 0;
-
-	/**
-	 * The current "box" view ('notices', 'sentbox', 'inbox')
-	 *
-	 * @access public
-	 * @var string
-	 */
-	public $box = 'inbox';
-
-	/**
-	 * The page number being requested.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $pag_page = 1;
-
-	/**
-	 * The number of items being requested per page.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $pag_num = 10;
-
-	/**
-	 * An HTML string containing pagination links.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	public $pag_links = '';
-
-	/**
-	 * Search terms for limiting the thread query.
-	 *
-	 * @access public
-	 * @var string
-	 */
-	public $search_terms = '';
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'thread',
+			'item_plural'       => 'threads',
+			'item_total_count'  => 'total_thread_count',
+			'item_count'        => 'thread_count',
+			'current_item'      => 'current_thread',
+			'hook_prefix'       => 'messages_box',
+		),
+		'params' => array(
+			'page_arg'          => 'mpage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -127,146 +56,107 @@ class BP_Messages_Box_Template {
 	 *        parameter. Default: 'mpage'.
 	 */
 	public function __construct( $user_id, $box, $per_page, $max, $type, $search_terms, $page_arg = 'mpage' ) {
-		$this->pag_page = isset( $_GET[$page_arg] ) ? intval( $_GET[$page_arg] ) : 1;
-		$this->pag_num  = isset( $_GET['num'] )   ? intval( $_GET['num'] )   : $per_page;
-
-		$this->user_id      = $user_id;
-		$this->box          = $box;
-		$this->type         = $type;
-		$this->search_terms = $search_terms;
-
-		if ( 'notices' == $this->box ) {
-			$this->threads = BP_Messages_Notice::get_notices( array(
-				'pag_num'  => $this->pag_num,
-				'pag_page' => $this->pag_page
-			) );
-		} else {
-			$threads = BP_Messages_Thread::get_current_threads_for_user( $this->user_id, $this->box, $this->type, $this->pag_num, $this->pag_page, $this->search_terms );
-
-			$this->threads            = $threads['threads'];
-			$this->total_thread_count = $threads['total'];
-		}
-
-		if ( !$this->threads ) {
-			$this->thread_count       = 0;
-			$this->total_thread_count = 0;
-		} else {
-			$total_notice_count = BP_Messages_Notice::get_total_notice_count();
+		$page = 1;
 
-			if ( !$max || $max >= (int) $total_notice_count ) {
-				if ( 'notices' == $this->box ) {
-					$this->total_thread_count = (int) $total_notice_count;
-				}
-			} else {
-				$this->total_thread_count = (int) $max;
-			}
-
-			if ( $max ) {
-				if ( $max >= count( $this->threads ) ) {
-					$this->thread_count = count( $this->threads );
-				} else {
-					$this->thread_count = (int) $max;
-				}
-			} else {
-				$this->thread_count = count( $this->threads );
-			}
+		if ( ! empty( $_GET[ $page_arg ] ) ) {
+			$page = $_GET[ $page_arg ];
 		}
 
-		if ( (int) $this->total_thread_count && (int) $this->pag_num ) {
-			$pag_args = array(
-				$page_arg => '%#%',
-			);
-
-			if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
-				$base = remove_query_arg( 's', wp_get_referer() );
-			} else {
-				$base = '';
-			}
-
-			$add_args = array();
-
-			if ( ! empty( $this->search_terms ) ) {
-				$add_args['s'] = $this->search_terms;
-			}
+		$params = array( 'params' => array(
+			'user_id'         => $user_id,
+			'box'             => $box,
+			'per_page'        => $per_page,
+			'max'             => $max,
+			'type'            => $type,
+			'search_terms'    => $search_terms,
+			'page_arg'        => $page_arg,
+			'page'            => $page,
+		) );
 
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $pag_args, $base ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_thread_count / (int) $this->pag_num ),
-				'current'   => $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Message pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Message pagination next text', 'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => $add_args,
-			) );
-		}
+		$this->set_loop( $params );
 	}
 
 	/**
-	 * Whether there are threads available in the loop.
+	 * Get Threads.
 	 *
-	 * @see bp_has_message_threads()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are items in the loop, otherwise false.
+	 * @access public
 	 */
-	public function has_threads() {
-		if ( $this->thread_count ) {
-			return true;
+	public function get_items() {
+		if ( 'notices' == $this->args['params']['box'] ) {
+			$threads = array(
+				'threads' => BP_Messages_Notice::get_notices( array(
+					'pag_num'  => $this->per_page,
+					'pag_page' => $this->page
+				) ),
+				'total' => BP_Messages_Notice::get_total_notice_count(),
+			);
+
+		} else {
+			$threads = BP_Messages_Thread::get_current_threads_for_user(
+				$this->args['params']['user_id'],
+				$this->args['params']['box'],
+				$this->args['params']['type'],
+				$this->per_page,
+				$this->page,
+				$this->args['params']['search_terms']
+			);
 		}
 
-		return false;
+		return $threads;
 	}
 
 	/**
-	 * Set up the next member and iterate index.
+	 * Set Threads pagination.
+	 *
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return object The next member to iterate over.
+	 * @access public
 	 */
-	public function next_thread() {
-		$this->current_thread++;
-		$this->thread = $this->threads[$this->current_thread];
+	public function set_pag_links() {
+		$pag_args = array(
+			$this->page_arg => '%#%'
+		);
 
-		return $this->thread;
-	}
+		if ( defined( 'DOING_AJAX' ) && true === (bool) DOING_AJAX ) {
+			$base = remove_query_arg( 's', wp_get_referer() );
+		} else {
+			$base = '';
+		}
 
-	/**
-	 * Rewind the threads and reset thread index.
-	 */
-	public function rewind_threads() {
-		$this->current_thread = -1;
-		if ( $this->thread_count > 0 ) {
-			$this->thread = $this->threads[0];
+		/**
+		 * Defaults to an empty array to make sure paginate_links()
+		 * won't add the $page_arg to the links which would break
+		 * pagination in case javascript is disabled.
+		 */
+		$add_args = array();
+
+		if ( ! empty( $this->search_terms ) ) {
+			$add_args['s'] = urlencode( $this->search_terms );
 		}
+
+		$this->pag_links = paginate_links( array(
+			'base'      => add_query_arg( $pag_args, $base ),
+			'format'    => '',
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
+			'prev_text' => _x( '&larr;', 'Message pagination previous text', 'buddypress' ),
+			'next_text' => _x( '&rarr;', 'Message pagination next text', 'buddypress' ),
+			'mid_size'  => 1,
+			'add_args'  => $add_args,
+		) );
 	}
 
 	/**
-	 * Whether there are threads left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_message_threads()} as part of the
-	 * while loop that controls iteration inside the threads loop, eg:
-	 *     while ( bp_message_threads() ) { ...
+	 * Override parent method as we need to do custom work.
 	 *
-	 * @see bp_message_threads()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are more threads to show, otherwise false.
+	 * @uses  BP_Messages_Box_Template->the_message_thread()
 	 */
-	function message_threads() {
-		if ( $this->current_thread + 1 < $this->thread_count ) {
-			return true;
-		} elseif ( $this->current_thread + 1 == $this->thread_count ) {
-
-			/**
-			 * Fires when at the end of threads to iterate over.
-			 *
-			 * @since BuddyPress (1.5.0)
-			 */
-			do_action( 'messages_box_loop_end' );
-			// Do some cleaning up after the loop
-			$this->rewind_threads();
-		}
-
-		$this->in_the_loop = false;
-		return false;
+	public function the_item() {
+		$this->the_message_thread();
 	}
 
 	/**
@@ -281,14 +171,14 @@ class BP_Messages_Box_Template {
 	public function the_message_thread() {
 
 		$this->in_the_loop = true;
-		$this->thread      = $this->next_thread();
+		$this->thread      = $this->next_item();
 
 		if ( ! bp_is_current_action( 'notices' ) ) {
 			$last_message_index     = count( $this->thread->messages ) - 1;
 			$this->thread->messages = array_reverse( (array) $this->thread->messages );
 
 			// Set up the last message data
-			if ( count($this->thread->messages) > 1 ) {
+			if ( count( $this->thread->messages ) > 1 ) {
 				if ( 'inbox' == $this->box ) {
 					foreach ( (array) $this->thread->messages as $key => $message ) {
 						if ( bp_loggedin_user_id() != $message->sender_id ) {
@@ -401,7 +291,7 @@ function bp_has_message_threads( $args = '' ) {
 	 * @param BP_Messages_Box_Template $messages_template Current message box template object.
 	 * @param array                    $r                 Array of parsed arguments passed into function.
 	 */
-	return apply_filters( 'bp_has_message_threads', $messages_template->has_threads(), $messages_template, $r );
+	return apply_filters( 'bp_has_message_threads', $messages_template->has_items(), $messages_template, $r );
 }
 
 /**
@@ -411,7 +301,7 @@ function bp_has_message_threads( $args = '' ) {
  */
 function bp_message_threads() {
 	global $messages_template;
-	return $messages_template->message_threads();
+	return $messages_template->items();
 }
 
 /**
@@ -421,7 +311,7 @@ function bp_message_threads() {
  */
 function bp_message_thread() {
 	global $messages_template;
-	return $messages_template->the_message_thread();
+	return $messages_template->the_item();
 }
 
 /**
@@ -1055,9 +945,9 @@ function bp_messages_pagination() {
 function bp_messages_pagination_count() {
 	global $messages_template;
 
-	$start_num = intval( ( $messages_template->pag_page - 1 ) * $messages_template->pag_num ) + 1;
+	$start_num = intval( ( $messages_template->page - 1 ) * $messages_template->per_page ) + 1;
 	$from_num  = bp_core_number_format( $start_num );
-	$to_num    = bp_core_number_format( ( $start_num + ( $messages_template->pag_num - 1 ) > $messages_template->total_thread_count ) ? $messages_template->total_thread_count : $start_num + ( $messages_template->pag_num - 1 ) );
+	$to_num    = bp_core_number_format( ( $start_num + ( $messages_template->per_page - 1 ) > $messages_template->total_thread_count ) ? $messages_template->total_thread_count : $start_num + ( $messages_template->per_page - 1 ) );
 	$total     = bp_core_number_format( $messages_template->total_thread_count );
 
 	echo sprintf( _n( 'Viewing 1 message', 'Viewing %1$s - %2$s of %3$s messages', $total, 'buddypress' ), $from_num, $to_num, number_format_i18n( $total ) );
@@ -1685,191 +1575,70 @@ function bp_message_get_recipient_usernames() {
 		return apply_filters( 'bp_get_message_get_recipient_usernames', $recipients );
 	}
 
-
 /**
  * Message Thread Template Class
  */
-class BP_Messages_Thread_Template {
+class BP_Messages_Thread_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Messages loop.
 	 *
-	 * @access public
-	 * @var int
-	 */
-	public $current_message = -1;
-
-	/**
-	 * Number of messages returned by the paged query.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $message_count = 0;
-
-	/**
-	 * The message object currently being iterated on.
-	 *
-	 * @access public
-	 * @var object
-	 */
-	public $message;
-
-	/**
-	 * Thread that the current messages belong to.
-	 *
-	 * @access public
-	 * @var BP_Messages_Thread
-	 */
-	public $thread;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @access public
-	 * @var bool
+	 * @access protected
+	 * @var array
 	 */
-	public $in_the_loop = false;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'message',
+			'item_plural'       => 'messages',
+			'item_total_count'  => 'total_message_count',
+			'item_count'        => 'message_count',
+			'current_item'      => 'current_message',
+			'hook_prefix'       => 'thread',
+		),
+		'params' => array(
+			'order' => 'ASC',
+		)
+	);
 
 	/**
-	 * The page number being requested.
+	 * Constructor method.
 	 *
-	 * @access public
-	 * @var int
+	 * @see BP_Messages_Thread::populate() for full parameter info
 	 */
-	public $pag_page = 1;
+	public function __construct( $thread_id = 0, $order = 'ASC', $args = array() ) {
+		$params = array( 'params' => array(
+			'thread_id'   => $thread_id,
+			'order'       => $order,
+			'thread_args' => $args,
+		) );
 
-	/**
-	 * The number of items being requested per page.
-	 *
-	 * @access public
-	 * @var int
-	 */
-	public $pag_num = 10;
+		$this->set_loop( $params );
+	}
 
 	/**
-	 * An HTML string containing pagination links.
+	 * Get Messages.
 	 *
-	 * @access public
-	 * @var string
-	 */
-	public $pag_links = '';
-
-	/**
-	 * The total number of messages matching the query.
+	 * @since BuddyPress (2.3.0)
 	 *
 	 * @access public
-	 * @var int
-	 */
-	public $total_message_count = 0;
-
-	/**
-	 * Constructor method.
-	 *
-	 * @see BP_Messages_Thread::populate() for full parameter info
 	 */
-	public function __construct( $thread_id = 0, $order = 'ASC', $args = array() ) {
-		$this->thread        = new BP_Messages_Thread( $thread_id, $order, $args );
-		$this->message_count = count( $this->thread->messages );
+	public function get_items() {
+		$this->thread = new BP_Messages_Thread(
+			$this->args['params']['thread_id'],
+			$this->args['params']['order'],
+			$this->args['params']['thread_args']
+		);
 
-		$last_message_index                 = $this->message_count - 1;
+		$last_message_index                 = count( $this->thread->messages ) - 1;
 		$this->thread->last_message_id      = $this->thread->messages[ $last_message_index ]->id;
 		$this->thread->last_message_date    = $this->thread->messages[ $last_message_index ]->date_sent;
 		$this->thread->last_sender_id       = $this->thread->messages[ $last_message_index ]->sender_id;
 		$this->thread->last_message_subject = $this->thread->messages[ $last_message_index ]->subject;
 		$this->thread->last_message_content = $this->thread->messages[ $last_message_index ]->message;
-	}
-
-	/**
-	 * Whether there are messages available in the loop.
-	 *
-	 * @see bp_thread_has_messages()
-	 *
-	 * @return bool True if there are items in the loop, otherwise false.
-	 */
-	public function has_messages() {
-		if ( ! empty( $this->message_count ) ) {
-			return true;
-		}
-
-		return false;
-	}
-
-	/**
-	 * Set up the next member and iterate index.
-	 *
-	 * @return object The next member to iterate over.
-	 */
-	public function next_message() {
-		$this->current_message++;
-		$this->message = $this->thread->messages[ $this->current_message ];
-
-		return $this->message;
-	}
-
-	/**
-	 * Rewind the messages and reset message index.
-	 */
-	public function rewind_messages() {
-		$this->current_message = -1;
-		if ( $this->message_count > 0 ) {
-			$this->message = $this->thread->messages[0];
-		}
-	}
-
-	/**
-	 * Whether there are messages left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_thread_messages()} as part of the
-	 * while loop that controls iteration inside the messages loop, eg:
-	 *     while ( bp_thread_messages() ) { ...
-	 *
-	 * @see bp_thread_messages()
-	 *
-	 * @return bool True if there are more messages to show, otherwise false.
-	 */
-	public function messages() {
-		if ( ( $this->current_message + 1 ) < $this->message_count ) {
-			return true;
-		} elseif ( ( $this->current_message + 1 ) === $this->message_count ) {
-
-			/**
-			 * Fires when at the end of messages to iterate over.
-			 *
-			 * @since BuddyPress (1.1.0)
-			 */
-			do_action( 'thread_loop_end' );
-			// Do some cleaning up after the loop
-			$this->rewind_messages();
-		}
-
-		$this->in_the_loop = false;
-		return false;
-	}
 
-	/**
-	 * Set up the current message inside the loop.
-	 *
-	 * Used by {@link bp_thread_the_message()} to set up the current
-	 * message data while looping, so that template tags used during
-	 * that iteration make reference to the current message.
-	 *
-	 * @see bp_thread_the_message()
-	 */
-	public function the_message() {
-		$this->in_the_loop = true;
-		$this->message     = $this->next_message();
-
-		// loop has just started
-		if ( 0 === $this->current_message ) {
-
-			/**
-			 * Fires if at the start of the message loop.
-			 *
-			 * @since BuddyPress (1.1.0)
-			 */
-			do_action( 'thread_loop_start' );
-		}
+		return array( 'messages' => $this->thread->messages, 'total' => 0 );
 	}
 }
 
@@ -1906,7 +1675,7 @@ function bp_thread_has_messages( $args = '' ) {
 
 	$thread_template = new BP_Messages_Thread_Template( $r['thread_id'], $r['order'], $extra_args );
 
-	return $thread_template->has_messages();
+	return $thread_template->has_items();
 }
 
 /**
@@ -1933,7 +1702,7 @@ function bp_thread_messages_order() {
 function bp_thread_messages() {
 	global $thread_template;
 
-	return $thread_template->messages();
+	return $thread_template->items();
 }
 
 /**
@@ -1944,7 +1713,7 @@ function bp_thread_messages() {
 function bp_thread_the_message() {
 	global $thread_template;
 
-	return $thread_template->the_message();
+	return $thread_template->the_item();
 }
 
 /**
diff --git src/bp-notifications/bp-notifications-template.php src/bp-notifications/bp-notifications-template.php
index 8ff04ac..f2e76ac 100644
--- src/bp-notifications/bp-notifications-template.php
+++ src/bp-notifications/bp-notifications-template.php
@@ -98,124 +98,32 @@ function bp_notifications_read_permalink() {
  *
  * @since BuddyPress (1.9.0)
  */
-class BP_Notifications_Template {
+class BP_Notifications_Template extends BP_Template_Loop {
 
 	/**
-	 * The loop iterator.
+	 * Default arguments for Notifications loop.
 	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $current_notification = -1;
-
-	/**
-	 * The number of notifications returned by the paged query.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $current_notification_count;
-
-	/**
-	 * Total number of notifications matching the query.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $total_notification_count;
-
-	/**
-	 * Array of notifications located by the query.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
+	 * @access protected
 	 * @var array
 	 */
-	public $notifications;
-
-	/**
-	 * The notification object currently being iterated on.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var object
-	 */
-	public $notification;
-
-	/**
-	 * A flag for whether the loop is currently being iterated.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var bool
-	 */
-	public $in_the_loop;
-
-	/**
-	 * The ID of the user to whom the displayed notifications belong.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $user_id;
-
-	/**
-	 * The page number being requested.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $pag_page;
-
-	/**
-	 * The number of items to display per page of results.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var int
-	 */
-	public $pag_num;
-
-	/**
-	 * An HTML string containing pagination links.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var string
-	 */
-	public $pag_links;
-
-	/**
-	 * A string to match against.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var string
-	 */
-	public $search_terms;
-
-	/**
-	 * A database column to order the results by.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var string
-	 */
-	public $order_by;
-
-	/**
-	 * The direction to sort the results (ASC or DESC)
-	 *
-	 * @since BuddyPress (1.9.0)
-	 * @access public
-	 * @var string
-	 */
-	public $sort_order;
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'notification',
+			'item_plural'       => 'notifications',
+			'item_total_count'  => 'total_notification_count',
+			'item_count'        => 'notification_count',
+			'current_item'      => 'current_notification',
+			'hook_prefix'       => 'notifications',
+		),
+		'params' => array(
+			'page_arg'          => 'npage',
+			'page'              => 1,
+			'per_page'          => 20,
+			'max'               => 0,
+		)
+	);
 
 	/**
 	 * Constructor method.
@@ -248,20 +156,6 @@ class BP_Notifications_Template {
 			'page_arg'          => 'npage',
 		) );
 
-		// Overrides
-
-		// Set which pagination page
-		if ( isset( $_GET[ $r['page_arg'] ] ) ) {
-			$r['page'] = intval( $_GET[ $r['page_arg'] ] );
-		}
-
-		// Set the number to show per page
-		if ( isset( $_GET['num'] ) ) {
-			$r['per_page'] = intval( $_GET['num'] );
-		} else {
-			$r['per_page'] = intval( $r['per_page'] );
-		}
-
 		// Sort order direction
 		$orders = array( 'ASC', 'DESC' );
 		if ( ! empty( $_GET['sort_order'] ) && in_array( $_GET['sort_order'], $orders ) ) {
@@ -270,153 +164,49 @@ class BP_Notifications_Template {
 			$r['sort_order'] = in_array( $r['sort_order'], $orders ) ? $r['sort_order'] : 'DESC';
 		}
 
-		// Setup variables
-		$this->pag_page     = $r['page'];
-		$this->pag_num      = $r['per_page'];
-		$this->user_id      = $r['user_id'];
-		$this->is_new       = $r['is_new'];
-		$this->search_terms = $r['search_terms'];
-		$this->page_arg     = $r['page_arg'];
-		$this->order_by     = $r['order_by'];
-		$this->sort_order   = $r['sort_order'];
-
-		// Setup the notifications to loop through
-		$this->notifications            = BP_Notifications_Notification::get( $r );
-		$this->total_notification_count = BP_Notifications_Notification::get_total_count( $r );
-
-		if ( empty( $this->notifications ) ) {
-			$this->notification_count       = 0;
-			$this->total_notification_count = 0;
-
-		} else {
-			if ( ! empty( $r['max'] ) ) {
-				if ( $r['max'] >= count( $this->notifications ) ) {
-					$this->notification_count = count( $this->notifications );
-				} else {
-					$this->notification_count = (int) $r['max'];
-				}
-			} else {
-				$this->notification_count = count( $this->notifications );
-			}
-		}
-
-		if ( (int) $this->total_notification_count && (int) $this->pag_num ) {
-			$add_args = array(
-				'sort_order' => $this->sort_order,
-			);
-
-			$this->pag_links = paginate_links( array(
-				'base'      => add_query_arg( $this->page_arg, '%#%' ),
-				'format'    => '',
-				'total'     => ceil( (int) $this->total_notification_count / (int) $this->pag_num ),
-				'current'   => $this->pag_page,
-				'prev_text' => _x( '&larr;', 'Notifications pagination previous text', 'buddypress' ),
-				'next_text' => _x( '&rarr;', 'Notifications pagination next text',     'buddypress' ),
-				'mid_size'  => 1,
-				'add_args'  => $add_args,
-			) );
-
-			// Remove first page from pagination
-			$this->pag_links = str_replace( '?'      . $r['page_arg'] . '=1', '', $this->pag_links );
-			$this->pag_links = str_replace( '&#038;' . $r['page_arg'] . '=1', '', $this->pag_links );
-		}
-	}
-
-	/**
-	 * Whether there are notifications available in the loop.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 *
-	 * @see bp_has_notifications()
-	 *
-	 * @return bool True if there are items in the loop, otherwise false.
-	 */
-	public function has_notifications() {
-		if ( $this->notification_count ) {
-			return true;
-		}
-
-		return false;
+		$this->set_loop( array( 'params' => $r ) );
 	}
 
 	/**
-	 * Set up the next notification and iterate index.
-	 *
-	 * @since BuddyPress (1.9.0)
+	 * Get Notifications.
 	 *
-	 * @return object The next notification to iterate over.
-	 */
-	public function next_notification() {
-
-		$this->current_notification++;
-
-		$this->notification = $this->notifications[ $this->current_notification ];
-
-		return $this->notification;
-	}
-
-	/**
-	 * Rewind the blogs and reset blog index.
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @since BuddyPress (1.9.0)
+	 * @access public
 	 */
-	public function rewind_notifications() {
-
-		$this->current_notification = -1;
-
-		if ( $this->notification_count > 0 ) {
-			$this->notification = $this->notifications[0];
-		}
+	public function get_items() {
+		return array(
+			'notifications' => BP_Notifications_Notification::get( $this->args['params'] ),
+			'total'         => BP_Notifications_Notification::get_total_count( $this->args['params'] )
+		);
 	}
 
 	/**
-	 * Whether there are notifications left in the loop to iterate over.
-	 *
-	 * This method is used by {@link bp_notifications()} as part of the
-	 * while loop that controls iteration inside the notifications loop, eg:
-	 *     while ( bp_notifications() ) { ...
-	 *
-	 * @since BuddyPress (1.9.0)
+	 * Set Notifications pagination.
 	 *
-	 * @see bp_notifications()
+	 * @since BuddyPress (2.3.0)
 	 *
-	 * @return bool True if there are more notifications to show,
-	 *         otherwise false.
+	 * @access public
 	 */
-	public function notifications() {
-
-		if ( $this->current_notification + 1 < $this->notification_count ) {
-			return true;
-
-		} elseif ( $this->current_notification + 1 == $this->notification_count ) {
-			do_action( 'notifications_loop_end');
-
-			$this->rewind_notifications();
-		}
-
-		$this->in_the_loop = false;
-		return false;
-	}
+	public function set_pag_links() {
+		$add_args = array(
+			'sort_order' => $this->sort_order,
+		);
 
-	/**
-	 * Set up the current notification inside the loop.
-	 *
-	 * Used by {@link bp_the_notification()} to set up the current
-	 * notification data while looping, so that template tags used during
-	 * that iteration make reference to the current notification.
-	 *
-	 * @since BuddyPress (1.9.0)
-	 *
-	 * @see bp_the_notification()
-	 */
-	public function the_notification() {
-		$this->in_the_loop  = true;
-		$this->notification = $this->next_notification();
+		$this->pag_links = paginate_links( array(
+			'base'      => add_query_arg( $this->page_arg, '%#%' ),
+			'format'    => '',
+			'total'     => ceil( (int) $this->{$this->args['labels']['item_total_count']} / (int) $this->per_page ),
+			'current'   => (int) $this->page,
+			'prev_text' => _x( '&larr;', 'Notifications pagination previous text', 'buddypress' ),
+			'next_text' => _x( '&rarr;', 'Notifications pagination next text',     'buddypress' ),
+			'mid_size'  => 1,
+			'add_args'  => $add_args,
+		) );
 
-		// loop has just started
-		if ( 0 === $this->current_notification ) {
-			do_action( 'notifications_loop_start' );
-		}
+		// Remove first page from pagination
+		$this->pag_links = str_replace( '?'      . $this->page_arg . '=1', '', $this->pag_links );
+		$this->pag_links = str_replace( '&#038;' . $this->page_arg . '=1', '', $this->pag_links );
 	}
 }
 
@@ -489,7 +279,7 @@ function bp_has_notifications( $args = '' ) {
 	// Setup the global query loop
 	buddypress()->notifications->query_loop = $query_loop;
 
-	return apply_filters( 'bp_has_notifications', $query_loop->has_notifications(), $query_loop );
+	return apply_filters( 'bp_has_notifications', $query_loop->has_items(), $query_loop );
 }
 
 /**
@@ -500,7 +290,7 @@ function bp_has_notifications( $args = '' ) {
  * @return array List of notifications.
  */
 function bp_the_notifications() {
-	return buddypress()->notifications->query_loop->notifications();
+	return buddypress()->notifications->query_loop->items();
 }
 
 /**
@@ -511,7 +301,7 @@ function bp_the_notifications() {
  * @return object The current notification within the loop.
  */
 function bp_the_notification() {
-	return buddypress()->notifications->query_loop->the_notification();
+	return buddypress()->notifications->query_loop->the_item();
 }
 
 /** Loop Output ***************************************************************/
@@ -991,9 +781,9 @@ function bp_notifications_pagination_count() {
 	 */
 	function bp_get_notifications_pagination_count() {
 		$query_loop = buddypress()->notifications->query_loop;
-		$start_num  = intval( ( $query_loop->pag_page - 1 ) * $query_loop->pag_num ) + 1;
+		$start_num  = intval( ( $query_loop->page - 1 ) * $query_loop->per_page ) + 1;
 		$from_num   = bp_core_number_format( $start_num );
-		$to_num     = bp_core_number_format( ( $start_num + ( $query_loop->pag_num - 1 ) > $query_loop->total_notification_count ) ? $query_loop->total_notification_count : $start_num + ( $query_loop->pag_num - 1 ) );
+		$to_num     = bp_core_number_format( ( $start_num + ( $query_loop->per_page - 1 ) > $query_loop->total_notification_count ) ? $query_loop->total_notification_count : $start_num + ( $query_loop->per_page - 1 ) );
 		$total      = bp_core_number_format( $query_loop->total_notification_count );
 		$pag        = sprintf( _n( 'Viewing 1 notification', 'Viewing %1$s - %2$s of %3$s notifications', $total, 'buddypress' ), $from_num, $to_num, $total );
 
diff --git src/bp-templates/bp-legacy/buddypress/members/register.php src/bp-templates/bp-legacy/buddypress/members/register.php
index e0f8f5b..8c84606 100644
--- src/bp-templates/bp-legacy/buddypress/members/register.php
+++ src/bp-templates/bp-legacy/buddypress/members/register.php
@@ -65,44 +65,48 @@
 					<?php /* Use the profile field loop to render input fields for the 'base' profile field group */ ?>
 					<?php if ( bp_is_active( 'xprofile' ) ) : if ( bp_has_profile( array( 'profile_group_id' => 1, 'fetch_field_data' => false ) ) ) : while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
 
-					<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
+						<?php if ( bp_profile_group_has_fields() ) : ?>
 
-						<div<?php bp_field_css_class( 'editfield' ); ?>>
+							<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
 
-							<?php
-							$field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
-							$field_type->edit_field_html();
+								<div<?php bp_field_css_class( 'editfield' ); ?>>
 
-							do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
+									<?php
+									$field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
+									$field_type->edit_field_html();
 
-							if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
-								<p class="field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
-									<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _ex( 'Change', 'Change profile field visibility level', 'buddypress' ); ?></a>
-								</p>
+									do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
 
-								<div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
-									<fieldset>
-										<legend><?php _e( 'Who can see this field?', 'buddypress' ) ?></legend>
+									if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
+										<p class="field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
+											<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _ex( 'Change', 'Change profile field visibility level', 'buddypress' ); ?></a>
+										</p>
 
-										<?php bp_profile_visibility_radio_buttons() ?>
+										<div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
+											<fieldset>
+												<legend><?php _e( 'Who can see this field?', 'buddypress' ) ?></legend>
 
-									</fieldset>
-									<a class="field-visibility-settings-close" href="#"><?php _e( 'Close', 'buddypress' ) ?></a>
+												<?php bp_profile_visibility_radio_buttons() ?>
 
-								</div>
-							<?php else : ?>
-								<p class="field-visibility-settings-notoggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
-									<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?>
-								</p>
-							<?php endif ?>
+											</fieldset>
+											<a class="field-visibility-settings-close" href="#"><?php _e( 'Close', 'buddypress' ) ?></a>
+
+										</div>
+									<?php else : ?>
+										<p class="field-visibility-settings-notoggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
+											<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?>
+										</p>
+									<?php endif ?>
 
-							<?php do_action( 'bp_custom_profile_edit_fields' ); ?>
+									<?php do_action( 'bp_custom_profile_edit_fields' ); ?>
 
-							<p class="description"><?php bp_the_profile_field_description(); ?></p>
+									<p class="description"><?php bp_the_profile_field_description(); ?></p>
+
+								</div>
 
-						</div>
+							<?php endwhile; ?>
 
-					<?php endwhile; ?>
+						<?php endif ;?>
 
 					<input type="hidden" name="signup_profile_field_ids" id="signup_profile_field_ids" value="<?php bp_the_profile_field_ids(); ?>" />
 
diff --git src/bp-templates/bp-legacy/buddypress/members/single/profile/edit.php src/bp-templates/bp-legacy/buddypress/members/single/profile/edit.php
index 7fe800f..ae2b58c 100644
--- src/bp-templates/bp-legacy/buddypress/members/single/profile/edit.php
+++ src/bp-templates/bp-legacy/buddypress/members/single/profile/edit.php
@@ -19,43 +19,47 @@ if ( bp_has_profile( 'profile_group_id=' . bp_get_current_profile_group_id() ) )
 
 		<div class="clear"></div>
 
-		<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
+		<?php if ( bp_profile_group_has_fields() ) : ?>
 
-			<div<?php bp_field_css_class( 'editfield' ); ?>>
+			<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
 
-				<?php
-				$field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
-				$field_type->edit_field_html();
+				<div<?php bp_field_css_class( 'editfield' ); ?>>
 
-				do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
-				?>
+					<?php
+					$field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
+					$field_type->edit_field_html();
 
-				<?php if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
-					<p class="field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
-						<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _e( 'Change', 'buddypress' ); ?></a>
-					</p>
+					do_action( 'bp_custom_profile_edit_fields_pre_visibility' );
+					?>
 
-					<div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
-						<fieldset>
-							<legend><?php _e( 'Who can see this field?', 'buddypress' ) ?></legend>
+					<?php if ( bp_current_user_can( 'bp_xprofile_change_field_visibility' ) ) : ?>
+						<p class="field-visibility-settings-toggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
+							<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?> <a href="#" class="visibility-toggle-link"><?php _e( 'Change', 'buddypress' ); ?></a>
+						</p>
 
-							<?php bp_profile_visibility_radio_buttons() ?>
+						<div class="field-visibility-settings" id="field-visibility-settings-<?php bp_the_profile_field_id() ?>">
+							<fieldset>
+								<legend><?php _e( 'Who can see this field?', 'buddypress' ) ?></legend>
 
-						</fieldset>
-						<a class="field-visibility-settings-close" href="#"><?php _e( 'Close', 'buddypress' ) ?></a>
-					</div>
-				<?php else : ?>
-					<div class="field-visibility-settings-notoggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
-						<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?>
-					</div>
-				<?php endif ?>
+								<?php bp_profile_visibility_radio_buttons() ?>
 
-				<?php do_action( 'bp_custom_profile_edit_fields' ); ?>
+							</fieldset>
+							<a class="field-visibility-settings-close" href="#"><?php _e( 'Close', 'buddypress' ) ?></a>
+						</div>
+					<?php else : ?>
+						<div class="field-visibility-settings-notoggle" id="field-visibility-settings-toggle-<?php bp_the_profile_field_id() ?>">
+							<?php printf( __( 'This field can be seen by: <span class="current-visibility-level">%s</span>', 'buddypress' ), bp_get_the_profile_field_visibility_level_label() ) ?>
+						</div>
+					<?php endif ?>
 
-				<p class="description"><?php bp_the_profile_field_description(); ?></p>
-			</div>
+					<?php do_action( 'bp_custom_profile_edit_fields' ); ?>
 
-		<?php endwhile; ?>
+					<p class="description"><?php bp_the_profile_field_description(); ?></p>
+				</div>
+
+			<?php endwhile; ?>
+
+		<?php endif ;?>
 
 	<?php do_action( 'bp_after_profile_field_content' ); ?>
 
diff --git src/bp-templates/bp-legacy/buddypress/members/single/settings/profile.php src/bp-templates/bp-legacy/buddypress/members/single/settings/profile.php
index 0fdae6f..cd4d040 100644
--- src/bp-templates/bp-legacy/buddypress/members/single/settings/profile.php
+++ src/bp-templates/bp-legacy/buddypress/members/single/settings/profile.php
@@ -6,7 +6,7 @@
 
 		<?php while ( bp_profile_groups() ) : bp_the_profile_group(); ?>
 
-			<?php if ( bp_profile_fields() ) : ?>
+			<?php if ( bp_profile_group_has_fields() ) : ?>
 
 				<table class="profile-settings" id="xprofile-settings-<?php bp_the_profile_group_slug(); ?>">
 					<thead>
diff --git src/bp-xprofile/bp-xprofile-admin.php src/bp-xprofile/bp-xprofile-admin.php
index 24d36e1..c9fb625 100644
--- src/bp-xprofile/bp-xprofile-admin.php
+++ src/bp-xprofile/bp-xprofile-admin.php
@@ -756,6 +756,8 @@ class BP_XProfile_User_Admin {
 
 			<?php endif; ?>
 
+			<?php if ( ! bp_profile_group_has_fields() ) { continue ; }  ?>
+
 			<?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?>
 
 				<div<?php bp_field_css_class( 'bp-profile-field' ); ?>>
diff --git src/bp-xprofile/bp-xprofile-template.php src/bp-xprofile/bp-xprofile-template.php
index 5c9e53f..7381f57 100644
--- src/bp-xprofile/bp-xprofile-template.php
+++ src/bp-xprofile/bp-xprofile-template.php
@@ -10,46 +10,108 @@
 // Exit if accessed directly
 if ( !defined( 'ABSPATH' ) ) exit;
 
-class BP_XProfile_Data_Template {
-	var $current_group = -1;
-	var $group_count;
-	var $groups;
-	var $group;
+/**
+ * The main xProfile Groups template loop class.
+ *
+ * Responsible for loading a group of fields into a loop for display.
+ */
+class BP_XProfile_Data_Template extends BP_Template_Loop {
+
+	/**
+	 * Default arguments for xProfile Groups loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'group',
+			'item_plural'       => 'groups',
+			'item_total_count'  => 'total_group_count',
+			'item_count'        => 'group_count',
+			'current_item'      => 'current_group',
+			'hook_prefix'       => 'xprofile_template',
+		),
+		'params' => array()
+	);
 
-	var $current_field = -1;
-	var $field_count;
-	var $field_has_data;
-	var $field;
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 * @var int
+	 */
+	public $current_field = -1;
 
-	var $in_the_loop;
-	var $user_id;
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 * @var object
+	 */
+	public $field;
 
+	/**
+	 * Constructor method.
+	 *
+	 * @see BP_XProfile_Group::get() for an in-depth description of arguments.
+	 *
+	 * @param int $user_id
+	 * @param int $profile_group_id
+	 * @param bool $hide_empty_groups
+	 * @param bool $fetch_fields
+	 * @param bool $fetch_field_data
+	 * @param bool|string $exclude_groups Comma-separated list of profile field group IDs to exclude
+	 * @param bool|string $exclude_fields Comma-separated list of profile field IDs to exclude
+	 * @param bool $hide_empty_fields
+	 * @param bool $fetch_visibility_level
+	 * @param bool $update_meta_cache
+	 */
 	function __construct( $user_id, $profile_group_id, $hide_empty_groups = false, $fetch_fields = false, $fetch_field_data = false, $exclude_groups = false, $exclude_fields = false, $hide_empty_fields = false, $fetch_visibility_level = false, $update_meta_cache = true ) {
-		$this->groups = bp_xprofile_get_groups( array(
-			'profile_group_id'    => $profile_group_id,
-			'user_id'             => $user_id,
-			'hide_empty_groups'   => $hide_empty_groups,
-			'hide_empty_fields'   => $hide_empty_fields,
-			'fetch_fields'        => $fetch_fields,
-			'fetch_field_data'    => $fetch_field_data,
+		$params = array( 'params' => array(
+			'profile_group_id'       => $profile_group_id,
+			'user_id'                => $user_id,
+			'hide_empty_groups'      => $hide_empty_groups,
+			'hide_empty_fields'      => $hide_empty_fields,
+			'fetch_fields'           => $fetch_fields,
+			'fetch_field_data'       => $fetch_field_data,
 			'fetch_visibility_level' => $fetch_visibility_level,
-			'exclude_groups'      => $exclude_groups,
-			'exclude_fields'      => $exclude_fields,
-			'update_meta_cache'   => $update_meta_cache,
+			'exclude_groups'         => $exclude_groups,
+			'exclude_fields'         => $exclude_fields,
+			'update_meta_cache'      => $update_meta_cache,
 		) );
 
-		$this->group_count = count($this->groups);
-		$this->user_id = $user_id;
+		$this->set_loop( $params );
 	}
 
-	function has_groups() {
-		if ( $this->group_count )
-			return true;
+	/**
+	 * Get xProfile Groups.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
+		$groups = bp_xprofile_get_groups( $this->args['params'] );
 
-		return false;
+		return array( 'groups' => $groups, 'total' => count( $groups ) );
 	}
 
-	function next_group() {
+	/**
+	 * Alias of BP_XProfile_Data_Template->next_item().
+	 */
+	public function next_group() {
+		$this->next_item();
+	}
+
+	/**
+	 * Override parent method as we need to do custom work.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 */
+	function next_item() {
 		$this->current_group++;
 
 		$this->group       = $this->groups[$this->current_group];
@@ -63,70 +125,67 @@ class BP_XProfile_Data_Template {
 		return $this->group;
 	}
 
-	function rewind_groups() {
-		$this->current_group = -1;
-		if ( $this->group_count > 0 ) {
-			$this->group = $this->groups[0];
-		}
-	}
-
-	function profile_groups() {
-		if ( $this->current_group + 1 < $this->group_count ) {
-			return true;
-		} elseif ( $this->current_group + 1 == $this->group_count ) {
-			do_action('xprofile_template_loop_end');
-			// Do some cleaning up after the loop
-			$this->rewind_groups();
-		}
-
-		$this->in_the_loop = false;
-		return false;
-	}
-
-	function the_profile_group() {
+	/**
+	 * Override parent method as we need to do custom work.
+	 * Set up the current xProfile Group inside the loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @global $group
+	 */
+	public function the_item() {
 		global $group;
 
-		$this->in_the_loop = true;
-		$group = $this->next_group();
+		parent::the_item();
 
-		if ( 0 == $this->current_group ) // loop has just started
-			do_action('xprofile_template_loop_start');
+		if ( ! empty( $this->group ) ) {
+			$group = $this->group;
+		}
 	}
 
-	/**** FIELDS ****/
+	/**** FIELDS : backcompat methods *********************************************/
 
-	function next_field() {
+	/**
+	 * These methods are used to ensure back compatibility for themes using
+	 * bp_profile_fields() without first initiating the loop thanks to bp_profile_group_has_fields()
+	 */
+
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 */
+	public function next_field() {
 		$this->current_field++;
 
 		$this->field = $this->group->fields[$this->current_field];
 		return $this->field;
 	}
 
-	function rewind_fields() {
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 */
+	public function rewind_fields() {
 		$this->current_field = -1;
 		if ( $this->field_count > 0 ) {
 			$this->field = $this->group->fields[0];
 		}
 	}
 
-	function has_fields() {
-		$has_data = false;
-
-		for ( $i = 0, $count = count( $this->group->fields ); $i < $count; ++$i ) {
-			$field = &$this->group->fields[$i];
-
-			if ( !empty( $field->data ) && $field->data->value != null ) {
-				$has_data = true;
-			}
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 */
+	public function profile_fields() {
+		if ( empty( $this->doing_it_wrong ) ) {
+			_doing_it_wrong( __FUNCTION__, __( 'bp_profile_group_has_fields() should always be used before bp_profile_fields(), please update your templates.', 'buddypress' ), '2.3.0' );
 		}
+		// Display the doing it wrong message once only
+		$this->doing_it_wrong = true;
 
-		if ( $has_data )
-			return true;
-
-		return false;
-	}
-
-	function profile_fields() {
 		if ( $this->current_field + 1 < $this->field_count ) {
 			return true;
 		} elseif ( $this->current_field + 1 == $this->field_count ) {
@@ -137,23 +196,15 @@ class BP_XProfile_Data_Template {
 		return false;
 	}
 
-	function the_profile_field() {
+	/**
+	 * @deprecated BuddyPress (2.3.0).
+	 *
+	 * @access public
+	 */
+	public function the_profile_field() {
 		global $field;
 
 		$field = $this->next_field();
-
-		// Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731
-		if ( ! empty( $field->data ) && ( ! empty( $field->data->value ) || '0' == $field->data->value ) ) {
-			$value = maybe_unserialize( $field->data->value );
-		} else {
-			$value = false;
-		}
-
-		if ( ! empty( $value ) || '0' == $value ) {
-			$this->field_has_data = true;
-		} else {
-			$this->field_has_data = false;
-		}
 	}
 }
 
@@ -185,25 +236,146 @@ function bp_has_profile( $args = '' ) {
 	);
 
 	$r = bp_parse_args( $args, $defaults, 'has_profile' );
-	extract( $r, EXTR_SKIP );
 
-	$profile_template = new BP_XProfile_Data_Template( $user_id, $profile_group_id, $hide_empty_groups, $fetch_fields, $fetch_field_data, $exclude_groups, $exclude_fields, $hide_empty_fields, $fetch_visibility_level, $update_meta_cache );
-	return apply_filters( 'bp_has_profile', $profile_template->has_groups(), $profile_template );
+	$profile_template = new BP_XProfile_Data_Template(
+		$r['user_id'],
+		$r['profile_group_id'],
+		$r['hide_empty_groups'],
+		$r['fetch_fields'],
+		$r['fetch_field_data'],
+		$r['exclude_groups'],
+		$r['exclude_fields'],
+		$r['hide_empty_fields'],
+		$r['fetch_visibility_level'],
+		$r['update_meta_cache']
+	);
+
+	return apply_filters( 'bp_has_profile', $profile_template->has_items(), $profile_template );
 }
 
 function bp_profile_groups() {
 	global $profile_template;
-	return $profile_template->profile_groups();
+	return $profile_template->items();
 }
 
 function bp_the_profile_group() {
 	global $profile_template;
-	return $profile_template->the_profile_group();
+	return $profile_template->the_item();
 }
 
-function bp_profile_group_has_fields() {
+/**
+ * xProfile Fields template loop class.
+ *
+ * Responsible for loading fields into a loop for display.
+ *
+ * @since BuddyPress (2.3.0)
+ */
+class BP_XProfile_Fields_Template extends BP_Template_Loop {
+
+	/**
+	 * Default arguments for xProfile Fields loop.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access protected
+	 * @var array
+	 */
+	protected $default_args = array(
+		'labels' => array(
+			'item'              => 'field',
+			'item_plural'       => 'fields',
+			'item_total_count'  => 'total_field_count',
+			'item_count'        => 'field_count',
+			'current_item'      => 'current_field',
+			'hook_prefix'       => 'xprofile_field',
+	) );
+
+	/**
+	 * Constructor method.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @param array $fields a list of all field objects
+	 * @param int $group_id the profile group id to loop the fields on.
+	 * @uses wp_list_filter()
+	 */
+	public function __construct( $fields = null, $group_id = 0 ) {
+		if ( empty( $fields ) || empty( $group_id ) ) {
+			return false;
+		}
+
+		$fields = wp_list_filter( $fields, array( 'group_id' => $group_id ) );
+
+		$this->set_loop( array( 'fields' => $fields ) );
+	}
+
+	/**
+	 * Get xProfile Fields.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @access public
+	 */
+	public function get_items() {
+		return array(
+			'fields' => $this->args['fields'],
+			'total'  => count( $this->args['fields'] ),
+		);
+	}
+
+	/**
+	 * Override parent method as we need to do custom work.
+	 *
+	 * @since BuddyPress (2.3.0)
+	 *
+	 * @global $field
+	 */
+	public function the_item() {
+		global $field;
+
+		parent::the_item();
+
+		if ( empty( $this->field ) ) {
+			return;
+		}
+
+		$field = $this->field;
+	}
+}
+
+/**
+ * Initialize the Fields loop for a given xProfile Group
+ *
+ * @global $profile_template
+ * @param array $fields a list of all field objects
+ * @param int $group_id the profile group id to loop the fields on.
+ * @uses  BP_XProfile_Fields_Template
+ * @return bool true if fields were found, false otherwise
+ */
+function bp_profile_group_has_fields( $fields = array(), $group_id = 0 ) {
 	global $profile_template;
-	return $profile_template->has_fields();
+
+	if ( empty( $fields ) ) {
+		if ( ! empty( $profile_template->group->fields ) ) {
+			$fields =& $profile_template->group->fields;
+
+		// We really need the group fields to init the loop
+		} else {
+			return false;
+		}
+	}
+
+	if ( empty( $group_id ) && ! empty( $profile_template->group->id ) ) {
+		$group_id =& $profile_template->group->id;
+	}
+
+	if ( empty( $profile_template ) ) {
+		$profile_template = new stdClass();
+	}
+
+	$profile_template->group_fields = new BP_XProfile_Fields_Template( $fields, $group_id );
+
+	return apply_filters( 'bp_profile_group_has_fields', $profile_template->group_fields->has_items(), $profile_template->group_fields );
 }
 
 function bp_field_css_class( $class = false ) {
@@ -214,17 +386,27 @@ function bp_field_css_class( $class = false ) {
 
 		$css_classes = array();
 
+		/**
+		 * Backcompat check in case bp_profile_group_has_fields()
+		 * wasn't used before bp_profile_fields()
+		 */
+		if ( empty( $profile_template->group_fields ) ) {
+			$group_field = $profile_template;
+		} else {
+			$group_field = $profile_template->group_fields;
+		}
+
 		if ( $class )
 			$css_classes[] = sanitize_title( esc_attr( $class ) );
 
 		// Set a class with the field ID
-		$css_classes[] = 'field_' . $profile_template->field->id;
+		$css_classes[] = 'field_' . $group_field->field->id;
 
 		// Set a class with the field name (sanitized)
-		$css_classes[] = 'field_' . sanitize_title( $profile_template->field->name );
+		$css_classes[] = 'field_' . sanitize_title( $group_field->field->name );
 
 		// Set a class indicating whether the field is required or optional
-		if ( ! empty( $profile_template->field->is_required ) ) {
+		if ( ! empty( $group_field->field->is_required ) ) {
 			$css_classes[] = 'required-field';
 		} else {
 			$css_classes[] = 'optional-field';
@@ -233,10 +415,11 @@ function bp_field_css_class( $class = false ) {
 		// Add the field visibility level
 		$css_classes[] = 'visibility-' . esc_attr( bp_get_the_profile_field_visibility_level() );
 
-		if ( $profile_template->current_field % 2 == 1 )
+		if ( $group_field->current_field % 2 == 1 ) {
 			$css_classes[] = 'alt';
+		}
 
-		$css_classes[] = 'field_type_' . sanitize_title( $profile_template->field->type );
+		$css_classes[] = 'field_type_' . sanitize_title( $group_field->field->type );
 		$css_classes = apply_filters_ref_array( 'bp_field_css_classes', array( &$css_classes ) );
 
 		return apply_filters( 'bp_get_field_css_class', ' class="' . implode( ' ', $css_classes ) . '"' );
@@ -346,12 +529,45 @@ function bp_the_profile_field_ids() {
 
 function bp_profile_fields() {
 	global $profile_template;
-	return $profile_template->profile_fields();
+
+	/**
+	 * Backcompat check in case bp_profile_group_has_fields()
+	 * wasn't used before bp_profile_fields()
+	 */
+	if ( empty( $profile_template->group_fields ) ) {
+		return $profile_template->profile_fields();
+	}
+
+	return $profile_template->group_fields->items();
 }
 
 function bp_the_profile_field() {
 	global $profile_template;
-	return $profile_template->the_profile_field();
+
+	/**
+	 * Backcompat check in case bp_profile_group_has_fields()
+	 * wasn't used before bp_profile_fields()
+	 */
+	if ( empty( $profile_template->group_fields ) ) {
+		$field = $profile_template->the_profile_field();
+	} else {
+		$field = $profile_template->group_fields->the_item();
+	}
+
+	// Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731
+	if ( ! empty( $field->data ) && ( ! empty( $field->data->value ) || '0' == $field->data->value ) ) {
+		$value = maybe_unserialize( $field->data->value );
+	} else {
+		$value = false;
+	}
+
+	if ( ! empty( $value ) || '0' == $value ) {
+		$profile_template->field_has_data = true;
+	} else {
+		$profile_template->field_has_data = false;
+	}
+
+	return $field;
 }
 
 function bp_the_profile_field_id() {
