If you need a simple event listing on a WordPress site — with a start date, title, and description — you do not always need a dedicated events plugin. Registering a custom post type and combining it with an ACF date field gives you a clean, lightweight solution with full control over the output.
Problem: A site needs to list upcoming events with a date, location, and description — but installing a full events plugin adds unnecessary complexity and overhead for a simple listing.
Solution: Register a custom post type event with a date meta field, query upcoming events with WP_Query using a meta_query date comparison and meta_key ordering, and build a straightforward list or card template in the theme.
Step 1. Register the event post type in functions.php:
<?php
add_action( 'init', 'register_event_post_type' );
function register_event_post_type() {
$labels = [
'name' => __( 'Events', 'theme-name' ),
'singular_name' => __( 'Event', 'theme-name' ),
'menu_name' => __( 'Events', 'theme-name' ),
'add_new_item' => __( 'Add New Event', 'theme-name' ),
'edit_item' => __( 'Edit Event', 'theme-name' ),
'not_found' => __( 'No events found', 'theme-name' ),
];
$args = [
'labels' => $labels,
'supports' => [ 'title', 'editor', 'excerpt', 'thumbnail' ],
'show_in_rest' => true,
'public' => true,
'has_archive' => false,
'menu_position' => 6,
'menu_icon' => 'dashicons-calendar-alt',
'rewrite' => [ 'slug' => 'event', 'with_front' => true ],
];
register_post_type( 'event', $args );
}
In ACF, create a Date Picker field named start_date and assign it to the event post type. Set the return format to F j, Y (e.g. October 15, 2019) so it is compatible with PHP's strtotime().
Step 2. Query and output upcoming events, filtering out any event whose start date has already passed:
<?php
// Collect IDs of past events to exclude
$all_events = new WP_Query( [
'post_type' => 'event',
'posts_per_page' => -1,
'fields' => 'ids',
'no_found_rows' => true,
] );
$exclude_ids = [];
foreach ( $all_events->posts as $event_id ) {
$start = get_field( 'start_date', $event_id );
if ( $start && strtotime( current_time( 'F j, Y' ) ) > strtotime( $start ) ) {
$exclude_ids[] = $event_id;
}
}
// Main query: upcoming events ordered by start date ascending
$events_query = new WP_Query( [
'post_type' => 'event',
'posts_per_page' => 10,
'order' => 'ASC',
'orderby' => 'meta_value',
'meta_key' => 'start_date',
'post__not_in' => $exclude_ids,
] );
if ( $events_query->have_posts() ) :
while ( $events_query->have_posts() ) : $events_query->the_post();
// Output event title, date, excerpt, etc.
endwhile;
wp_reset_postdata();
else :
echo '<p>' . esc_html__( 'No upcoming events at the moment. Check back soon!', 'theme-name' ) . '</p>';
endif;
NOTE: This approach compares dates as strings via strtotime(), which works reliably when the ACF return format is consistent. For more complex date filtering — timezone awareness, multi-day events, or recurring events — a dedicated ACF meta query with a DATETIME comparison or a purpose-built plugin (The Events Calendar) will serve you better.