How-to: Custom Post Type for Events

Chances are you found this page looking for a tutorial on how to create Events with Custom Post Types within WordPress. I will outline the entire process I’ve used for one of my themes that I’m re-coding (in order to make use of this great feature). You can also download the entire custom post type file that includes the code mentioned here. This is the visual output as is in testing today (please see the second tutorial for the code on outputting the design):

Whilst this tutorial shows all the code, I’ll go over the “default” parts quite quickly and then focus on any code relating to the actual Events component. As a designer, I’ve found it very manageable to put together something that 5 years ago would have been so much more work (and who likes manual work?). So if I can do it, anyone can (really).

1) event requirements

Before hitting the code, it’s important to understand what you really need so that you can strive towards the simplest end solution. For the events custom post type of my theme,  I need to be able to show the following items (beyond the default fields):

  • Start Date
  • Start Time
  • End Date
  • End Time

I debated if I even required an end date (2 more fields to fill out for the user), but figured I’d probably be limiting my options for some of the other ideas I have. The great thing about storing dates is that they’re done so using Unix Time (i.e. seconds elapsed since 1/1/1970 as a single integer). This means that we have tons of flexibility when it comes to extracting and showing the dates with default PHP functions. As such, whilst the end user will see 4 fields, we’re only storing 2 fields (thus my custom post type will only require those 2 extra custom fields).

One thing I wanted to avoid was also feature bloat. There are plenty of event plug-ins out there with advanced functionality (recurrence, rsvp, paypal, etc.). I’ll leave those awesome things to the plug-in designers, there’s absolutely no need to reinvent the wheel there. My primary goal is to design themes, and the out of the box functionality I provide is a simple foundation that works with the design (more on that in my next tutorial). So, onto the good stuff:

2) registering our custom post type

I’ll assume you’re already somewhat familiar with this aspect of the custom post type process (it’s actually very well documented within the WordPress Codex). I’m not doing anything special either, so feel free to have a look or just skip right onto the next section. All we’re doing is providing a set of labels or phrases to be used by the Custom Post Type and then defining the characteristics or behavior of the post type itself. Also, if you want to save yourself an afternoon, make sure the actual name of the custom post type (not label) is 20 characters or less.

// 1. Custom Post Type Registration (Events)

add_action( 'init', 'create_event_postype' );

