14+ years building on WordPress / Replies in under 5 hours
WordPress 9 min read · Updated July 2026

How to Get the Title in WordPress Template Page

Photo of Ajay Khandal
Ajay Khandal
WordPress Developer
TL;DR

WordPress has two title functions: get_the_title() returns a string (you must echo it), and the_title() echoes directly with optional $before/$after wrapper markup. Inside the loop, either works — use the_title('<h2>', '</h2>') for tidy heading output. Outside the loop or for a specific post, always pass an ID to get_the_title($post_id). When outputting a title inside an HTML attribute, use esc_attr(get_the_title()) — never unescaped. For archive/taxonomy page names, use get_the_archive_title() instead. Always call wp_reset_postdata() after a custom WP_Query loop, or subsequent title calls return the wrong post.

Getting the current post or page title in a WordPress template is one of the first things you’ll need when writing theme PHP. WordPress provides two functions for this: get_the_title(), which returns the title as a string, and the_title(), which echoes it directly. They look almost identical, but the distinction between returning and echoing matters — using the wrong one is a common source of blank output or unexpected double-printing.

This guide covers both functions, when to use each, how to get titles outside the loop, archive page titles, and how to escape correctly when using a title in an HTML attribute. If you’re deciding between writing custom templates and using a page builder, see page builders vs. custom code in WordPress — the functions here are only relevant when you’re working in PHP templates.

get_the_title()

get_the_title( int|WP_Post $post = 0 )

Returns the post title as a string. The $post parameter is optional — omit it to get the current post’s title (inside the loop), or pass an integer post ID or a WP_Post object to get any post’s title from anywhere.

Because it returns a value rather than printing it, you control how it’s used:

// Echo it directly
echo get_the_title();

// Store it in a variable
$title = get_the_title();

// Use it in an attribute — always escape when going into HTML attributes
<img src="..." alt="<?php echo esc_attr( get_the_title() ); ?>">

// Use it in a link title
<a href="<?php the_permalink(); ?>" title="<?php echo esc_attr( get_the_title() ); ?>">
    Read more
</a>

Return value: get_the_title() returns the post title with protected characters decoded (e.g. &amp;&) but without any HTML tags — post titles are stored as plain text in the database. If the post is password-protected, WordPress prepends “Protected: ” to the title; for private posts, it prepends “Private: “.

Getting a specific post’s title by ID

// Inside or outside the loop — pass any post ID
$page_title = get_the_title( 42 );
echo $page_title;

// Or pass a WP_Post object
$post_obj = get_post( 42 );
echo get_the_title( $post_obj );

This is the function to use when you need a title outside the main loop — for example, pulling the parent page’s title in a hierarchical template, or displaying a “related posts” heading next to another post’s thumbnail.

the_title()

the_title( string $before = '', string $after = '', bool $echo = true )

Displays or retrieves the current post title with optional wrapper markup. Inside the loop, it uses the current post — like get_the_title() with no arguments, but echoed rather than returned.

$before (string, optional)
Markup to prepend to the title. Default: '' (empty string).
$after (string, optional)
Markup to append to the title. Default: '' (empty string).
$echo (bool, optional)
When true (the default), echoes the output. Pass false to return the string instead.

Examples

// Simple echo — most common form inside a template loop
the_title();

// Wrap in heading tags
the_title( '<h1 class="entry-title">', '</h1>' );

// Wrap with a permalink
the_title(
    '<h2><a href="' . get_permalink() . '">',
    '</a></h2>'
);

// Return instead of echo (useful for string concatenation)
$title_string = the_title( '', '', false );

The $before/$after approach is common in loop templates where every post needs a heading with the same markup structure. It keeps the template tidy compared to manually opening and closing a heading tag around every echo get_the_title() call.

Which Function to Use When

  • Inside the loop, echoing directly: Either works. the_title() is slightly more idiomatic for just printing the title; echo get_the_title() is equally correct.
  • Inside the loop, wrapping in heading tags: the_title( '<h2>', '</h2>' ) is the cleaner option.
  • Inside an HTML attribute: Always esc_attr( get_the_title() )the_title() should not be used in attributes since it echoes without attribute escaping context.
  • Outside the loop, with a specific post ID: get_the_title( $post_id )the_title() does not accept a post ID argument.
  • Storing the title in a variable: get_the_title()the_title() with false also works but is less readable.

Template Examples

single.php — single post template

if ( have_posts() ) :
    while ( have_posts() ) :
        the_post();
        ?>
        <article id="post-<?php the_ID(); ?>">
            <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
            <div class="entry-content">
                <?php the_content(); ?>
            </div>
        </article>
        <?php
    endwhile;
