Allow HTML in excerpt

  • Here is my excerpt code.

    // Generate custom excerpt length
    function wpbx_excerpt_length($length) {
        return 300;
    add_filter('excerpt_length', 'wpbx_excerpt_length');

    How do I allow html like <a> <b> <i> <br>


    I've recently answered a few questions regarding excerpts, so I'm going to give a detailed explanation covering as much as I can.


    There seems to be a couple of questions arising from this answer on where the code should go, and the answer is, it is really up to you and how you see fit. There are are a couple of options where you can place the code (if not explicitly stated):

    • In your theme's functions.php or any file use as a functions file. Just remember when you do this, if the theme is not your own, all changes will be lost when you upgrade your theme

    • A better way would be to use the code in a child theme. As above, in the functions.php or functions related file

    • Use the code in a plugin. This is the prefered way as this makes the code available across all themes. If you switch themes, you don't have to worry about rewriting the same code.

    I hope this clears things up a bit :-)


    the_excerpt() first of all doesn't accept any parameters, so nothing can be passed to it. It is a fact that the_excerpt() trims the content to 55 words, and all HTML tags are stripped before returning the text. the_excerpt() is located in wp-includes/post-template.php. To allow certain or all HTML tags in the excerpt, a new excerpt has to be created.

    First of all, the original function needs to be removed first, and then the new function needs to be hooked to get_the_excerpt. Please take note, this new excerpt will still be callable as the_excerpt() in template files, no need to change that. get_the_excerpt() is located in wp-includes/post-template.php.

    The excerpt uses wp_trim_excerpt to return the trimmed text, so we need to remove wp_trim_excerpt first from the excerpt filter. wp_trim_excerpt() is located in wp-includes/formatting.php, line 2355. This is how:

    remove_filter('get_the_excerpt', 'wp_trim_excerpt');

    You can now add your new excerpt to get_the_excerpt

    add_filter('get_the_excerpt', 'wpse_custom_wp_trim_excerpt');

    To allow html tags/formatting, we will need to specify which tags you will need to allow. You can use the following strip_tags statement to achieve that

    $wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags());

    The second argument wpse_allowedtags() is a small function that is used to add the tags the_excerpt() will allow. For a complete list of valid HTML 5 tags, go and check it out here. Here is function, add any html tags to this that you need to allow/keep

    function wpse_allowedtags() {
    // Add custom tags to this string
        return '<script>,<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>'; 

    If you need to allow all HTML tags, that is, no stripping of any tags, the strips_tags() function can be omitted/removed completely.

    A point to note however, when html tags are allowed, these tags are counted as words, so your word count for excerpts with tags and without tags will not be the same. To correct that, you will need to remove these tags from the actual word count first so that only words are counted.

    I have written an excerpt that will allow all tags, count only words as words, and complete a sentence after the set amount of words (so the text won't be trimmed mid-sentence) and add a read more text after the last word.

    Here is the complete code

    function wpse_allowedtags() {
        // Add custom tags to this string
            return '<script>,<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>'; 
    if ( ! function_exists( 'wpse_custom_wp_trim_excerpt' ) ) : 
        function wpse_custom_wp_trim_excerpt($wpse_excerpt) {
        $raw_excerpt = $wpse_excerpt;
            if ( '' == $wpse_excerpt ) {
                $wpse_excerpt = get_the_content('');
                $wpse_excerpt = strip_shortcodes( $wpse_excerpt );
                $wpse_excerpt = apply_filters('the_content', $wpse_excerpt);
                $wpse_excerpt = str_replace(']]>', ']]&gt;', $wpse_excerpt);
                $wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags()); /*IF you need to allow just certain tags. Delete if all tags are allowed */
                //Set the excerpt word count and only break after sentence is complete.
                    $excerpt_word_count = 75;
                    $excerpt_length = apply_filters('excerpt_length', $excerpt_word_count); 
                    $tokens = array();
                    $excerptOutput = '';
                    $count = 0;
                    // Divide the string into tokens; HTML tags, or words, followed by any whitespace
                    preg_match_all('/(<[^>]+>|[^<>\s]+)\s*/u', $wpse_excerpt, $tokens);
                    foreach ($tokens[0] as $token) { 
                        if ($count >= $excerpt_length && preg_match('/[\,\;\?\.\!]\s*$/uS', $token)) { 
                        // Limit reached, continue until , ; ? . or ! occur at the end
                            $excerptOutput .= trim($token);
                        // Add words to complete sentence
                        // Append what's left of the token
                        $excerptOutput .= $token;
                $wpse_excerpt = trim(force_balance_tags($excerptOutput));
                    $excerpt_end = ' <a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&raquo;&nbsp;' . sprintf(__( 'Read more about: %s &nbsp;&raquo;', 'wpse' ), get_the_title()) . '</a>'; 
                    $excerpt_more = apply_filters('excerpt_more', ' ' . $excerpt_end); 
                    //$pos = strrpos($wpse_excerpt, '</');
                    //if ($pos !== false)
                    // Inside last HTML tag
                    //$wpse_excerpt = substr_replace($wpse_excerpt, $excerpt_end, $pos, 0); /* Add read more next to last word */
                    // After the content
                    $wpse_excerpt .= $excerpt_more; /*Add read more in new paragraph */
                return $wpse_excerpt;   
            return apply_filters('wpse_custom_wp_trim_excerpt', $wpse_excerpt, $raw_excerpt);
    remove_filter('get_the_excerpt', 'wp_trim_excerpt');
    add_filter('get_the_excerpt', 'wpse_custom_wp_trim_excerpt'); 

    You can just remove the '//' from functions that you need extra.


    Sometimes you need to display simple excerpts of different lengths and it is not viable to write an excerpt for every post/function/page. Here is a nice small little function using wp_trim_words

    function wpse_custom_excerpts($limit) {
        return wp_trim_words(get_the_excerpt(), $limit, '<a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&hellip;' . __( 'Read more &nbsp;&raquo;', 'wpse' ) . '</a>');

    What this little function does is taking get_the_excerpt, trimming it to $limit set by the user, and returning the text with a read more link at the end.

    You can call this excerpt as follow in your template

    echo wpse_custom_excerpts($limit);

    where $limit will be your word count, so an excerpt of 30 words will be

    echo wpse_custom_excerpts(30);

    Just one thing to remember here, if you set your limit to more that 55 words, only 55 words will be returned as the excerpt is only 55 words in length. If longer excerpts are needed, use get_the_content instead.


    If you just need to alter the length of the_excerpt(), you can use the following function

    function wpse_excerpt_length( $length ) {
        return 20;
    add_filter( 'excerpt_length', 'wpse_excerpt_length', 999 );

    Remember, you will need to set a priority bigger than 10 so that your custom function executes after the default.


    All text returned by the excerpt have the hated [...] at the end that is not clickable. To add a read more text in the place of the hellips, use this function

     function wpse_excerpt_more( $more ) {
        return ' <a class="read-more" href="'. get_permalink( get_the_ID() ) . '">' . __('Read More', 'your-text-domain') . '</a>';
    add_filter( 'excerpt_more', 'wpse_excerpt_more' );


    Excerpt first paragraph

    I want to keep this complete, so here is the excerpt that trims after the first paragraph.

    Here is a function that keeps HTML tags in tact, adds a "Read More" link at the end of the excerpt and trims the excerpt after the first paragraph.

    if ( ! function_exists( 'wpse0001_custom_wp_trim_excerpt' ) ) : 
        function wpse0001_custom_wp_trim_excerpt($wpse0001_excerpt) {
            global $post;
            $raw_excerpt = $wpse0001_excerpt;
            if ( '' == $wpse0001_excerpt ) {
                $wpse0001_excerpt = get_the_content('');
                $wpse0001_excerpt = strip_shortcodes( $wpse0001_excerpt );
                $wpse0001_excerpt = apply_filters('the_content', $wpse0001_excerpt);
                $wpse0001_excerpt = substr( $wpse0001_excerpt, 0, strpos( $wpse0001_excerpt, '</p>' ) + 4 );
                $wpse0001_excerpt = str_replace(']]>', ']]&gt;', $wpse0001_excerpt);
                $excerpt_end = ' <a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&raquo;&nbsp;' . sprintf(__( 'Read more about: %s &nbsp;&raquo;', 'pietergoosen' ), get_the_title()) . '</a>'; 
                $excerpt_more = apply_filters('excerpt_more', ' ' . $excerpt_end); 
                //$pos = strrpos($wpse0001_excerpt, '</');
                //if ($pos !== false)
                // Inside last HTML tag
                //$wpse0001_excerpt = substr_replace($wpse0001_excerpt, $excerpt_end, $pos, 0);
                // After the content
                $wpse0001_excerpt .= $excerpt_more;
                return $wpse0001_excerpt;
            return apply_filters('wpse0001_custom_wp_trim_excerpt', $wpse0001_excerpt, $raw_excerpt);
    remove_filter('get_the_excerpt', 'wp_trim_excerpt');
    add_filter('get_the_excerpt', 'wpse0001_custom_wp_trim_excerpt');

    EDIT 29-10-2015

    For anyone that need a workaround to not display the read more link after the excerpt when the excerpt is shorter that the amount of words set, please see the following question and answer

    where exactly do i place this part `function wpse_allowedtags() { // Add custom tags to this string return ',<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>'; }` im confused

    All this code goes in `functions.php`. You can add that just above `if ( ! function_exists( 'wpse_custom_wp_trim_excerpt' ) ) :` in your `functions.php`

    @user32447 see edit

    i know that but there are certain tags I want allowed and I don't know how to achieve that, i placed the code in my functions.php but what do i change to allow certain tags @pieter goosen

    am i suppose to erase this line and replace it `$wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags());`

    nvm i missed the top of the code

    No, keep `$wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags());`. You must add your tags to these `return ',<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>';` You can also delete all this tags and just add yours

    if this goes into functions.php file won't that get overwritten when an update comes along ?

    @mcgrailm yes it would. That is why it is important to add this to your **child themes'** functions.php. You can even add it to a must-use plugin

    Perhaps also add ":" as a token?

    @PieterGoosen Wow, this is a great explanation! There is just one thing: the function is going to add words until the next . or ! (e.g.), but what if there is no more words after the final . or !? The "Read more" link is still going to show... I know the excerpt function is not supposed to show the full content but that can be hard to control if one editor write a small article. What do you think of this situation?

    @Pipo You are correct. I will look at this issue after the weekend, I'm going to be a bit busy. Will keep you up to date. Thanks for your reply

    @PieterGoosen No worries, there is no rush, I am just trying to learn from your code. Have a good sunday

    Does this make sure that no tags are left open? I'm asking because of this question.

    There is an issue with ul an li tags that I am aware of, but generally, this should just actually be stripped from an excerpt. @ialocin

    Nice! So if I have lets say 100 words and there is a opening tag without a closing one it will be stripped.

    @ialocin no it won't. You can implement something like this to strip out tags that are left open. What I meant is, one should actually strip tags like ul and li tags from the excerpt. In one of the functions above you can do it.

    Too bad, I misunderstood you then - obviously. This was more about for example the `` tag.

    I think this need testing :-). When I constructed this, it was more meant for a tags, which at that stage was my main issue. I only later noticed that ul and li tags got stripped halfway through, so you would sit with the opening tag and not the closing one. To be honest, this code does have it flaws :-)

    @PieterGoosen Just mentioning that hooking the excerpt_more function won't work unless you give it a lower priority since template-tags.php is being loaded after the current theme's functions.php

    @PieterGoosen thank you for the highly informative post- quick question: would it be possible to modify the excerpt in this way for only a specific post type?

    Can you just write a quick solution that I can copy and paste in my code? It's summer and I don't want to read a book.

License under CC-BY-SA with attribution

Content dated before 6/26/2020 9:53 AM