Ken Muse

My New, Updated Speaking Page

I am always trying to make this site better. Over the last few months I have been able to steadily improve my site’s visibility in Google searches. One feature, however, eluded me. I couldn’t get my speaking dates to consistently show up in the results. I wanted to makes those events easier to discover.

Previously, I implemented support for JSON-LD and the Event schema. This made it easier for systems to scrape my speaking page and gather the event details. Unfortunately, Google needed a little something extra. Turns out that while they can read those details, they will only include them in the search results if you have a single event per page.

Until now, I managed the events as a big YAML file and some Hugo templating. This has worked great for me, but not for Google. My first thought was to use ‘ExecuteAsTemplate’ to generate the individual pages dynamically. This has three problems. First, the caching means that I can’t use ‘hugo server‘ to preview those pages. Once the pages are generated, Hugo won’t automatically rebuild them when the content changes. The second issue is that I wanted the pages to inherit the look and feel for the site. I didn’t want to reimplement the base template design. Finally, Hugo already supports a list view (which is truly what my speaking page is) and a single page view. It’s never good to rebuild functionality already provided by the platform.

To automate some of these changes, I used Powershell scripts to read the YAML file and generate Markdown files for each event. The original YAML was stored in the file’s metadata header. I then migrated the custom code to a template for the list view and created the single page view. Because the original code had a good separation of concerns, it didn’t take much time to migrate my approach. The end result? I now have a page for each event that provides more details. It also allows Google to read the individual events; they are already appearing in search results! The JSON-LD template code exists in a partial, so I use that in both the list page and the individual event pages.

My approach had one more advantage. I added an additional theme template for generating ICS calendar files (index.ics). This allowed me to make the event data available as a download able calendar invite. I’m still fine-tuning that part. 😄

Adding the calendar events to the template consisted of three steps:

  1. Create an index.ics template file in the speaking folder beside the single.html and list.html files. It contains the code for writing the ICS file.

  2. Adding metadata to each event page to tell Hugo to write both an HTML file and a calendar file. Since I already had some automation to generate the pages, I just updated that code to write additional YAML:

    2- HTML
    3- Calendar
  3. Update the single.html theme template to write out the link to the generated calendar file. I have this wrapped in some code that only includes the link if the event date hasn’t passed.

    1{{- with  .OutputFormats.Get “calendar” -}}
    2     <div class=“col-4 offset-1 justify-content-center mt-4>
    3         <a href=“{{ .RelPermalink | safeURL }}” class=“btn btn-primary>
    4             <i class=“fa fa-calendar pt-1 pr-2 btn-primary aria-hidden=“true”></i>
    5             Add this event to your calendar
    6         </a>
    7     </div>
    8{{- end -}}

As you can imagine, separating the display logic from the metadata meant that writing an ICS isn’t much different than outputting the inline JSON-LD event details. Now, each event page also includes an ability to download a calendar invite. You now have an easy way to see all of the event details and a way to add any speaking events you’re interested in to your calendar.

Enjoy the updated site … and thanks for all the support!