wp_list_comments()WP 2.7.0

List comments. Used in the comments.php template to list comments for a particular post.

Used in the comments.php template to list comments for a particular post.

Hooks from the function

Return

null|String. Void if 'echo' argument is true, or no comments to list. Otherwise, HTML list of comments.

Usage

wp_list_comments( $args, $comments );
$args(string|array)

Formatting options.

Default: array()

  • walker(object)
    Instance of a Walker class to list comments.
    Default: null

  • max_depth(int)
    The maximum comments depth.
    Default: ''

  • style(string)
    The style of list ordering. Accepts 'ul', 'ol', or 'div'. 'div' will result in no additional list markup.
    Default: 'ul'

  • callback(callable)
    Callback function to use.
    Default: null

  • end-callback(callable)
    Callback function to use at the end.
    Default: null

  • type(string)
    Type of comments to list. Accepts 'all', 'comment', 'pingback', 'trackback', 'pings'.
    Default: 'all'

  • page(int)
    Page ID to list comments for.
    Default: ''

  • per_page(int)
    Number of comments to list per page.
    Default: ''

  • avatar_size(int)
    Height and width dimensions of the avatar size.
    Default: 32

  • reverse_top_level(true|false)
    Ordering of the listed comments. If true, will display newest comments first.
    Default: null

  • reverse_children(true|false)
    Whether to reverse child comments in the list.
    Default: null

  • format(string)
    How to format the comments list. Accepts 'html5', 'xhtml'. if the theme supports it.
    Default: 'html5'

  • short_ping(true|false)
    Whether to output short pings.
    Default: false

  • echo(true|false)
    Whether to echo the output or return it.
    Default: true
$comments(WP_Comment[])
Array of WP_Comment objects.
Default: null

Examples

1

#1 Example of using the walker parameter and creating your own class

An example of a comments.php file:

<?php
if ( post_password_required() ) {
	return;
}

if ( $comments ) {
	?>

	<div class="comments" id="comments">

		<?php
		wp_list_comments( [
			'walker'      => new TwentyTwenty_Walker_Comment(),
			'avatar_size' => 120,
			'style'       => 'div',
		] );
		?>

	</div><!-- comments -->

	<?php
}

Walker Class:

<?php

/**
 * CUSTOM COMMENT WALKER
 * A custom walker for comments, based on the walker in Twenty Nineteen.
 *
 * @since Twenty Twenty 1.0
 */
class TwentyTwenty_Walker_Comment extends Walker_Comment {

