How to Keep Paragraph Breaks in WordPress Query Loop Previews

Query Loop and Latest Post Excerpt blocks turns multi-paragraph content into one flat blob.
Here is the code snippet I used to fix it.

I made an issue in the Gutenberg repository on Github here: https://github.com/WordPress/gutenberg/issues/80380

The problem is that post previews of the text shows up in one blob. So using the Query Loop block or the Latest Post block makes it look like this:

Post excerpt in WordPress no paragraph breaks
Post excerpt in WordPress with no paragraph breaks.

Adding the following code snippet in a code snippet plugin did the work so the preview looks similar to the post.

// Workaround for automatic Query Loop excerpts preserving Gutenberg paragraph breaks.
if ( ! function_exists( 'ftu_excerpt_collect_paragraph_blocks_v2' ) ) {
	function ftu_excerpt_collect_paragraph_blocks_v2( $blocks, &$paragraphs = array() ) {
		foreach ( $blocks as $block ) {
			if ( isset( $block['blockName'] ) && 'core/paragraph' === $block['blockName'] ) {
				$html = trim( $block['innerHTML'] ?? '' );
				$text = trim( wp_strip_all_tags( $html ) );

				if ( $text !== '' ) {
					$paragraphs[] = array(
						'html' => $html,
						'text' => $text,
					);
				}
			}

			if ( ! empty( $block['innerBlocks'] ) ) {
				ftu_excerpt_collect_paragraph_blocks_v2( $block['innerBlocks'], $paragraphs );
			}
		}

		return $paragraphs;
	}
}

if ( ! function_exists( 'ftu_excerpt_trim_paragraph_html_to_words_v2' ) ) {
	function ftu_excerpt_trim_paragraph_html_to_words_v2( $paragraphs, $max_words = 60 ) {
		$output = '';
		$count  = 0;

		foreach ( $paragraphs as $paragraph ) {
			$words = preg_split( '/\s+/', $paragraph['text'], -1, PREG_SPLIT_NO_EMPTY );
			$word_total = count( $words );

			if ( $count + $word_total <= $max_words ) {
				$output .= $paragraph['html'];
				$count  += $word_total;
				continue;
			}

			$remaining = $max_words - $count;

			if ( $remaining > 0 ) {
				$trimmed_words = array_slice( $words, 0, $remaining );
				$trimmed_text  = esc_html( implode( ' ', $trimmed_words ) ) . '&hellip;';
				$output       .= '<p>' . $trimmed_text . '</p>';
			}

			break;
		}

		return $output;
	}
}

add_filter( 'render_block_core/post-excerpt', function( $block_content, $block, $instance ) {
	if ( empty( $instance->context['postId'] ) ) {
		return $block_content;
	}

	$post_id = (int) $instance->context['postId'];
	$post    = get_post( $post_id );

	if ( ! $post instanceof WP_Post ) {
		return $block_content;
	}

	$paragraphs = ftu_excerpt_collect_paragraph_blocks_v2( parse_blocks( $post->post_content ) );

	if ( empty( $paragraphs ) ) {
		return $block_content;
	}

	$max_words = 60;
	if ( isset( $block['attrs']['excerptLength'] ) && is_numeric( $block['attrs']['excerptLength'] ) ) {
		$max_words = (int) $block['attrs']['excerptLength'];
	}

	$excerpt_html = ftu_excerpt_trim_paragraph_html_to_words_v2( $paragraphs, $max_words );

	if ( empty( $excerpt_html ) ) {
		return $block_content;
	}

	$more_text = '';
	if ( ! empty( $block['attrs']['moreText'] ) ) {
		$more_text = sprintf(
			'<a class="wp-block-post-excerpt__more-link" href="%s">%s</a>',
			esc_url( get_permalink( $post_id ) ),
			esc_html( $block['attrs']['moreText'] )
		);
	}

	$show_more_on_new_line = ! isset( $block['attrs']['showMoreOnNewLine'] ) || $block['attrs']['showMoreOnNewLine'];
	$wrapper_attributes     = get_block_wrapper_attributes();

	$content  = '<div ' . $wrapper_attributes . '>';
	$content .= '<div class="wp-block-post-excerpt__excerpt">' . $excerpt_html . '</div>';

	if ( $more_text ) {
		$content .= $show_more_on_new_line
			? '<p class="wp-block-post-excerpt__more-text">' . $more_text . '</p>'
			: $more_text;
	}

	$content .= '</div>';

	return $content;
}, 10, 3 );

The result is this:

Post excerpt in WordPress with paragraph breaks
Post excerpt in WordPress with paragraph breaks.

The code snippet keeps the paragraph breaks in the post excerpts.

Share the article:

Leave a Reply

Your email address will not be published. Required fields are marked *