HaskellYesod

Adding Twitter summary card in Yesod

Posted on by

In this blog post we are referring to Twitters Card Meta Tags. As you can see, we have two required fields and a bunch of optional fields. So, how can we add those values in Yesod inside our handler?

Let's write a little helper module which we can use in any handler we want to:

module Ersocon.Metatags where

import Prelude
import Yesod


setTwitterSummary (MonadWidget m) => Text -> Text -> m ()

We define one simple function which will accept two Text values and result in a MonadWidget. In this case the text values reflect the required fields of the Twitter Card definition (this will be our minimal example we would like to start with). Let's add the implementation which will make use of Yesods toWidgetHead function:

setTwitterSummary (MonadWidget m) => Text -> Text -> m ()
setTwitterSummary title description = do
  toWidgetHead([hamlet|<meta name=twitter:card content=summary>|])
  toWidgetHead([hamlet|<meta name=twitter:title content=#{title}>|])
  toWidgetHead([hamlet|<meta name=twitter:description content=#{description}>|])

Neat! Very straight forward, isn't it? The only pitfall of toWidgetHead is the fact that it will add more and more tags if you call setTwitterSummary multiple times. It has no checks for already existent tags (like Yesods setTitle has). Please keep this in mind.

In the next step, let's use this function in our handler code:

module Ersocon.SmapleHandler where

import Import
import Ersocon.Metatags

getBlogPostR :: BlogPostId -> Handler Html
getBlogPostR blogPostId = do
  blogPost <- runDB $ get404 blogPostId

  defaultLayout $ do
    setTitle (blogPostTitle blogPost)
    setTwitterSummary (blogPostTitle blogPost) (blogPostDescription blogPost)
    $(widgetFile "blog/blog-post")

This could be a very simple example, assuming you have BlogPost model which always has a title and a description. Furthermore we expect title and description to be of type Text. But what if the description field is optional and a Textarea, like this:

BlogPost
  uuid Text
  title Text
  description Textarea Maybe
  UniqueBlogPostUuid uuid
  Primary uuid 

We can't change our setTwitterSummary code since both fields are required. Let's rewrite our handler code to meet the new requirements (we will substitute an empty description with our title):

getBlogPostR :: BlogPostId -> Handler Html
getBlogPostR blogPostId = do
  blogPost <- runDB $ get404 blogPostId

  defaultLayout $ do
    setTitle (blogPostTitle blogPost)
    case blogPostDescription blogPost of
      Just description ->
        setTwitterSummary (blogPostTitle blogPost) (unTextarea description)
      Nothing ->
        setTwitterSummary (blogPostTitle blogPost) (blogPostTitle blogPost)
    $(widgetFile "blog/blog-post")

You can add images and any other properties in the same way!

I hope this small tutorial was helpful. If you have any questions or comments, please use the area below.