	/**
	 * Outputs a comment in the HTML5 format.
	 *
	 * @since Twenty Twenty 1.0
	 *
	 * @see wp_list_comments()
	 * @see https://developer.wordpress.org/reference/functions/get_comment_author_url/
	 * @see https://developer.wordpress.org/reference/functions/get_comment_author/
	 * @see https://developer.wordpress.org/reference/functions/get_avatar/
	 * @see https://developer.wordpress.org/reference/functions/get_comment_reply_link/
	 * @see https://developer.wordpress.org/reference/functions/get_edit_comment_link/
	 *
	 * @param WP_Comment $comment Comment to display.
	 * @param int        $depth   Depth of the current comment.
	 * @param array      $args    An array of arguments.
	 */
	protected function html5_comment( $comment, $depth, $args ) {

		$tag = 'div' === $args['style'] ? 'div' : 'li';

		?>
		<<?= $tag ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
			<article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
				<footer class="comment-meta">
					<div class="comment-author vcard">
						<?php
						$comment_author_url = get_comment_author_url( $comment );
						$comment_author     = get_comment_author( $comment );
						$avatar             = get_avatar( $comment, $args['avatar_size'] );
						if ( 0 !== $args['avatar_size'] ) {
							if ( empty( $comment_author_url ) ) {
								echo wp_kses_post( $avatar );
							} else {
								printf( '<a href="%s" rel="external nofollow" class="url">', $comment_author_url ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped --Escaped in https://developer.wordpress.org/reference/functions/get_comment_author_url/
								echo wp_kses_post( $avatar );
							}
						}

						printf(
							'<span class="fn">%1$s</span><span class="screen-reader-text says">%2$s</span>',
							esc_html( $comment_author ),
							__( 'says:', 'twentytwenty' )
						);

						if ( ! empty( $comment_author_url ) ) {
							echo '</a>';
						}
						?>
					</div><!-- .comment-author -->

					<div class="comment-metadata">
						<?php
						/* translators: 1: Comment date, 2: Comment time. */
						$comment_timestamp = sprintf( __( '%1$s at %2$s', 'twentytwenty' ), get_comment_date( '', $comment ), get_comment_time() );

						printf(
							'<a href="%s"><time datetime="%s" title="%s">%s</time></a>',
							esc_url( get_comment_link( $comment, $args ) ),
							get_comment_time( 'c' ),
							esc_attr( $comment_timestamp ),
							esc_html( $comment_timestamp )
						);

						if ( get_edit_comment_link() ) {
							printf(
								' <span aria-hidden="true">•</span> <a class="comment-edit-link" href="%s">%s</a>',
								esc_url( get_edit_comment_link() ),
								__( 'Edit', 'twentytwenty' )
							);
						}
						?>
					</div><!-- .comment-metadata -->

				</footer><!-- .comment-meta -->

				<div class="comment-content entry-content">

					<?php

					comment_text();

					if ( '0' === $comment->comment_approved ) {
						?>
						<p class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.', 'twentytwenty' ); ?></p>
						<?php
					}

					?>

				</div><!-- .comment-content -->

				<?php

				$comment_reply_link = get_comment_reply_link(
					array_merge(
						$args,
						array(
							'add_below' => 'div-comment',
							'depth'     => $depth,
							'max_depth' => $args['max_depth'],
							'before'    => '<span class="comment-reply">',
							'after'     => '</span>',
						)
					)
				);

				$by_post_author = twentytwenty_is_comment_by_post_author( $comment );

				if ( $comment_reply_link || $by_post_author ) {
					?>

					<footer class="comment-footer-meta">

						<?php
						if ( $comment_reply_link ) {
							echo $comment_reply_link; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Link is escaped in https://developer.wordpress.org/reference/functions/get_comment_reply_link/
						}
						if ( $by_post_author ) {
							echo '<span class="by-post-author">' . __( 'By Post Author', 'twentytwenty' ) . '</span>';
						}
						?>

					</footer>

					<?php
				}
				?>

			</article><!-- .comment-body -->

		<?php
	}
}
0

#2 Comment output using a custom function

To customize the appearance of each comment, you can use the callback parameter in which you specify the name of the function and then create that function with the desired code (output):

<ul class="commentlist">
	<?php 
	wp_list_comments( [ 
		'type'     => 'comment',
		'callback' => 'mytheme_comment',
	] );
	?>
</ul>

We display a list of comments -- type=comment -- i.e. they are not pings, and we also use our function that forms the appearance of the comment -- callback=mytheme_comment. Function mytheme_comment() should be described separately, you can add it in the file functions.php or directly in the same file where this code is located (usually it is comments.php).

<?php
function mytheme_comment( $comment, $args, $depth ) {

	if ( 'div' === $args['style'] ) {
		$tag       = 'div';
		$add_below = 'comment';
	}
	else {
		$tag       = 'li';
		$add_below = 'div-comment';
	}

	$classes = ' ' . comment_class( empty( $args['has_children'] ) ? '' : 'parent', null, null, false );
	?>

	<<?= $tag . $classes; ?> id="comment-<?php comment_ID() ?>">
	<?php if ( 'div' != $args['style'] ) { ?>
		<div id="div-comment-<?php comment_ID() ?>" class="comment-body"><?php
	} ?>

	<div class="comment-author vcard">
		<?php
		if ( $args['avatar_size'] != 0 ) {
			echo get_avatar( $comment, $args['avatar_size'] );
		}
		printf(
			__( '<cite class="fn">%s</cite> <span class="says">says:</span>' ),
			get_comment_author_link()
		);
		?>
	</div>

	<?php if ( $comment->comment_approved == '0' ) { ?>
		<em class="comment-awaiting-moderation">
			<?php _e( 'Your comment is awaiting moderation.' ); ?>
		</em><br/>
	<?php } ?>

	<div class="comment-meta commentmetadata">
		<a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ); ?>">
			<?php
			printf(
				__( '%1$s at %2$s' ),
				get_comment_date(),
				get_comment_time()
			); ?>
		</a>

		<?php edit_comment_link( __( '(Edit)' ), '  ', '' ); ?>
	</div>

	<?php comment_text(); ?>

	<div class="reply">
		<?php
		comment_reply_link(
			array_merge(
				$args,
				array(
					'add_below' => $add_below,
					'depth'     => $depth,
					'max_depth' => $args['max_depth']
				)
			)
		); ?>
	</div>

	<?php if ( 'div' != $args['style'] ) { ?>
		</div>
	<?php }
}

IMPORTANT: the callback function must contain only the opening tag <li> and it must not be closed. See parameter description for details.

0

#3 Default use