endif;

archive.php — post loop

if ( have_posts() ) :
    while ( have_posts() ) :
        the_post();
        ?>
        <article>
            <?php
            the_title(
                '<h2><a href="' . get_permalink() . '">',
                '</a></h2>'
            );
            ?>
            <?php the_excerpt(); ?>
        </article>
        <?php
    endwhile;
endif;

Custom WP_Query loop — getting titles outside the main loop

$recent_posts = new WP_Query([
    'post_type'      => 'post',
    'posts_per_page' => 3,
    'orderby'        => 'date',
    'order'          => 'DESC',
]);

if ( $recent_posts->have_posts() ) :
    echo '<ul>';
    while ( $recent_posts->have_posts() ) :
        $recent_posts->the_post();
        echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
    endwhile;
    echo '</ul>';
    wp_reset_postdata(); // always reset after a custom query
endif;

Getting a title by ID — outside any loop

// In a widget, sidebar template, or functions.php callback:
$parent_id  = wp_get_post_parent_id( get_the_ID() );
$parent_title = $parent_id ? get_the_title( $parent_id ) : '';

echo '<p>Section: <a href="' . get_permalink( $parent_id ) . '">'
    . esc_html( $parent_title )
    . '</a></p>';

Archive and Taxonomy Page Titles

On category, tag, custom taxonomy, author, and date archive pages, the_title() and get_the_title() return the title of the current post in the loop — not the archive page name. To get the archive page’s own descriptive title, use get_the_archive_title():

// In archive.php, above the loop:
echo '<h1>' . get_the_archive_title() . '</h1>';
// Output examples: "Category: News", "Tag: WordPress", "Author: Ajay Khandal"

// Strip the "Category: " prefix if you want just the name:
echo single_cat_title( '', false );  // category archives
echo single_tag_title( '', false );  // tag archives
echo single_term_title( '', false ); // any taxonomy term

For page templates on static front pages or custom pages, the_title() and get_the_title() work exactly as they do in single.php — they return the page’s title, not the blog name or site tagline.

The HTML <title> Tag Is Different

The post title functions covered here output the post’s editorial title — the same text that appears in the page’s <h1> heading. The HTML <title> element (what appears in browser tabs and search engine results) is separate, controlled by wp_get_document_title() and typically managed by an SEO plugin like Rank Math or Yoast.

Do not use the_title() or get_the_title() in the <head><title> block — WordPress’s wp_head() hook handles that, and an SEO plugin should be setting the exact title tag content. The two are independent.

Escaping Title Output

Post titles are stored as plain text in WordPress, but they can contain characters that need escaping depending on where you use the output:

// Outputting in page body — escape as HTML
echo esc_html( get_the_title() );

// Inside an HTML attribute (alt, title, aria-label)
echo esc_attr( get_the_title() );

// Inside a URL (query string value)
echo esc_url( add_query_arg( 'title', get_the_title(), $base_url ) );

// the_title() applies esc_html internally when $echo is true
// so plain the_title() in body copy is safe
the_title();

The safe pattern: use the_title() when printing in body copy, and esc_attr( get_the_title() ) whenever the title goes into an HTML attribute. The title can legitimately contain characters like ", <, or & — a post titled get_the_title() & Related Functions will break your markup if you don’t escape it in an attribute context.

The the_title Filter

Both the_title() and get_the_title() pass through the the_title filter before returning. This lets you modify title output sitewide without editing template files:

// Add a "(Draft)" suffix to draft posts in the admin or on the frontend
add_filter( 'the_title', function( $title, $id ) {
    if ( get_post_status( $id ) === 'draft' ) {
        $title .= ' (Draft)';
    }
    return $title;
}, 10, 2 );

// Strip emoji from all titles
add_filter( 'the_title', function( $title ) {
    return preg_replace( '/[\x{1F600}-\x{1F64F}]/u', '', $title );
} );

The filter receives the title string and the post ID. Return the modified string — don’t echo inside the filter callback.

Title functions are typically used alongside other template tag functions in the same file. The complete picture of common template tags:

  • get_the_ID() / the_ID() — current post ID
  • get_permalink() / the_permalink() — post URL
  • get_the_excerpt() / the_excerpt() — post excerpt
  • get_the_date() / the_date() — published date
  • get_the_author() / the_author() — post author display name
  • the_content() — full post body (no get_ equivalent — always echoes)

For displaying images in templates, see how to use wp_get_attachment_image() — it’s the paired function to these title/permalink tags, covering featured images, ACF image fields, and srcset handling in custom loops.

