How To Show Event CPT Loop On Archive Elementor Free

If you want to put this on the event archive page, you can do so by checking out my other guide How To Edit Event Archive Elementor Free

  1. First you need to add the custom code in a snippet plugin in the video i am using WpCodeBox which is a paid plugin. You can also make it work with a free code snippet plugin, just remember to set the snippet to php. Its also possible to create a child theme and place the code inside the child themes functions file.
<?php
/**
 * Linnet Advanced Post Grid Widget
 */

add_action( 'elementor/init', function() {

    add_action( 'elementor/widgets/register', function( $widgets_manager ) {

        class Linnet_Post_Grid_Widget extends \Elementor\Widget_Base {

            public function get_name() {
                return 'linnet_post_grid';
            }

            public function get_title() {
                return __( 'Linnet Post Grid', 'linnet' );
            }

            public function get_icon() {
                return 'eicon-posts-grid';
            }

            public function get_categories() {
                return [ 'general' ];
            }

            protected function register_controls() {

                $this->start_controls_section(
                    'content_section',
                    [
                        'label' => __( 'Content', 'linnet' ),
                        'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
                    ]
                );

                // Post Type selector
                $post_types = get_post_types( [ 'public' => true ], 'objects' );
                $post_type_options = [];
                foreach ( $post_types as $post_type ) {
                    $post_type_options[ $post_type->name ] = $post_type->labels->singular_name;
                }

                $this->add_control(
                    'post_type',
                    [
                        'label' => __( 'Post Type', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SELECT,
                        'options' => $post_type_options,
                        'default' => 'post',
                    ]
                );

                $this->add_control(
                    'posts_per_page',
                    [
                        'label' => __( 'Posts Per Page', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::NUMBER,
                        'default' => 6,
                    ]
                );

                // Order By
                $this->add_control(
                    'order_by',
                    [
                        'label' => __( 'Order By', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SELECT,
                        'options' => [
                            'title_asc'              => 'Title Ascending',
                            'title_desc'             => 'Title Descending',
                            'post_date_asc'          => 'Post Date Ascending',
                            'post_date_desc'         => 'Post Date Descending',
                            'acf_date_asc'           => 'ACF Date Ascending',
                            'acf_date_desc'          => 'ACF Date Descending',
                            'acf_datetime_asc'       => 'ACF Date/Time Ascending',
                            'acf_datetime_desc'      => 'ACF Date/Time Descending',
                        ],
                        'default' => 'post_date_desc',
                    ]
                );

                $this->add_control(
                    'acf_order_field',
                    [
                        'label' => __( 'ACF Field Name (for ordering)', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::TEXT,
                        'placeholder' => 'my_acf_date_field',
                        'condition' => [
                            'order_by' => [ 'acf_date_asc', 'acf_date_desc', 'acf_datetime_asc', 'acf_datetime_desc' ],
                        ],
                    ]
                );

                // Taxonomy filter — add empty option first
                $taxonomy_options = [ '' => '— No Taxonomy Filter —' ];
                $taxonomies = get_taxonomies( [ 'public' => true ], 'objects' );
                foreach ( $taxonomies as $taxonomy ) {
                    $taxonomy_options[ $taxonomy->name ] = $taxonomy->labels->singular_name;
                }

                $this->add_control(
                    'filter_taxonomy',
                    [
                        'label' => __( 'Filter Taxonomy', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SELECT,
                        'options' => $taxonomy_options,
                        'default' => '',
                    ]
                );

                $this->add_control(
                    'filter_terms_include',
                    [
                        'label' => __( 'Include Terms (comma separated IDs)', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::TEXT,
                        'placeholder' => '1,2,3',
                    ]
                );

                $this->add_control(
                    'filter_terms_exclude',
                    [
                        'label' => __( 'Exclude Terms (comma separated IDs)', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::TEXT,
                        'placeholder' => '4,5,6',
                    ]
                );

                // ACF Date/Datetime filter
                $this->add_control(
                    'acf_date_filter_field',
                    [
                        'label' => __( 'ACF Date/DateTime Field for Filter', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::TEXT,
                        'placeholder' => 'my_acf_date_field',
                    ]
                );

                $this->add_control(
                    'acf_date_filter_past',
                    [
                        'label' => __( 'Show posts where date is in the PAST', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SWITCHER,
                        'default' => '',
                    ]
                );

                $this->add_control(
                    'acf_date_filter_future',
                    [
                        'label' => __( 'Show posts where date is in the FUTURE', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SWITCHER,
                        'default' => '',
                    ]
                );


                // Enable Pagination
                $this->add_control(
                    'enable_pagination',
                    [
                        'label' => __( 'Enable Pagination', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SWITCHER,
                        'default' => 'yes',
                    ]
                );

                // Custom Anchor ID
                $this->add_control(
                    'anchor_id',
                    [
                        'label' => __( 'Custom Anchor ID (for pagination scroll)', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::TEXT,
                        'placeholder' => 'linnet-post-grid',
                        'description' => __( 'If empty, default is "linnet-post-grid".', 'linnet' ),
                    ]
                );

                // Columns per device
                $this->add_responsive_control(
                    'columns',
                    [
                        'label' => __( 'Columns', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::SELECT,
                        'default' => '3',
                        'options' => [
                            '1' => '1',
                            '2' => '2',
                            '3' => '3',
                            '4' => '4',
                        ],
                        'selectors' => [
                            '{{WRAPPER}} .linnet-post-grid' => 'grid-template-columns: repeat({{VALUE}}, 1fr);',
                        ],
                    ]
                );

                // Display Order Repeater
                $this->add_control(
                    'display_order',
                    [
                        'label' => __( 'Display Order', 'linnet' ),
                        'type' => \Elementor\Controls_Manager::REPEATER,
                        'fields' => [
                            [
                                'name'    => 'element_type',
                                'label'   => __( 'Element Type', 'linnet' ),
                                'type'    => \Elementor\Controls_Manager::SELECT,
                                'options' => [
                                    'title'           => 'Post Title',
                                    'featured_image'  => 'Featured Image',
                                    'acf_field'       => 'ACF Field',
                                ],
                                'default' => 'title',
                            ],
                            [
                                'name'    => 'acf_field_name',
                                'label'   => __( 'ACF Field Name', 'linnet' ),
                                'type'    => \Elementor\Controls_Manager::TEXT,
                                'placeholder' => 'my_acf_field',
                                'condition' => [
                                    'element_type' => 'acf_field',
                                ],
                            ],
                            [
                                'name'    => 'acf_field_class',
                                'label'   => __( 'ACF Field CSS Class', 'linnet' ),
                                'type'    => \Elementor\Controls_Manager::TEXT,
                                'placeholder' => 'my-class-name',
                                'condition' => [
                                    'element_type' => 'acf_field',
                                ],
                            ],
                        ],
                        'default' => [
                            [ 'element_type' => 'featured_image' ],
                            [ 'element_type' => 'title' ],
                        ],
                        'title_field' => '{{{ element_type }}}',
                    ]
                );

                $this->end_controls_section();
            }

            protected function render() {

                $settings = $this->get_settings_for_display();

                $anchor_id = ! empty( $settings['anchor_id'] ) ? $settings['anchor_id'] : 'linnet-post-grid';
                $paged_param_name = 'linnet_paged_' . sanitize_title( $anchor_id );
                $paged = isset( $_GET[ $paged_param_name ] ) ? max( 1, intval( $_GET[ $paged_param_name ] ) ) : 1;


                $args = [
                    'post_type'      => $settings['post_type'],
                    'posts_per_page' => $settings['posts_per_page'],
                    'paged'          => $paged,
                ];

                // ORDER BY
                switch ( $settings['order_by'] ) {
                    case 'title_asc':
                        $args['orderby'] = 'title';
                        $args['order'] = 'ASC';
                        break;
                    case 'title_desc':
                        $args['orderby'] = 'title';
                        $args['order'] = 'DESC';
                        break;
                    case 'post_date_asc':
                        $args['orderby'] = 'date';
                        $args['order'] = 'ASC';
                        break;
                    case 'post_date_desc':
                        $args['orderby'] = 'date';
                        $args['order'] = 'DESC';
                        break;
                    case 'acf_date_asc':
                    case 'acf_date_desc':
                    case 'acf_datetime_asc':
                    case 'acf_datetime_desc':
                        $args['meta_key'] = $settings['acf_order_field'];
                        $args['orderby'] = 'meta_value';
                        $args['order'] = ( strpos( $settings['order_by'], 'asc' ) !== false ) ? 'ASC' : 'DESC';
                        break;
                    default:
                        $args['orderby'] = 'date';
                        $args['order'] = 'DESC';
                        break;
                }

                // Taxonomy filter — with proper empty handling
                if ( ! empty( $settings['filter_taxonomy'] ) ) {

                    $has_include = ! empty( $settings['filter_terms_include'] );
                    $has_exclude = ! empty( $settings['filter_terms_exclude'] );

                if ( $has_include || $has_exclude ) {

                $tax_query = [
                    'taxonomy' => $settings['filter_taxonomy'],
                    'field'    => 'term_id',
                ];

                if ( $has_include ) {
                    $tax_query['terms'] = array_map( 'intval', explode( ',', $settings['filter_terms_include'] ) );
                    $tax_query['operator'] = 'IN';
                } elseif ( $has_exclude ) {
                 $tax_query['terms'] = array_map( 'intval', explode( ',', $settings['filter_terms_exclude'] ) );
                $tax_query['operator'] = 'NOT IN';
                }

                $args['tax_query'] = [ $tax_query ];

    } 
    // ELSE → do NOT set tax_query → show all posts
}
// ACF Date Filter — IN PAST / IN FUTURE
if ( ! empty( $settings['acf_date_filter_field'] ) ) {

    $now = current_time( 'Y-m-d H:i:s' ); // current time, in WP timezone

    // Only IN PAST
    if ( 'yes' === $settings['acf_date_filter_past'] && 'yes' !== $settings['acf_date_filter_future'] ) {
        $args['meta_query'][] = [
            'key'     => $settings['acf_date_filter_field'],
            'compare' => '<',
            'type'    => 'DATETIME',
            'value'   => $now,
        ];
    }

    // Only IN FUTURE
    if ( 'yes' === $settings['acf_date_filter_future'] && 'yes' !== $settings['acf_date_filter_past'] ) {
        $args['meta_query'][] = [
            'key'     => $settings['acf_date_filter_field'],
            'compare' => '>',
            'type'    => 'DATETIME',
            'value'   => $now,
        ];
    }

    // If BOTH are ON → no meta_query needed → show all posts
}


                $query = new WP_Query( $args );

                $anchor_id = ! empty( $settings['anchor_id'] ) ? $settings['anchor_id'] : 'linnet-post-grid';

                // Pagination styling
                echo '<style>
.linnet-post-image img{
    width:100%;
}
.linnet-pagination nav ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    list-style: none;
    padding: 0;
    margin: 20px 0;
    gap: 6px;
}

.linnet-pagination nav li {
    display: inline-flex;
}

.linnet-pagination nav a,
.linnet-pagination nav span {
    display: inline-block;
    padding: 8px 12px;
    border: 1px solid #ccc;
    text-decoration: none;
    color: #333;
    border-radius: 4px;
    font-size: 14px;
    min-width: 36px;
    text-align: center;
}

.linnet-pagination nav .current {
    background: #333;
    color: #fff;
    border-color: #333;
}

.linnet-pagination nav a:hover {
    background: #f0f0f0;
    border-color: #888;
}

@media (max-width: 480px) {
    .linnet-pagination nav a,
    .linnet-pagination nav span {
        padding: 6px 8px;
        font-size: 13px;
        min-width: auto;
    }
}
</style>';

                // Grid wrapper
                echo '<div id="' . esc_attr( $anchor_id ) . '" class="linnet-post-grid" style="display:grid; grid-gap:20px;">';

                if ( $query->have_posts() ) {
                    while ( $query->have_posts() ) {
                        $query->the_post();

                        echo '<div class="linnet-post-item" style="border:1px solid #ccc; padding:10px;">';

                        foreach ( $settings['display_order'] as $order_item ) {
                            switch ( $order_item['element_type'] ) {
                                case 'featured_image':
                                    if ( has_post_thumbnail() ) {
                                    echo '<div class="linnet-post-image">';
                                    echo '<a href="' . esc_url( get_permalink() ) . '">';
                                    the_post_thumbnail( 'medium' );
                                    echo '</a>';
                                    echo '</div>';
                                    }

                                    break;

                                case 'title':
                                    echo '<h3 class="linnet-post-title"><a href="' . esc_url( get_permalink() ) . '">' . get_the_title() . '</a></h3>';
                                    break;

                                case 'acf_field':
                                    $field_name = $order_item['acf_field_name'];
                                    $field_class = isset( $order_item['acf_field_class'] ) ? $order_item['acf_field_class'] : '';
                                    if ( ! empty( $field_name ) ) {
                                        $acf_value = get_field( $field_name );
                                        if ( $acf_value ) {
                                            echo '<div class="linnet-post-acf ' . esc_attr( $field_class ) . '">' . esc_html( $acf_value ) . '</div>';
                                        }
                                    }
                                    break;
                            }
                        }

                        echo '</div>'; // linnet-post-item
                    }

                    echo '</div>'; // linnet-post-grid

                    if ( 'yes' === $settings['enable_pagination'] ) {
                        $pagination = paginate_links( [
                            'base'      => add_query_arg( $paged_param_name, '%#%' ) . '#' . $anchor_id,
                            'format'    => '',
                            'current'   => max( 1, $paged ),
                            'total'     => $query->max_num_pages,
                            'type'      => 'list',
                            'prev_text' => __( '&laquo; Previous' ),
                            'next_text' => __( 'Next &raquo;' ),
                        ] );

                        if ( $pagination ) {
                            echo '<div class="linnet-pagination"><nav>' . $pagination . '</nav></div>';
                        }
                    }

                    wp_reset_postdata();

                } else {
                    echo '<p>' . __( 'No posts found.', 'linnet' ) . '</p>';
                }
            }
        }

        $widgets_manager->register( new \Linnet_Post_Grid_Widget() );
    });
});

add_action( 'admin_notices', function() {
    if ( ! did_action( 'elementor/loaded' ) ) {
        echo '<div class="notice notice-warning"><p>Linnet Post Grid Widget requires Elementor to be active.</p></div>';
    }
});
  1. Then go to your your archive template and edit it with Elementor. Linnet templates -> Templates.
  2. Go to widgets and search for linnet post grid, then add it to the page.
  3. In the module content settings, locate the post type option and set it to Event.
  4. Then click publish, now your events will show on the archive page.