Displays a list of comments. It is used in the comments.php template file. The presence of tree view and pagination in comments is controlled through the admin panel Settings > Discussion.

<ol class="commentlist">
	<?php wp_list_comments(); ?>
</ol>

Or customizing using parameters:

$cpage = get_query_var( 'cpage' ) ? get_query_var( 'cpage' ) : 1;

wp_list_comments(
	[
		'avatar_size'       => 60,
		'short_ping'        => true,
		'type'              => 'comment',
		'callback'          => 'ic_comment_list',
		'per_page'          => get_option( 'comments_per_page' ),
		'page'              => $cpage,
		'reverse_top_level' => get_option( 'default_comments_page' ) === 'oldest' ? false : true,
	]
);
0

#4 Display comments for a specific post

If you want to display the usual list of comments, but this list should include only the necessary comments.

Let's say we need to output the comments of a certain post. To do that, we'll first get the comments we want and then pass them to the second parameter of wp_list_comments() to output them in the usual format. The tree view, if set, is preserved.

<ol class="commentlist">
	<?php
	// Retrieve post comments with ID XXX from the database 
	$comments = get_comments( [
		'post_id' => XXX,
		'status' => 'approve' // moderated comments
	] );

	// generate a list of received comments
	wp_list_comments( [
		'per_page'          => 10,   // Pagination of comments - 10 per page
		'reverse_top_level' => false // Show last comments at the beginning
	], $comments );
	?>
</ol>
0

#5 “Comment is awaiting moderation” alert.

If you are using WordPress 4.9.6 or higher and not showing “Comment is awaiting moderation” alert.

You can follow the steps below.

  1. Settings > Discussion > enable “Show comments cookies opt-in checkbox.”
  2. After activating checkbox will show on comments form like this. “Save my name, email, and website in this browser for the next time I comment.”

That’s it.

If you want to customize this checkbox field, you can use this code.

$comment_form = array(

	'fields' => array(
		'cookies' => '<p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"' . $consent . ' />' .
		'<label for="wp-comment-cookies-consent">' . __( 'Save my name, email, and website in this browser for the next time I comment.' ) . '</label></p>',
	),
);

comment_form( $comment_form );

Notes

  • See: WP_Query::$comments
  • Global. WP_Query. $wp_query WordPress Query object.
  • Global. Int. $comment_alt
  • Global. Int. $comment_depth
  • Global. Int. $comment_thread_alt
  • Global. true|false. $overridden_cpage
  • Global. true|false. $in_comment_loop

Changelog

Since 2.7.0 Introduced.

wp_list_comments() code WP 6.4.3

