How to add meta tags in Yesod

Posted on by

So you would like to add meta tags to your Yesod project? Let's say description and keywords. When I started to learn Yesod I thought this is already supported and is as easy as setTitle (which obviously sets the title tag for the page). Unfortunately there is no functions for setDescription and setKeywords (Yesod version 1.4). Here we have to use Yesods toWidgetHead. Let's define the mentioned functions on our own:

module MyProject.Html where

import Import
import Data.Text (Text)

setDescription :: (MonadWidget m) => Text -> m ()

setKeywords :: (MonadWidget m) => [Text] -> m ()

Looks pretty easy, right? For description, we pass in a Text and for keywords, we pass in a list of keywords. Now we will add the implementation:

module MyProject.Html where

import Import
import Data.Text (Text)
import qualified Data.Text as T

--| Add description meta tag to the head part of the page
setDescription :: (MonadWidget m) => Text -> m ()
setDescription description =
  toWidgetHead([hamlet|<meta name=description content=#{description}>|])

--| Add keywords meta tag to the head part of the page
setKeywords :: (MonadWidget m) => [Text] -> m ()
setKeywords keywords =
  toWidgetHead([hamlet|<meta name=keywords content=#{formatted}>|])
      formatted :: Text
      formatted = T.intercalate ", " keywords


The implementation is very straight forward. By utilizing the toWidgetHead function provided by Yesod we can easily add additional tags to the head of our project. The pitfall of this solution is the fact that we (as developers) need to take care where and how we call those functions. For instance, if we call setDescription two times inside the Handler function the description tag will be added twice to the head as well.

Besides this nothing special is used to implement meta tags. Since setKeywords accepts a list of keywords we glue them together to one single Text. Let's have a look how to use those functions:

import MyProject.Html

getSomeResourceR :: Handler Html
getSomeResourceR = do
  -- someData <- rundDB $ ....
  defaultLayout $ do
    setTitle "My Title"
    setDescription "My Description"
    setKeywords ["My","Keywords","Are","Useless"]
    $(widgetFile "someresource")

Done! But hey, wait a minute! What about i18n for descriptions? Good question! Let's implement setDescriptionI (in analogy to setTitleI) by using a message renderer:

--| Add translated description meta tag to the head part of the page
setDescriptionI :: (MonadWidget m, RenderMessage (HandlerSite m) msg) => msg -> m ()
setDescriptionI msg =do
  mr <- getMessageRender
  toWidgetHead([hamlet|<meta name=description content=#{mr msg}>|])

I hope this small tutorial saved you a couple of minutes. If you have any comments, thoughts or questions feel free to use the section below.