function create_event_postype() {

$labels = array(
    'name' => _x('Events', 'post type general name'),
    'singular_name' => _x('Event', 'post type singular name'),
    'add_new' => _x('Add New', 'events'),
    'add_new_item' => __('Add New Event'),
    'edit_item' => __('Edit Event'),
    'new_item' => __('New Event'),
    'view_item' => __('View Event'),
    'search_items' => __('Search Events'),
    'not_found' =>  __('No events found'),
    'not_found_in_trash' => __('No events found in Trash'),
    'parent_item_colon' => '',

$args = array(
    'label' => __('Events'),
    'labels' => $labels,
    'public' => true,
    'can_export' => true,
    'show_ui' => true,
    '_builtin' => false,
    'capability_type' => 'post',
    'menu_icon' => get_bloginfo('template_url').'/functions/images/event_16.png',
    'hierarchical' => false,
    'rewrite' => array( "slug" => "events" ),
    'supports'=> array('title', 'thumbnail', 'excerpt', 'editor') ,
    'show_in_nav_menus' => true,
    'taxonomies' => array( 'tf_eventcategory', 'post_tag')

register_post_type( 'tf_events', $args);


3) custom taxonomy

This step is optional, but always a safe play for anyone who’d like the ability to categorize their events. Again, I’m not doing anything out of the ordinary (except maybe making it hierarchical). Don’t forget to attach the taxonomy to the post type:

function create_eventcategory_taxonomy() {

$labels = array(
    'name' => _x( 'Categories', 'taxonomy general name' ),
    'singular_name' => _x( 'Category', 'taxonomy singular name' ),
    'search_items' =>  __( 'Search Categories' ),
    'popular_items' => __( 'Popular Categories' ),
    'all_items' => __( 'All Categories' ),
    'parent_item' => null,
    'parent_item_colon' => null,
    'edit_item' => __( 'Edit Category' ),
    'update_item' => __( 'Update Category' ),
    'add_new_item' => __( 'Add New Category' ),
    'new_item_name' => __( 'New Category Name' ),
    'separate_items_with_commas' => __( 'Separate categories with commas' ),
    'add_or_remove_items' => __( 'Add or remove categories' ),
    'choose_from_most_used' => __( 'Choose from the most used categories' ),

register_taxonomy('tf_eventcategory','tf_events', array(
    'label' => __('Event Category'),
    'labels' => $labels,
    'hierarchical' => true,
    'show_ui' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'event-category' ),

add_action( 'init', 'create_eventcategory_taxonomy', 0 );

4) creating & showing columns

This is where it starts to get more interesting. Within the dashboard, custom post types are listed the same way as regular posts & pages are, however we’re the ones who need to make it all happen. This is what our final output will look like:

As you’ll see from the code below, we need to create a function to define our headers (tf_events_edit_columns), and then another function to define with which content we’re going to populate it with (tf_events_custom_columns). Beyond the standard fields (title, description, etc.), I’m also going to show my times here, and this is thought process behind the event-related fields:

  • Dates – Here I’m grabbing the dates and hardcoding which format they should be output in. Dates are more or less universal, whereby times aren’t (i.e. 24hr vs AM/PM). Seeing as (in my case) the end date will almost always be the same as the start, I’ve subdued its visual effect to reduce the clutter a bit.
  • Times – As I mentioned above, 24hr vs AM/PM is an important factor. So to make this easy on the end-user we’ll grab the local format using get_option(‘time_format’) which we can then feed right into the date() function.
// 3. Show Columns

add_filter ("manage_edit-tf_events_columns", "tf_events_edit_columns");
add_action ("manage_posts_custom_column", "tf_events_custom_columns");

function tf_events_edit_columns($columns) {

$columns = array(
    "cb" => "<input type=\"checkbox\" />",
    "tf_col_ev_cat" => "Category",
    "tf_col_ev_date" => "Dates",
    "tf_col_ev_times" => "Times",
    "tf_col_ev_thumb" => "Thumbnail",
    "title" => "Event",
    "tf_col_ev_desc" => "Description",
return $columns;

function tf_events_custom_columns($column)
global $post;
$custom = get_post_custom();
switch ($column)
case "tf_col_ev_cat":
    // - show taxonomy terms -
    $eventcats = get_the_terms($post->ID, "tf_eventcategory");
    $eventcats_html = array();
    if ($eventcats) {
    foreach ($eventcats as $eventcat)
    array_push($eventcats_html, $eventcat->name);
    echo implode($eventcats_html, ", ");
    } else {
    _e('None', 'themeforce');;
case "tf_col_ev_date":
    // - show dates -
    $startd = $custom["tf_events_startdate"][0];
    $endd = $custom["tf_events_enddate"][0];
    $startdate = date("F j, Y", $startd);
    $enddate = date("F j, Y", $endd);
    echo $startdate . '<br /><em>' . $enddate . '</em>';
case "tf_col_ev_times":
    // - show times -
    $startt = $custom["tf_events_startdate"][0];
    $endt = $custom["tf_events_enddate"][0];
    $time_format = get_option('time_format');
    $starttime = date($time_format, $startt);
    $endtime = date($time_format, $endt);
    echo $starttime . ' - ' .$endtime;
case "tf_col_ev_thumb":
    // - show thumb -
    $post_image_id = get_post_thumbnail_id(get_the_ID());
    if ($post_image_id) {
    $thumbnail = wp_get_attachment_image_src( $post_image_id, 'post-thumbnail', false);
    if ($thumbnail) (string)$thumbnail = $thumbnail[0];
    echo '<img src="';
    echo bloginfo('template_url');
    echo '/timthumb/timthumb.php?src=';
    echo $thumbnail;
    echo '&h=60&w=60&zc=1" alt="" />';
case "tf_col_ev_desc";


In order to save space, I’ve combined my start & end dates into one column, as well as my start and end times. Again, this makes it all a little easier on the user. Here is also the CSS used which I’m only calling within the admin area (it’s a great help as it can really reduce those wide columns on larger monitors):

/* Columns CPT Events */

th#tf_col_ev_date, th#tf_col_ev_cat {width:150px}
td.tf_col_ev_date em {color:gray;}
th#tf_col_ev_times {width:150px}
th#tf_col_ev_thumb {width:100px}

5) show meta box

We then need to provide our users with fields they can fill out, for which a custom meta box is required. It’d be nice if WordPress would automatically do that for a predefined set of field types (text, date, checkbox, dropdown, etc.), however this hasn’t made its way into the system yet. So with the current state of things, you’ll need to beautify the look of it yourself. On a positive note, WordPress 3.1 will use jQuery 1.4.4 and jQuery UI 1.8.6., at least we’ll be somewhat up-to-date there. For the purpose of this tutorial, I’ve left out any fancy formatting or branding, this being the final look:

In the code below, we’ll register the meta-box, link it to the custom post type and then create the form. Again, here is the thought process behind the event-related fields:

  • Split the Database Date Value – As you may recall, I’m using 2 custom fields in the database to output 4 visible data-entry fields. I do this by using the date() function for both, but only outputting the *date* within the first variable, and only the *time* within the second.
  • If Empty, show Today – Zero in Unix Time is 1970. Clearly, it’d be tough to scroll through 40 years worth of months for the end-user (talk about the quickest refund in the history of WordPress). As such, if we have no value in the database (i.e. when we’re creating a new event), we’ll populate it with todays date. If you don’t want the current time to be shown (remember how we split 1 database field into 2 meta fields), then we’ll also need to change the new time field to 0.
  • Strong Date Format – You’ll notice here that I predefined the date format to “D, M d, Y“. When we’re going to use the strtotime() function later on to save our data, it needs to function for all sorts of dates. When experimenting with shorter date formats I noticed that often the day & month would get switched (US vs Euro date styles). By using “D, M d, Y” (i.e. “Fri, Feb 11, 2011” I not only have something that is visually functional but also feeds back into the database without any issues.

When looking at the code, you could argue that we need more controls & validation. I understand that to a degree, but people aren’t THAT stupid. If I’m asking them to input “14:00” for 2pm, 99% will do it on the first try and the remaining 1% would have learned it on their second attempt. Label me pragmatic, but don’t call my clients stupid :)

// 4. Show Meta-Box

add_action( 'admin_init', 'tf_events_create' );

function tf_events_create() {
    add_meta_box('tf_events_meta', 'Events', 'tf_events_meta', 'tf_events');

function tf_events_meta () {

// - grab data -

global $post;
$custom = get_post_custom($post->ID);
$meta_sd = $custom["tf_events_startdate"][0];
$meta_ed = $custom["tf_events_enddate"][0];
$meta_st = $meta_sd;
$meta_et = $meta_ed;

// - grab wp time format -

$date_format = get_option('date_format'); // Not required in my code
$time_format = get_option('time_format');

// - populate today if empty, 00:00 for time -

if ($meta_sd == null) { $meta_sd = time(); $meta_ed = $meta_sd; $meta_st = 0; $meta_et = 0;}

// - convert to pretty formats -

$clean_sd = date("D, M d, Y", $meta_sd);
$clean_ed = date("D, M d, Y", $meta_ed);
$clean_st = date($time_format, $meta_st);
$clean_et = date($time_format, $meta_et);

// - security -

echo '<input type="hidden" name="tf-events-nonce" id="tf-events-nonce" value="' .
wp_create_nonce( 'tf-events-nonce' ) . '" />';

// - output -

<div class="tf-meta">
    <li><label>Start Date</label><input name="tf_events_startdate" class="tfdate" value="<?php echo $clean_sd; ?>" /></li>
    <li><label>Start Time</label><input name="tf_events_starttime" value="<?php echo $clean_st; ?>" /><em>Use 24h format (7pm = 19:00)</em></li>
    <li><label>End Date</label><input name="tf_events_enddate" class="tfdate" value="<?php echo $clean_ed; ?>" /></li>
    <li><label>End Time</label><input name="tf_events_endtime" value="<?php echo $clean_et; ?>" /><em>Use 24h format (7pm = 19:00)</em></li>

Also, some very basic CSS to structure it a little:

/* Metabox */

.tf-meta {  }
.tf-meta ul li { height: 20px; clear:both; margin: 0 0 15px 0;}
.tf-meta ul li label { width: 100px; display:block; float:left; padding-top:4px; }
.tf-meta ul li input { width:125px; display:block; float:left; }
.tf-meta ul li em { width: 200px; display:block; float:left; color:gray; margin-left:10px; padding-top: 4px}

Furthermore, I tweaked the jQuery a little to show 3 months right off the bat as well as the calendar icon. I think this helps the end-user (and especially my clients). Keep in mind that the following code is in a separate .js file, not in the document directly (as you’d need to switch out the $ with jQuery). The last section of the article will also show you how to implement the datepicker:

    dateFormat: 'D, M d, yy',
    showOn: 'button',
    buttonImage: '/yourpath/icon-datepicker.png',
    buttonImageOnly: true,
    numberOfMonths: 3


6) save meta box

Saving the custom data deserves a little section of it’s own, but the code is all quite standard. Check Nonce, ok? Check if Current User is allowed, ok? Save the data. The only special part is where we take the 4 events-related input fields and merge them back to the 2 database fields. As we’ve kept everything nice and simple so far, it’s no issue, we’ll just merge the date & time fields and wrap it in a strtotime() function, like so: $fulldate = strtotime ( $date . $time ). As we’ve been using “safe” date & time formats, there’ll be no issue here. Obviously the actual code requires all the other WordPress parameters to make it work, but nothing out of the ordinary:

// 5. Save Data

add_action ('save_post', 'save_tf_events');

function save_tf_events(){

global $post;

// - still require nonce

if ( !wp_verify_nonce( $_POST['tf-events-nonce'], 'tf-events-nonce' )) {
    return $post->ID;

if ( !current_user_can( 'edit_post', $post->ID ))
    return $post->ID;

// - convert back to unix & update post

return $post;
$updatestartd = strtotime ( $_POST["tf_events_startdate"] . $_POST["tf_events_starttime"] );
update_post_meta($post->ID, "tf_events_startdate", $updatestartd );

return $post;
$updateendd = strtotime ( $_POST["tf_events_enddate"] . $_POST["tf_events_endtime"]);
update_post_meta($post->ID, "tf_events_enddate", $updateendd );


7) customize update messages

I think this part is often overlooked in custom post types tutorials but makes the usability aspect so much cleaner. Whenever a user updates something, we’d like it to use events or a similar phrase to update the user instead of “post”. My final code is below, but it’s almost an exact match to what’s in the Codex under the register_post_type page.

// 6. Customize Update Messages

add_filter('post_updated_messages', 'events_updated_messages');

function events_updated_messages( $messages ) {

  global $post, $post_ID;

  $messages['tf_events'] = array(
    0 => '', // Unused. Messages start at index 1.
    1 => sprintf( __('Event updated. <a href="%s">View item</a>'), esc_url( get_permalink($post_ID) ) ),
    2 => __('Custom field updated.'),
    3 => __('Custom field deleted.'),
    4 => __('Event updated.'),
    /* translators: %s: date and time of the revision */
    5 => isset($_GET['revision']) ? sprintf( __('Event restored to revision from %s'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
    6 => sprintf( __('Event published. <a href="%s">View event</a>'), esc_url( get_permalink($post_ID) ) ),
    7 => __('Event saved.'),
    8 => sprintf( __('Event submitted. <a target="_blank" href="%s">Preview event</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
    9 => sprintf( __('Event scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview event</a>'),
      // translators: Publish box date format, see
      date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
    10 => sprintf( __('Event draft updated. <a target="_blank" href="%s">Preview event</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),

  return $messages;

8) Custom jQuery UI for Events only

Last but not least, in order to make the jQuery Datepicker work in the WordPress admin area, we’ll want do some configuring. We’ll de-register the default WP jQuery UI file and then register our custom files downloaded from the official jQuery site (or linked to from the Google jQuery CDN). You’ll notice the pubforce-admin.js below, that will contain our datepicker settings mentioned above in the meta box section of this tutorial.

// 7. JS Datepicker UI

function events_styles() {
    global $post_type;
    if( 'tf_events' != $post_type )
    wp_enqueue_style('ui-datepicker', get_bloginfo('template_url') . '/css/jquery-ui-1.8.9.custom.css');

function events_scripts() {
    global $post_type;
    if( 'tf_events' != $post_type )
    wp_enqueue_script('jquery-ui', get_bloginfo('template_url') . '/js/jquery-ui-1.8.9.custom.min.js', array('jquery'));
    wp_enqueue_script('ui-datepicker', get_bloginfo('template_url') . '/js/jquery.ui.datepicker.min.js');
    wp_enqueue_script('custom_script', get_bloginfo('template_url').'/js/pubforce-admin.js', array('jquery'));

add_action( 'admin_print_styles-post.php', 'events_styles', 1000 );
add_action( 'admin_print_styles-post-new.php', 'events_styles', 1000 );

add_action( 'admin_print_scripts-post.php', 'events_scripts', 1000 );
add_action( 'admin_print_scripts-post-new.php', 'events_scripts', 1000 );

9) your turn to speak !

It may look like a lot of code (feel free to download the entire code here), but for the functionality and flexibility provided it’s really automated. If you have any questions, feedback or suggestions, feel free to comment below. Like said, I’m not a developer so I’m more then open to ideas and would also update the code above (& provide credit). Re-tweet if you feel like it too ;) Thank you!

I have already released Part 2: Query, Loop & Design Output , check it out!

Here are also the next tutorials planned in connection with this event post type:

other articles in this series

  1. Front-end: Design & Shortcodes
  2. Front-end: iCal Export
  3. Front-end: Implementing FullCalender

related links

  • George Mamadashvili

    Hello Noel. There’re bunch of event plugins, GigPress is my favorite, but sometimes it’s more easier to register custom post type and add few meta boxes.

    Waiting for next tutorial

  • Noel

    Hi George,

    Completely agree! In my case, I don’t want to rely on external developers to provide for my clients. Just want to be able to give them a simple events “tool” that is fully integrated into the design (and I’ll post that shortly!). Really just depends on your requirements, as I mentioned though beyond what I’m doing here there’s no need to reinvent the wheel with so many great feature-rich event plugins out there.



  • Nice tutorial, can’t wait for part two, is there any way you can display these event by maybe sorting upcoming events and past events, with respective thumbnails??

  • Noel

    Hi Zech,

    Yep, funny thing you mention it, I was just working on one of the display designs for featured events. If I manage to stay up a bit, I might even get the tutorial out today.



  • Excellent work, might look to put this into a design soon.

  • Steve

    Came across your tutorial whilst looking for a simple an events/calendar plugin solution. Gave up on this as they were either too bloated with extra features I didn’t want or took an approach which made it difficult to beat them into submission with css.

    Now used your approach and plugged this in to the jQuery plugin ‘FullCalendar’ so I can pull out the events and give them to the calendar as a jason encoded array.

    Thanks for a very useful tutorial – be interested to see where you take this.

  • Thanks, will stay up until i see the second part of the tutorial, I’m in Arusha, Tanzania and i know we have different time zones.

  • Noel

    Hi All,

    Glad this tutorial has worked out for you all!



  • Thanks Noel,
    lots of good things in your tutorial.

  • Tazek

    Hi Noel,

    Excellent tutorial!

    I’d like to populate the jQuery UI Calendar with all events that are registered.
    Do you have any idea about how to do it?

    Thank you!

  • Great article, I know where I can use.

    Thank you Noel

  • Noel

    Hi Tazek,

    Thank you! Sorry, I don’t have a clue to be honest, I haven’t delved much into the entire ui datepicker functionality.



  • Pingback: WordPress Community Links: talk about WordPress from Tumblr edition | WPCandy()

  • smallitude

    Hello Noel,

    Great tutorial. I’m not much of a coder, can you tell me how can i add an existent metabox to a post type?

    I see the box in normal posts and i’d like to make it appear in the custom posts.


  • Noel

    Hi there,

    You can add the box by defining the boxes you want to see when you register the custom post type, one of the arguments being, ‘supports’, i.e. : ‘supports’ => array(‘title’,’editor’,’author’,’thumbnail’,’excerpt’,’comments’). Check out the codex for more info on this..



  • Steve N

    Noel – thank you SO much for these two tutorials. I’ve been Googling morosely for the last couple of weeks, looking for exactly this information and here it is! I’ve been trying to develop a “least barrier to entry” theme for churches that want a website but that don’t have resources within their congregation to develop one. A calendar is vital for a church but I want to integrate a simple one so they aren’t intimidated by managing plugins (and the good event calendar plugins are so feature-rich they are even more intimidating). If the other Steve a few comments up swings by again, I’d love to know more about the json approach to integrating and populating FullCalendar. I’ve used that with great success on a Rails app but I’m still pretty early in my WordPress journey.
    Thanks again, Noel. I’m thrilled, and grateful!

  • Noel

    Hi Steve,

    Glad you found this helpful and thank you for the kind comments! I’ve been so busy I haven’t had a chance to get the remaining posts up, but will do so in due time :)

    Good call on FullCalendar, would definitely be interesting (and I can’t imagine it being that hard), I’ll add that to the list.



  • squarecandy

    Awesome – I’ve been trying to wrap my brain around this since 3.0 thanks for laying it all out so nicely!

  • squarecandy

    Hey Noel –

    Inspired by your amazing post – here is my “variations on a theme”:

    This is aimed at the needs of my musician clients:
    Many of their events don’t have a specific end time/date and they sometimes need single day events with no time (“all-day” events) or a series of days with no time display like a festival or something like that…

    Thanks again for sharing your work on this!

    I’m 100% with you on the tactic of building this stuff into a custom theme just the way you want it when possible – instead of relying on a third party plugin, where updates, etc might break the work you’ve done.

  • Noel

    Hi Peter,

    Glad it worked out! I’m assuming your all-day events or series of days will be better solved through a fullcalendar front-end display, I’ll have a look at that for the next tutorial ;)



  • Noel,
    This is exactly what I was looking for. Very very good tutorial. Thank you for your time in writing this.

  • Also, I should note that the trailing comma in your jQuery initiation of the datepicker will cause breakage in IE. This superfluous comma should be removed.

    (Line 8 of pubforce-admin.js)

  • Hey there,

    Thank you very much for that tutorial, I quite needed it !

    I have a little problem with the UI Datepicker function. It doesn’t work, and also it breaks the image uploader from WP (it opens a new windows). If I remove that function, works fine, without the datepicker ofc.

    Any ideas ? The urls are good, already checked..

    Again, thanks !

  • Noel

    Hi Skylar & Romain,

    Thank you both for the kind comments! I had updated it in my base files but failed to reflect it here (i.e. the download file). Romain, I removed the deregister core UI line to remedy that, not perfect, but worked on every installation I tried it with (i.e. no more trouble).

    Hope that helps!



  • Pingback: Where are the best tutorials on using Custom Post types in WordPress 3.0? - Quora()

  • Nice instructions.

    Have you checked into Event Espresso? It’s a great WordPress Event Plugin with free and premium versions and a free test drive:

    The plugin offers custom registration forms, payment options, attendee management, recurring events, soon to offer multi-event registration where attendees can register for more than one event at a time, and more!

    Isn’t it amazing what can be done with WordPress?

  • Noel

    Hi Garth,

    Shameless plug, but I guess anything goes for a click these days :) I actually mention in the article that there are deeper plug-ins out there, and that the goal of this tutorial was to create something simple. In other words, for those seeking something that is *not* that complex and can be incorporated *directly* into the theme files. Good luck with the sales.



  • Adam

    Great post,
    The only part I can’t follow (so far) was where you added the call to ui.calendar.js
    I know that wordpress has many of the jquery ui js files included, and I’m trying to include .tabs

    If you could clarify that section I’d really appreciate it.

    Thanks so much for taking the time to post this on the web.

  • Adam

    Answered my own question after downloading the source code, I guess I just missed that part in the tutorial.

    Thanks again.

  • Noel

    Sorry for the delay Adam, good to hear everything panned out though :)



  • Hey thanks for the excellent tutorial!

    Just wanted to add in a couple things that others may be looking to do.

    I am using this in a child theme and was having trouble figuring out how to implement the path for the datepicker and ultimately eliminated the button image altogether.

    One of the arguments you can use in the jQuery script that calls the datepicker is to showOn: focus. That will pop up the datepicker as soon as the filed is clicked on.

    To do that just replace ‘button’ with ‘focus’ in the pubforce-admin.js file. You can also remove the other lines referring to the image.

    This also makes the content of the meta box fit better if you want it on the side.

    One more tweak I added was to give the meta-box a context of ‘side’ and a priority of ‘high’ which automatically places the meta box in the sidebar and right under the publish box. This is done in the function to add the meta box.

    Just add in:

    , ‘side’, ‘high’

    at the end of the function call. The function would then look like this:

    function tf_events_create() {
    add_meta_box(‘tf_events_meta’, ‘Event Date & Time’, ‘tf_events_meta’, ‘tf_events’, ‘side’, ‘high’);

    Thanks again for the awesome tutorial!

  • Cool. this is a good job Noel. On like 314 of cpt-events.php there is a curly bracket, i don’t think it belongs there. Gives me an error ?>

  • Hi,

    Works fine now ! Thanks Noel :)


  • Pingback: Helpful Tips, Hacks and Tutorials about WordPress - Wordpress Arena()

  • Really cool guide to coding with wordpress. I’ve recently wrote a post on the 2 hour coding challenge – building an autofollower for Twitter. Check it out:

  • Tengil

    Thanks for the how-to. I just made the end date/time optional then I was ready to press.

  • Thanks for the detailed instructions, Noel!

    I’ll confirm what @Vezu said… line 314 of the cpt-events.php seems to have an extra ‘}’. My site wouldn’t show up until I removed it.

  • @Noel

    Thank you for sharing it with us :)


    Yes, I had the same problem so I have removed it. It’s about the last curly bracket in the code.

  • Wow! Just great!
    Thanx (squarecandy too ;-)

  • Hey noel,

    I’m facing a new problem I didn’t have before, on a new fresh WP install.

    Whatever is the date I enter in my event, when I click on save, it’s putting back the default “january 1st 1970 date”. I’ve been using this custom post type already on another install, and didn’t have the problem. Could it be because the new install is running 3.1.2 ? (other one 3.1.1)


  • Pingback: Sin tiempo para escribir.106, Carrero()

  • lauritz

    thanks for the great tutorial. i have a funny one for you: i have scheduled events. if the scheduled date was missed (event is over) the wordpress admin shows me a “Missed schedule” notice. is there a way to automatically publish an event when it is over (thinking about an events archive)?

  • Tom

    I’m going to look like a complete fool saying this, but where do the cpt-events php files typically go? I tried adding them to the functions.php file, and adding them to a functions.php file by itself, but that only seemed to generate an error.

    Any help is appreciated. Thanks!

  • thank you for the helpful post as i try to wrap my head around custom post types.

    i’m actually working on integrating an events custom post type as well and was wondering if you could chime in on this issue:

    i’d like to be able to update the categories for a custom post type WITHOUT using the normal categories admin widget.

    for example, i have the option to select a venue from a dropdown list and that selection is stored in a variable. when i publish/save the post, how do i save that variable as the (only) category for the post?

    i would assume i’d have to use the wp_set_post_categories function in combo with wp_update/insert_post?

    any ideas would be much appreciated.

  • Jared P

    Noel, thank you for this incredible tutorial! It is the perfect solution for a theme that I am working on in which I need to create news, events and a calendar.

    After finishing part I, I am getting the following error:

    Notice: Undefined index: tf_events_startdate in …/wp-content/themes/themename/functions.php
    Notice: Undefined index: tf_events_enddate in …/wp-content/themes/themename/functions.php

    The lines of code producing the error are within the function: tf_events_meta ()
    $meta_sd = $custom[“tf_events_startdate”][0];
    $meta_ed = $custom[“tf_events_enddate”][0];

    The error pops up in the Add event section, right above the start/end date and time section.

    Any thoughts on what I’m doing incorrectly? Thanks again for this tutorial and all the greta help you provide in your comments section.

  • Jared P

    In the Add New event section I’m also getting:

    Notice: Undefined index: tf-events-nonce in /wp-content/themes/themename/functions.php on line 325

    Notice: Trying to get property of non-object in /wp-content/themes/themename/functions.php on line 326

  • Andy

    First off, great tutorial – thanks for sharing. Is there any way at all to have recurring events? I’m trying to find the best way for displaying theatre show dates for a website I’m doing. I really like the approach of having every event item as a custom post – how ever I need to be able to display it several times and at different dates.

    Could this be possible with some modification and your approach? Every plugin out there either does not do what I’m trying to achieve or is bloated.

  • Charles

    This is most likely covered in the codex, but I’m pretty new to all of this and a little overwhelmed. I added the contents of your cpt-events.php to my theme’s functions.php file. It works (after getting rid of that extra ‘ } ‘ that was mentioned earlier by another poster). However, my functions file is pretty bloated as it is. What do I need to do to keep your file separate and have it execute, a few lines of code that direct to your file perhaps?


  • Charles

    You can delete my last comment. I read Justin Tadlock’s excellent tutorial on how to turn all of this into a plugin. Works like a charm. Thanks again. Great post.

  • Pingback: links for 2011-08-05 - CreativeArtGroup - Blog()

  • David

    Any chance you could provide a follow-up that shows how to add a new metabox field for Location, for example?

    Cheers for your work on this so far, very helpful.

  • Marlon

    Amazing! Thanks for sharing! Very good for learning PHP and WP.

  • Noel

    Thanks for all the great feedback! It’s a little too hectic at the moment, but here a few comments:

    @ Nonce issue – I’m using WP 3.0+ on a regular Apache / SQL Box.
    @ Date is 1970 – I’m using WP 3.0+ on a regular Apache / SQL Box. Code will not function on an IIS box.
    @ Recurring Events – Whole different ballgame, and probably a whole lot more code :)

    If anyone wants to see an updated version of this, please check out this github page:


  • Marlon

    Is there a way to translate the dates?

  • Great plugin!
    I ran into some errors with debugging turned on.
    One thing I tried was:

    if ( isset( $_POST[‘tf-events-nonce’] ) && ! wp_verify_nonce( $_POST[‘tf-events-nonce’], ‘tf-events-nonce’ ) ) {
    return $post->ID;

    That fixed one error, although I’m not sure if that is the correct way to handle it. After that, I ran into the same errors as Jared P. The fix for that seems to be to remove the $post->ID from current_user_can and add the plural to edit_posts like so:

    if ( !current_user_can( ‘edit_posts’ ) )
    return $post->ID;

    I hope that helps someone!
    Thanks again for the great information!

  • Andy Lav

    what a great tutorial, and i don’t know if you’ve thought of making the start/end fields a date range, just to stop people choosing an end date which is before the start date… based on the jquery example the following should work (if your input fields have ‘id’ attrs to match the ‘name’ attrs):

    var dates = $(“.tfdate”).datepicker({
    dateFormat: ‘D, M d, yy’,
    showOn: ‘button’,
    buttonImage: ‘/yourpath/icon-datepicker.png’,
    buttonImageOnly: true,
    numberOfMonths: 3,
    onSelect: function(selectedDate){
    var option = == ‘tf_events_start_date’ ? ‘minDate’ : ‘maxDate’;
    var instance = $(this).data(‘datepicker’);
    var date = $.datepicker.parseDate(instance.settings.dateFormat||$.datepicker._defaults.dateFormat, selectedDate, instance.settings);
    dates.not(this).datepicker(‘option’, option, date);

  • Andy Lav

    what a great tutorial, and i don’t know if you’ve thought of making the start/end fields a date range, just to stop people choosing an end date which is before the start date… based on the jquery example the following should work (if your input fields have ‘id’ attrs to match the ‘name’ attrs):

    var dates = $(“.tfdate”).datepicker({
    dateFormat: ‘D, M d, yy’,
    showOn: ‘button’,
    buttonImage: ‘/yourpath/icon-datepicker.png’,
    buttonImageOnly: true,
    numberOfMonths: 3,
    onSelect: function(selectedDate){
    var option = == ‘tf_events_start_date’ ? ‘minDate’ : ‘maxDate’;
    var instance = $(this).data(‘datepicker’);
    var date = $.datepicker.parseDate(instance.settings.dateFormat||$.datepicker._defaults.dateFormat, selectedDate, instance.settings);
    dates.not(this).datepicker(‘option’, option, date);

  • Anonymous

    There is some fantastic graphic design in these examples, however I don’t think they do a good job in drawing your eye or attention to a specific message on the page.
    They cause your eye to jot about looking for something to stand out.
    The aspire labs does a good gob as it hits me with a benefit straight away, so I can think i’ll keep reading.
    Most of the others are just examples of design without much marketing.

    However a great display of graphic design.

    web design dubai

  • I get it clear idea about your topic.Above all the points are explained very clearly.

  • Ramesh

    i am using wordpress 3.3, front end didn’t show the time & date came this Thursday, January 1, 1970Thursday, January 1, 1970 the only. its not took current format

  • Sorry with the delays in commenting back, I literally have no time anymore so I don’t get to support these items as much as I may want…

    @ Marlon, Not sure what you mean by translating dates, if you mean using “Monday” in a different language, you sure can. There’s a function for that in the codex, just don’t remember it off the top my head.

    @ John, checking and verifying for nonce certainly makes sense. Thanks for the fixes too!

    @ Andy, good thoughts there, that’s also a nice way to enahcne the datepicker.

    @ Ramesh, seems like there are more issues on that WP install then just the few lines of code above.

  • Anders Kitson

    Everything Works up until Step #6 I enter a new event with a title description and time, but it hangs at post.php with a blank white screen. Not sure what I might be doing wrong

  • Yachzo

    I feel like a total newbie but how do I can’t seem to get the relative path to icon-datepicker.png (buttonImage: ‘/yourpath/icon-datepicker.png’,) to work right. Only works if i set an absolute path for it. What do I put instead of ‘yourpath’ to point to the root of my site.

  • Anonymous

    Great post!

    Just wondering if there’s any way to use the event date as part of the permalink structure instead of just the post_name… I suspect that the EP_PERMALINK thing could do that, but I’ve never seen a good article about it’s use

  • Barbara

    Hi Noel,

    Great tutorial! I’m trying to figure out how to expire the event when the date is passed. I’ve seen some tutorials for expiring posts using a custom field – but it would seem like what I need is to use the End Date and End Time from above somehow and use that as the expiration. Any advice on how that could be done?

  • Rafael

    Yeap, but as Noel mentioned above, the date format must be submited as “D, M d, Y” format.

    To do this you will need convert your date format BEFORE submit.

    1 – change all date(“D, M d, Y”) to the format that you want to display. If you want to change the linguage, use the date_i18n function (“D, d M, Y”) – you can use same parameters that used in date() function.

    2 – convert YOUR date format to the DEFAULT (“D, d M, Y”) format…
    to do this you need to change the function save_tf_events (step 5 – Save Data)

    this code will help you:

    // grab input submited
    $sd_convert = $_POST[“agenda_startdate”];

    // convert to timestamp
    $sd_convert = strtotime( $sd_convert );

    // convert to default (“D, d M, Y”)
    $sd_result = date(“D, M d, Y”, $sd_convert);

    // insert line #23 step 5
    $updatestartd = strtotime ( $sd_result . $_POST[“agenda_starttime”] );

    Do it again with “enddate” input.

    DONT forget to change the calendar dateFormat:

  • Caswell

    Wow this was increddibly helpful. Thanks a thousantimes. I used it to create job offer posts as well, with a little tweaks: taxonomies as job categories, dates can be used for job offer expiration, and thumbnail as recruiter company logo.

  • Antoine

    Great! Thank you for sharing this!!

  • Antoine

    Where do I insert all the files on my WordPress page? In the functions.php? And if yes how do I insert CSS for the Admin part and .js for the Admin part! PLEASE HELP :((

  • Mike

    I got everything working, but now when I logout of wordpress, and go to log back in I get a blank white screen.

  • Mike

    I got everything working, but now when I logout of wordpress, and go to log back in I get a blank white screen.

  • Christopher Welty

    Thank you kindly for this good sir! I can’t wait to give it a try, tis just was I was looking for

  • John

    Hi, I’m having some trouble with this – Everything appears fine when I post an event. But when I go to ‘Events’ it lists no Events, and when I try to view an Event page after creating it it throws a 404 Error. I’ve implemented all of the script, do you know where I might be going wrong?

  • Hi Noel, in our usage of your code, the events are disappearing 1 hour after the start time even if the event continues on for several days. Can you advise what we might be doing wrong? Thanks

  • Is

    Do you have an example where something more than time can be added? For example Contact information or City etc.

  • Daan

    Please note that for the content in the custom columns to show up with WordPress 3.1 or higher, you must change the hook from “manage_posts_custom_column” to “manage_{your_cpt_name_here}_posts_custom_column”.

  • Daan

    Please note that for the content in the custom columns to show up with WordPress 3.1 or higher, you must change the hook from “manage_posts_custom_column” to “manage_{your_cpt_name_here}_posts_custom_column”.

  • I had the same issue. My solution was: In my theme directory, I copied single.php to single-tf_events.php. (Or if you used a different post type name than what Noel is providing in this article, it should be named single-{posttypename}.php.)


  • Dave

    Everything works fine till I get to step 7. After entering step 7 in my functions.php I receive a blank page when going both to the dashboard or actual website. The error I receive is, “The character encoding of the HTML document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the page must to be declared in the document or in the transfer protocol.”

    I have tried updating libraries to see if that was the issue as well as double checking to make sure everything was pointing to the right place. Can anyone help?

  • Emiliano


  • Keisa

    where exactly would I add the very first code? Into functions.php?

  • Keisa

    Would you mind adding what file each code should be located in, Please? I’m a beginner…and I’d love to create my own events instead of using gigpress :(

  • Luke

    I would like to use this, just without the jQuery date picker. I want a plain HTML form elements date picker, identical to the default WordPress publishing date picker. I’m going to have a crack at it, but I’m in a little over my depth. Any pointers would be appreciated.

  • Luke

    The date picker is nice, especially on an iPad 3. However, you said you want to avoid feature float. The date picker adds 4 javascript files and a CSS file for this one minor feature. Falling back to a more “mandraulic” HTML form-based datepicker would make it lighter and faster. Just a wee bit more bothersome for editors creating new events. But it’s a compromise I want to try.

  • Luke

    Well, I just did it. Simply leaving out the jQuery code part falls back to basic HTML form elements without the additional date picker.

    Next, I want to improve those inputs. Make them just like the post publish date inputs. That might be a little harder.

  • Hey Noel: I didn’t see if there was an answer to some of your viewers questions on here so I will give a little shout: @ Jason and John anytime you create a new custom post type you have to go into your admin and under settings / permalinks you simply have to hit save in order to refresh the permalink structure. Visiting the permalink settings flushes the WP_Rewrite rules. It’s these rules that govern where WordPress looks for everything, including CPTs.

  • francesjane

    Hi there, just wondering – now WordPress is on v3.5.1 do you still need to deregister the jquery files and re-register or is the datepicker now included in core? Cheers Fran

  • Amazing tutorial! Really good!

    But I have a problem.

    When you de-register jQuery UI file and add the new js, in my admin page I can’t add and image to the content with the button on the top (add object) and also can’t add a thumbnail image on the modal box (wp charge the adding objects on a iframe instead)

    Please I need your help because I implemented your events custom post type on a client page and I don’t know how I can fix it!

    Thanks and regards!

  • Thank You

  • Looks good.

    But “categorize their events”: I want to work with that events exclusively with standrad post categories and tags. Event a review of an event – what is not a event – should be an event. Understand? Then I let 3) and 4) away, right?

  • Mircea

    How I can display the events by location? . eg. [events location=”London”] to display just events from London

    I want to use shortcodes with different locations in different pages.

    Thank you very much

  • Mel

    Great tutorial! The only thing that I desperately need is to add a normal text custom field (e.g. for Location, or to add a external link)

    I tried to combine it with other tutorials on that matter, somehow dont get it to work. Maybe you have a quicktip?

  • Kasia D’Aquino

    I don’t know if you found an answer but I had the same issue. I noticed there was an error coming up in the included jquery-ui-1.8.9.custom.min.js file. So I commented that line out so it wouldn’t load the file and it fixed the problem. Now both the date picker and the media upload work.

  • Thanks Kasia! yes, at last I found the same solution, it doesn’t like me so much, but it’s the only way XD

  • Grietje Goedkoop

    Very good!!! Thank you so much.

  • Grietje Goedkoop

    In stead of a startdate and end date I use it to scheduled several dates for the same event.
    How can I leave a date field blank? or turn it into the default 1-1-1970?

  • digamber

    Just chaange it to default permalink then rechange it post

  • Margrethe Moxnes

    Great tutorial! Just what I was looking for!

  • Matthias

    a really incredible Tutorial!
    Can someone help me with getting this to work with the possibility to have more than one date per event?! It would be great!!!

  • Mac Dsilva

    Hi Noel,

    Great Tutorial… :)

    Is there a way by which the post event form can be used by anonymous users to post events? These events will appear directly on the Home Page..

  • Lucas Gabriel


    hey I’ve downloaded the plugin and WORKS LIKE A CHARM! the problem resided in the jQuery 1.8.9 version and all so I went to the jquery-ui website and downloaded my own customized version of the 1.11.2 build and now it works JUST FINE!

    thx again 4 the guy that created this tutorial, u r amazing a a hot girl should give u sum serious head for this, u rlly deserve it.

    thx again mate.


  • Arjun Yonjan

    Thanks somebody went without plugin.

  • Kévin Lemonnier
  • Whiz Blogger

    Hello, most importantly thank you for posting on this point
    with awesome clarification. I Have seeking long to locate a decent article on
    how to create a Post in WordPress
    and at long last I got by answer
    with clear clarification .Thanks a considerable measure

  • Theodor Machnich

    Hi Noel, this is a great tutorial, thank you so much.
    Mi Theme is using a pre build custom post type from the Theme Developer and id has already a Event CPT.
    I searching a solution to add a the possibility to support recurring/repeating Events in one field comma separated.
    This means I can setup for the same Event more the on Date.
    My Shows are almost in a row on the same venue, same Time and whit the same content, only the Date is changing.
    To have a field to add this information for this repeating Dates will be great and I think its better to hold the database small.
    What you think, is this possibel whit code, don’t Like to a third party Plugin….

  • 4y’later…

    Couple of options : if(isset()), if(array_key_exists()), reset(), …
    Just check if it’s there first. Prevent the error.

  • Thank You! Very useful )