function wp_list_comments( $args = array(), $comments = null ) {
	global $wp_query, $comment_alt, $comment_depth, $comment_thread_alt, $overridden_cpage, $in_comment_loop;

	$in_comment_loop = true;

	$comment_alt        = 0;
	$comment_thread_alt = 0;
	$comment_depth      = 1;

	$defaults = array(
		'walker'            => null,
		'max_depth'         => '',
		'style'             => 'ul',
		'callback'          => null,
		'end-callback'      => null,
		'type'              => 'all',
		'page'              => '',
		'per_page'          => '',
		'avatar_size'       => 32,
		'reverse_top_level' => null,
		'reverse_children'  => '',
		'format'            => current_theme_supports( 'html5', 'comment-list' ) ? 'html5' : 'xhtml',
		'short_ping'        => false,
		'echo'              => true,
	);

	$parsed_args = wp_parse_args( $args, $defaults );

	/**
	 * Filters the arguments used in retrieving the comment list.
	 *
	 * @since 4.0.0
	 *
	 * @see wp_list_comments()
	 *
	 * @param array $parsed_args An array of arguments for displaying comments.
	 */
	$parsed_args = apply_filters( 'wp_list_comments_args', $parsed_args );

	// Figure out what comments we'll be looping through ($_comments).
	if ( null !== $comments ) {
		$comments = (array) $comments;
		if ( empty( $comments ) ) {
			return;
		}
		if ( 'all' !== $parsed_args['type'] ) {
			$comments_by_type = separate_comments( $comments );
			if ( empty( $comments_by_type[ $parsed_args['type'] ] ) ) {
				return;
			}
			$_comments = $comments_by_type[ $parsed_args['type'] ];
		} else {
			$_comments = $comments;
		}
	} else {
		/*
		 * If 'page' or 'per_page' has been passed, and does not match what's in $wp_query,
		 * perform a separate comment query and allow Walker_Comment to paginate.
		 */
		if ( $parsed_args['page'] || $parsed_args['per_page'] ) {
			$current_cpage = get_query_var( 'cpage' );
			if ( ! $current_cpage ) {
				$current_cpage = 'newest' === get_option( 'default_comments_page' ) ? 1 : $wp_query->max_num_comment_pages;
			}

			$current_per_page = get_query_var( 'comments_per_page' );
			if ( $parsed_args['page'] != $current_cpage || $parsed_args['per_page'] != $current_per_page ) {
				$comment_args = array(
					'post_id' => get_the_ID(),
					'orderby' => 'comment_date_gmt',
					'order'   => 'ASC',
					'status'  => 'approve',
				);

				if ( is_user_logged_in() ) {
					$comment_args['include_unapproved'] = array( get_current_user_id() );
				} else {
					$unapproved_email = wp_get_unapproved_comment_author_email();

					if ( $unapproved_email ) {
						$comment_args['include_unapproved'] = array( $unapproved_email );
					}
				}

				$comments = get_comments( $comment_args );

				if ( 'all' !== $parsed_args['type'] ) {
					$comments_by_type = separate_comments( $comments );
					if ( empty( $comments_by_type[ $parsed_args['type'] ] ) ) {
						return;
					}

					$_comments = $comments_by_type[ $parsed_args['type'] ];
				} else {
					$_comments = $comments;
				}
			}

			// Otherwise, fall back on the comments from `$wp_query->comments`.
		} else {
			if ( empty( $wp_query->comments ) ) {
				return;
			}
			if ( 'all' !== $parsed_args['type'] ) {
				if ( empty( $wp_query->comments_by_type ) ) {
					$wp_query->comments_by_type = separate_comments( $wp_query->comments );
				}
				if ( empty( $wp_query->comments_by_type[ $parsed_args['type'] ] ) ) {
					return;
				}
				$_comments = $wp_query->comments_by_type[ $parsed_args['type'] ];
			} else {
				$_comments = $wp_query->comments;
			}

			if ( $wp_query->max_num_comment_pages ) {
				$default_comments_page = get_option( 'default_comments_page' );
				$cpage                 = get_query_var( 'cpage' );
				if ( 'newest' === $default_comments_page ) {
					$parsed_args['cpage'] = $cpage;

					/*
					* When first page shows oldest comments, post permalink is the same as
					* the comment permalink.
					*/
				} elseif ( 1 == $cpage ) {
					$parsed_args['cpage'] = '';
				} else {
					$parsed_args['cpage'] = $cpage;
				}

				$parsed_args['page']     = 0;
				$parsed_args['per_page'] = 0;
			}
		}
	}

	if ( '' === $parsed_args['per_page'] && get_option( 'page_comments' ) ) {
		$parsed_args['per_page'] = get_query_var( 'comments_per_page' );
	}

	if ( empty( $parsed_args['per_page'] ) ) {
		$parsed_args['per_page'] = 0;
		$parsed_args['page']     = 0;
	}

	if ( '' === $parsed_args['max_depth'] ) {
		if ( get_option( 'thread_comments' ) ) {
			$parsed_args['max_depth'] = get_option( 'thread_comments_depth' );
		} else {
			$parsed_args['max_depth'] = -1;
		}
	}

	if ( '' === $parsed_args['page'] ) {
		if ( empty( $overridden_cpage ) ) {
			$parsed_args['page'] = get_query_var( 'cpage' );
		} else {
			$threaded            = ( -1 != $parsed_args['max_depth'] );
			$parsed_args['page'] = ( 'newest' === get_option( 'default_comments_page' ) ) ? get_comment_pages_count( $_comments, $parsed_args['per_page'], $threaded ) : 1;
			set_query_var( 'cpage', $parsed_args['page'] );
		}
	}
	// Validation check.
	$parsed_args['page'] = (int) $parsed_args['page'];
	if ( 0 == $parsed_args['page'] && 0 != $parsed_args['per_page'] ) {
		$parsed_args['page'] = 1;
	}

	if ( null === $parsed_args['reverse_top_level'] ) {
		$parsed_args['reverse_top_level'] = ( 'desc' === get_option( 'comment_order' ) );
	}

	if ( empty( $parsed_args['walker'] ) ) {
		$walker = new Walker_Comment();
	} else {
		$walker = $parsed_args['walker'];
	}

	$output = $walker->paged_walk( $_comments, $parsed_args['max_depth'], $parsed_args['page'], $parsed_args['per_page'], $parsed_args );

	$in_comment_loop = false;

	if ( $parsed_args['echo'] ) {
		echo $output;
	} else {
		return $output;
	}
}