For enqueueing the scripts and styles that your custom templates depend on, see the right way to enqueue scripts and styles in WordPress — using wp_enqueue_script() correctly is the template setup step that comes alongside writing the PHP itself.

If you need to output post content through non-standard means — rendering a post’s content as part of another template — WordPress shortcodes are the bridge between template PHP and content-embedded functionality.

Common Mistakes

  • Using the_title() outside the loop without a post ID. the_title() doesn’t accept a post ID — it uses the global $post. Outside the loop, $post is whatever was last set, which may not be the post you want. Use get_the_title( $post_id ) explicitly.
  • Not echoing get_the_title(). get_the_title() returns a string — it doesn’t print anything. A common template bug is calling get_the_title() on its own line and seeing nothing, because the return value was discarded.
  • Skipping esc_attr() in HTML attributes. A title like “The <b>Basics</b>” or “Breadcrumbs & Navigation” inside an unescaped attribute will break your HTML. Always escape: esc_attr( get_the_title() ).
  • Forgetting wp_reset_postdata() after a custom WP_Query. After a custom query loop, the global $post is set to the last post in that query. Any get_the_title() or the_title() call after the custom loop — before wp_reset_postdata() — returns the wrong post’s title.
  • Confusing the post title with the <title> tag. get_the_title() and the_title() return the post’s editorial title (the h1). The browser tab/SEO title in <head> is set by wp_get_document_title() and modified by SEO plugins — they’re separate.

Frequently asked questions

get_the_title() returns the post title as a string — you must echo it to display it. the_title() echoes the title directly by default, with optional $before and $after wrapper markup as its first two parameters. get_the_title() also accepts a post ID or WP_Post object, so it works outside the loop. the_title() uses the current global $post and does not accept a post ID. For most in-loop title output, both work — use the_title() for heading wrappers and get_the_title() when you need the title in a variable, in an HTML attribute, or for a post other than the current one.

Use get_the_title( $post_id ) with the integer post ID as the argument. For example: $title = get_the_title( 42 ); echo esc_html( $title ); This works from anywhere in your theme — functions.php, a widget template, a sidebar, or a custom shortcode — without needing to be inside a have_posts() loop. You can also pass a WP_Post object: get_the_title( get_post( 42 ) ). the_title() cannot be used this way — it always uses the current global $post.

Use get_the_archive_title() for the descriptive title of category, tag, author, custom taxonomy, and date archive pages. It returns a string like 'Category: News' or 'Tag: WordPress'. To get just the term name without the 'Category: ' prefix, use single_cat_title('', false) for categories, single_tag_title('', false) for tags, or single_term_title('', false) for any taxonomy term. get_the_title() and the_title() return the title of the current post in the loop on archive pages — not the archive's own descriptive name.

Only with esc_attr() applied: echo esc_attr( get_the_title() ). Post titles can contain characters like double quotes, ampersands, or angle brackets that would break an unescaped HTML attribute. A title like 'Breadcrumbs & Navigation' unescaped in an alt or title attribute will produce invalid HTML. esc_attr() converts those characters to their safe HTML entity equivalents (&, ", etc.) so the attribute parses correctly. When outputting in the page body (not in an attribute), use esc_html( get_the_title() ) or just the_title(), which applies esc_html internally.

The most common cause is not calling wp_reset_postdata() after a custom WP_Query loop. After a custom query runs, the global $post object is set to the last post in that query. Any get_the_title() call without an explicit post ID after that loop will return the last post's title from the custom query, not the main page's post. Fix: always call wp_reset_postdata() immediately after the endwhile in a custom WP_Query loop. If you're outside the main loop intentionally, pass the specific post ID: get_the_title( $post_id ).

Use the 'the_title' filter hook. Both get_the_title() and the_title() pass through this filter before returning, so any function hooked to it modifies all title output. The filter receives two arguments: the title string and the post ID. Example: add_filter('the_title', function($title, $id) { if (is_admin()) return $title; return $title . ' — My Site'; }, 10, 2); Return the modified string from the callback — don't echo inside the filter. Scope your modifications carefully; the filter runs on every title output including nav menus, widget titles, and admin screens, so use conditional checks (is_admin(), is_singular(), etc.) if you only want it in specific contexts.

Photo of Ajay Khandal

Written by Ajay Khandal

I'm a freelance WordPress developer with 14+ years of experience building, fixing, and speeding up sites for businesses, agencies, and store owners across the US, UK, Europe, and Australia. I specialize in custom themes, WooCommerce, and performance — the kind of work that shows up as faster load times and fewer support tickets. No account managers, no outsourced tickets — you work directly with me, with replies typically inside 5 hours.

Work with me →