Adding Social Media Sharing Buttons to a Nikola Site

In November 2011 I migrated my site from Joomla to Nikola.

Unlike Joomla (and, for that matter, Wordpress/Drupal), Nikola is a static site generator (SSG): when adding content to the site I invoke Nikola and it generates a static files for each of the pages in the site (one of which you're reading now).

This provides significant security and performance benefit. There's no PHP in the background calculating what to serve to you, the file is simply loaded from disk and served on request, reducing both response time and attack surface.

However, because responses are not dynamically calculated, there are things that become harder as a page cannot dynamically include something based upon the user's request.

Social Media sharing icons are an example of this.

In a dynamic site, we might update a template to include

<div class="social-icon">
  <?php
   // Broken up for readability on smaller screens
   $t = urlencode($page->title);
   $p = urlencode($_SERVER['REQUEST_URI']);
   $d = "https://www.bentasker.co.uk";
  ?>
   <a href="https://twitter.com/share?text=<?php echo $t; ?>&url=<?php echo $d . $p; ?>&via=bentasker">
     Tweet This
   </a>
</div>

This injects the title of the current page, as well as the path of the page being requested (in practice you'd use one of the CMS's methods rather than the $_SERVER superglobal, but it allows the example to fit into a small screen more easily).

In a SSG we need to use a slightly different approach. Although the underlying principle is similar, rather than acquiring details from the request, we instead need to ask Nikola's templating engine to include specific details from the post so that we can display preconfigured buttons like these:

This post details the process of adding social media sharing icons to Nikola post pages - it's more or less exactly the process I used to add icons to my own site.


Creating a Custom Theme

You might already have a custom theme (a template in Joomla speak), but as Nikola comes with a bunch of built in ones it's also quite possible that you haven't.

You can check the current theme in conf.py

grep "THEME =" conf.py

Then look in the themes directory to see whether a directory exists with that name

ls themes

If not, create a new theme as a child of whichever theme is currently in use

nikola theme -n <new theme name> --parent <theme name> --engine mako

For example, when first setting up this site, I ran

nikola theme -n bootblog4-btasker --parent bootblog4 --engine mako

Nikola will then copy the base files into the new theme.

Enable the theme by editing conf.py and setting THEME to be the name of the theme created above.

THEME = "bootblog4-btasker"

Copying the Post Template in

Although there's a custom theme, it may not yet contain the post template because it's not added to custom themes by default, instead inheriting from the parent theme.

The following command will check whether the template exists and, if not, copy it into the theme (remember to provide your theme name)

if [[ ! -e themes/<new theme name>/templates/post.tmpl ]]
then
    nikola theme -c post.tmpl
fi

You should now have the post.tmpl template in your theme, ready for editing.


Button Types

There are two mechanisms which can be used for social media sharing buttons:

  • Load javascript from each social network to create tweet/like/share buttons
  • Use locally hosted images with a hyperlink

My preference has long been strongly towards the second option, because

  • Buttons load faster
  • A social media outage/issue doesn't block rendering of our site
  • Visitor's privacy is preserved (sites really should avoid embedding active social media content to prevent networks tracking users around the web).
  • Javascript isn't required for the buttons to function
  • Compromise of the social media JS doesn't threaten the security of our visitors

However, using local images does require images to use in the first place.

At time of writing, some of the ones I use on my site are from a single source of freely available images:

The others are

  • Twitter share button: created using the official logo before I found the site above
  • The email button: built with an icon from that site (because they didn't have an explicit share one)
  • The Telegram button: built from the official logo
  • The QQ button: built from official logos

I saved my images into images/social-icons:

ls -1 images/social-icons/
email.jpg
linkedin.jpg
qq.jpg
reddit.jpg
telegram.jpg
tweet_button.jpg
whatsapp.jpg

If you choose a different path (or filenames) remember to amend when we add markup to the template shortly.

If for some reason, you do want to use javascript embeds, the process for adding them is much the same as the one we'll follow for images (you'll just paste in the embed code rather than the markup that I provide).


Adding Social Icons

In order to add the icons/buttons we need to edit post.tmpl (remember to substitute your theme name in).

vi themes/bootblog4-btasker/templates/post.tmpl

Find where in the template you want to add the icons. I opted for just after the </div> following ${post.text()} so that the icons would display at the end of the content:

Template Location

First we create a div to house the share buttons

<div class="socials">
</div>

Within that, we'll need to create an entry for each of the social media sites.

For Twitter we do this

<a href="https://twitter.com/share?text=${post.title()|h}&url=${abs_link(post.permalink())}&via=bentasker"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="twitterimg" 
         loading="lazy"
         src="/images/social-icons/tweet_button.jpg"
         alt="Share this post on Twitter"
         title="Share this post on Twitter"
         >
</a>

(Remember to change the value of via in href to be your Twitter username (you can remove it if you don't have a Twitter account), as well as the path in the image src if necessary).

Within the URL we're calling three functions provided by the templating engine:

  • post.title(): returns the title of the post (the |h tells the engine to HTML encode it)
  • post.permalink(): returns the path to the post
  • abs_link(): converts the path from a relative to an absolute URL by bolting scheme and domain onto the front (those are taken from the config variable SITE_URL).

This will result in output like the following

<a href="https://twitter.com/share?text=Adding%20Social%20Media%20Sharing%20Buttons%20to%20a%20Nikola%20Site&amp;url=https://www.bentasker.co.uk/posts/blog/adding-sm-share-icons-to-a-nikola-site-template.html&amp;via=bentasker"
    target="_blank"
    rel="noopener nofollow"
    >
        <img class="twitterimg" 
             loading="lazy"
             src="../../images/social-icons/tweet_button.jpg"
             alt="Share this post on Twitter"
             title="Share this post on Twitter"
         >
</a>

We use these template functions in the URL of each of the networks that we want to add a share button for:

Reddit:

<a href="https://www.reddit.com/submit?url=${abs_link(post.permalink())}&title=${post.title()|h}" 
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="redditimg" 
         loading="lazy"
         src="/images/social-icons/reddit.jpg"
         alt="Share this post on Reddit"
         title="Share this post on Reddit"
         >
</a>

LinkedIN:

<a href="https://www.linkedin.com/shareArticle?url=${abs_link(post.permalink())}&source=www.bentasker.co.uk"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="linkedinimg" 
         loading="lazy"
         src="/images/social-icons/linkedin.jpg"
         alt="Share this post on LinkedIN"
         title="Share this post on LinkedIN"
         >
</a>

Whatsapp:

<a href="https://api.whatsapp.com/send?text=${abs_link(post.permalink())}"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="whatsappimg"
         loading="lazy"
         src="/images/social-icons/whatsapp.jpg"
         alt="Share this post via Whatsapp"
         title="Share this post via Whatsapp"
        >
</a>

Telegram:

<a href="https://t.me/share/url?url=${abs_link(post.permalink())}&text=${post.title()|h}"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="telegramimg" 
         loading="lazy"
         src="/images/social-icons/telegram.jpg"
         alt="Share this post via Telegram"
         title="Share this post via Telegram"
         >
</a>

Tencent QQ:

<a href="https://connect.qq.com/widget/shareqq/index.html?url=${abs_link(post.permalink())}&title=${post.title()|h}"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="qqimg" 
         loading="lazy"
         src="/images/social-icons/qq.jpg"
         alt="Share this post via QQ"
         title="Share this post via QQ"
         >
</a>

Email:

<a href="mailto:?subject=${post.title()|h}&body=${abs_link(post.permalink())}"
   target=_blank 
   rel="noopener nofollow"
   >
    <img class="emailimg"
         loading="lazy"
         src="/images/social-icons/email.jpg"
         alt="Share this post via Email"
         title="Share this post via Email"
        >
</a>

It is possible to do similar for Facebook, but you first need to create a developer account and acquire an Opengraph App ID. Once you've done that, you'll want the following URL format

https://www.facebook.com/dialog/feed?app_id=<your_app_id>&link=${abs_link(post.permalink())}

I've not included a full Facebook example here because I've not built one (actually, I thought quite hard about whether I even wanted to include Whatsapp).

Signal, unfortunately, doesn't appear to support an URL scheme for sending messages.

Once the markup is put together, we'll have added something like this to our template.

<div class="socials">
    <a href="https://twitter.com/share?text=${post.title()|h}&url=${abs_link(post.permalink())}&via=bentasker" target=_blank rel="noopener nofollow">
        <img class="twitterimg" 
             loading="lazy" src="/images/social-icons/tweet_button.jpg" alt="Share this post on Twitter" title="Share this post on Twitter">
    </a>

    <a href="https://www.reddit.com/submit?url=${abs_link(post.permalink())}&title=${post.title()|h}" target=_blank rel="noopener nofollow">
        <img class="redditimg" 
             loading="lazy" src="/images/social-icons/reddit.jpg" alt="Share this post on Reddit" title="Share this post on Reddit">
    </a>

    <a href="https://www.linkedin.com/shareArticle?url=${abs_link(post.permalink())}&source=www.bentasker.co.uk" target=_blank rel="noopener nofollow">
        <img class="linkedinimg" 
             loading="lazy" src="/images/social-icons/linkedin.jpg" alt="Share this post on LinkedIN" title="Share this post on LinkedIN">
    </a>

    <a href="https://api.whatsapp.com/send?text=${abs_link(post.permalink())}" target=_blank rel="noopener nofollow">
        <img class="whatsappimg"
             loading="lazy" src="/images/social-icons/whatsapp.jpg" alt="Share this post via Whatsapp" title="Share this post via Whatsapp">
    </a>

    <a href="https://t.me/share/url?url=${abs_link(post.permalink())}&text=${post.title()|h}"
    target=_blank 
    rel="noopener nofollow"
    >
        <img class="telegramimg" 
            loading="lazy"
            src="/images/social-icons/telegram.jpg"
            alt="Share this post via Telegram"
            title="Share this post via Telegram"
            >
    </a>

    <a href="https://connect.qq.com/widget/shareqq/index.html?url=${abs_link(post.permalink())}&title=${post.title()|h}"
    target=_blank 
    rel="noopener nofollow"
    >
        <img class="qqimg" 
            loading="lazy"
            src="/images/social-icons/qq.jpg"
            alt="Share this post via QQ"
            title="Share this post via QQ"
            >
    </a>

    <a href="mailto:?subject=${post.title()|h}&body=${abs_link(post.permalink())}" target=_blank rel="noopener nofollow">
        <img class="emailimg"
             loading="lazy" src="/images/social-icons/email.jpg" alt="Share this post via Email" title="Share this post via Email">
    </a>

</div>

Supporting CSS

You'll almost certainly want to add some styling to control how the buttons are displayed. I opted to keep it simple:

.socials {
    text-align: center;
}

.socials img {
    max-width: 100px;
    margin-right: 20px;
    margin-bottom: 20px;
}

This can then be added into the theme's css

vi themes/bootblog4-btasker/assets/css/bootblog.css

Building The Site

Now that all our changes are in place, we just need to invoke Nikola in order to (re)build the site. Because there have been template changes it'll regenerate all pages

nikola build

Posts should now have social sharing icons, which when clicked should lead to a sharing dialog on the relevant social network

Sharing in Twitter


Conclusion

Adding social media sharing button to a Nikola based site is actually fairly straightforward once you know how, it's just that it relies on things which the handbook doesn't cover.

With relatively few steps, we've added social media sharing buttons to our site in a way that respects the privacy and security of visitors.

For those who want to get a little more advanced, information on theming is available, along with information on the variables and functions available in templates.