Inside The Loop, the_title() and the_content() just work. When you only have a post ID, in a widget, a sidebar, a custom template, or an AJAX handler, you have to fetch the data yourself. This DevAnswers guide covers get_post() for the whole object and get_the_title() for the title, and the raw-versus-formatted trap that catches people out with the content.
Get the Post Object by ID
get_post( $id ) returns a WP_Post object holding the post’s stored data, which you read through properties such as $post->post_title and $post->post_content. A numerically valid ID that points to a post which no longer exists returns null, so check the result before you use it:
$post = get_post( 7 );
if ( ! $post ) {
return; // no post with that ID
}
echo esc_html( $post->post_title );
That null check matters in a widget or template where a stale or deleted ID would otherwise throw a PHP warning when you reach for a property. The object carries the rest of the post’s columns too, such as post_date, post_status, post_author, and post_excerpt, so once you have it you rarely need a second query. If you have several IDs to display, fetch them together with get_posts() or WP_Query rather than calling get_post() in a loop.
Outside The Loop there is no current post to fall back on, so always pass the explicit ID you want rather than relying on a default.
Get the Post Title
For the title on its own, the cleanest call is get_the_title( $id ). It accepts the ID directly, so you do not have to fetch the object yourself first, and it returns the title as a string after applying the the_title filter that plugins use to modify titles:
echo esc_html( get_the_title( 7 ) );
If you already have the $post object, $post->post_title gives you the same text raw, without the the_title filter. Because plugins and core can adjust titles through that filter, get_the_title() returns the value your visitors actually see, which is what you want in a listing or a widget. Reach for the raw property only when you need the stored title untouched.
Get the Post Content: Raw vs Formatted
Content is where people get caught. Reading $post->post_content gives you the raw stored content: shortcodes are not expanded and there are no automatic paragraphs, so it will not look like the front end. To render it the way visitors see it, pass it through the the_content filter:
$post = get_post( 7 );
if ( $post ) {
echo apply_filters( 'the_content', $post->post_content );
}
That filter runs WordPress’s default content formatting, most importantly wpautop() for paragraphs and line breaks and do_shortcode() for shortcodes. Use the raw post_content property only when you genuinely want the unprocessed text, for example to run your own parsing; for anything you are displaying, the filtered version is almost always what you want.
The content is stored raw on purpose: the same source can be edited, re-themed, and run through different filters at render time, so the formatting you see on the front end is applied on the way out, not baked into the database. That is why reading the property directly skips all of it.
Mind the escaping difference between the two. The title is plain text, so escape it with esc_html() as in the examples above. The content, whether raw or filtered, is HTML by design, so do not wrap it in esc_html() or you will print the tags as visible text; the the_content filter returns formatted markup ready to print, the same HTML the front end outputs. It formats rather than sanitizes, so do not rely on it to clean untrusted input.
One caveat when you render another post’s content this way: some shortcodes and dynamic blocks read the current global post, so outside that post’s own loop they can resolve against the wrong post. To handle that, assign the target to the global $post and prime the template data with setup_postdata() before applying the filter, then call wp_reset_postdata() to restore the previous context. If you also need the post’s custom fields, fetch those by ID the same way. You can also get the post slug using the same ID if you need to build custom links.
Summary
With a post ID in hand: get_post() returns the object (always null-check it), get_the_title() returns the filtered title, and for the body you choose between the raw post_content property and apply_filters( 'the_content', ... ) for fully formatted output. Reach for the the_content filter whenever the content needs to look the way it does on the front end, and keep the snippet in a child theme or small plugin (see where to add code in WordPress).
Comments