This question refers to @Pieter Goosen´s extensive work about how to alter the main query using the pre_get_posts hook on true pages. It is also referenced in the codex.

With his method you are able to inject specified posts in your page without having to touch the template file itself. This gives numerous development options.

Until now his method uses CSS to hide the post object which the standard loop outputs for the page that is displayed. There are two actions hooks, loop_start and loop_end that are used to create a hidden container around the post object. It would be great if it was possible to completly exclude the page´s post object.

I tried to hook into pre_get_posts with something like $query->set('post__not_in', get_the_ID(); at loop_start but this didnt work. I am lacking the skills needed to put the finishing touches on this.

Can you help? Remember we dont want to touch the template files. Thanks in advance.

  • This is the same problem I faced when I wrote the code in the linked answer. Unfortunately I haven't fount a better solution as hiding the page with CSS. The big issue is, if you need to keep the page integrity for use in breadcrumbs etc, you need to let the main query run its course. It would be nice if someone can come up with another approach to this. Unfortunately I'm really tied up with other projects, so I just do not have any time to code or play with possible scenarios. I hope you get this solved, it will not only be useful to yourself, but to many others out there Commented May 17, 2016 at 12:50
  • I will keep trying and agree this should be considered as relevant. Good luck and thanks for responding to this.
    – matjaeck
    Commented May 17, 2016 at 13:24
  • 1
    so you are trying to have a page, say "/page/", and you want to alter the main query to instead display some posts... is that correct?? what is your desired end-goal?
    – Caleb
    Commented Jul 2, 2016 at 5:57

2 Answers 2


I think pre_get_posts should work with $query->set(). Only problem I see there is you are using get_the_ID() in a hook. Try using global $post variable to get the current post id.


Here's a simple hack that might help you achieve to whatever it is you are trying to do -: Warning: This is only tested on twentysixteen and twentyseventeen themes so it may or may not work to your current theme.

class VirtualPage {
 * @var int $page_id    The ID of the page where you would want to inject your custom template.
var $page_id = 0;

 * @var (mixed) $callback   The callback function/method that will be called to replace the current post.
var $callback = false;

function __construct( $page_id, $callback = false ) {
    $this->page_id = $page_id;
    $this->callback = $callback;

     * Set the injector when there are posts found.
    add_action( 'posts_results', array( $this, 'posts_results' ), 10, 2 );

function posts_results( $posts, $wp ) {

    if ( $wp->is_main_query()
        && $wp->is_singular
        && count( $posts ) > 0
        && $posts[0]->ID == $this->page_id ) {

        $found_posts = count( $posts );

         * $wp->post_count holds the number of iterated posts. We'll make WP believe that all
         * posts are iterated.
        $wp->post_count = $found_posts;

         * $wp->current_post holds the current post index. Setting it to the last post index
         * will immediately trigger `loop_end` action hook.
        $wp->current_post = $found_posts - 1;

        add_action( 'loop_end', array( $this, 'loop_end' ) );

         * Immediately remove the hook!
        remove_action( 'posts_results', array( $this, 'posts_results' ), 10, 2 );

    return $posts;

function loop_end() {
    if ( $this->callback ) {
        // Call your callback here or do your stuff here
        call_user_func( $this->callback );

     * Immediately remove the hook!
    remove_action( 'loop_end', array( $this, 'loop_end' ) );


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.