A few of my recent projects—like Cooking Vinyl Compilations and ReadABooker—aim to earn a little money via affiliate links. That only works if people actually find the pages, share them, and get decent previews in social apps. In other words: the boring, fragile glue of SEO and social meta tags matters.
As I lined up a couple more sites in the same vein, I noticed I was writing very similar code again and again: take an object with title
, url
, image
, description
, and spray out the right <meta>
tags for Google, Twitter, Facebook, iMessage, Slack, and so on. It’s fiddly, easy to get 80% right, and annoying to maintain across projects. So I pulled it into a small Moo role—MooX::Role::SEOTags
—that any page-ish class can consume and just emit the right tags.
What are these tags and why should you care?
When someone shares your page, platforms read a handful of standardised tags to decide what to show in the preview:
- Open Graph (
og:*
) — The de-facto standard for title, description, URL, image, and type. Used by Facebook, WhatsApp, Slack, iMessage and others. - Twitter Cards (
twitter:*
) — Similar idea for Twitter/X; the common pattern istwitter:card=summary_large_image
plus title/description/image. - Classic SEO tags —
<title>
,<meta name="description">
, and a canonical URL tell search engines what the page is about and which URL is the “official” one.
MooX::Role::SEOTags gives you one method that renders all of that, consistently, from your object’s attributes.
For more information about OpenGraph, see ogp.me.
What it does
MooX::Role::SEOTags
adds a handful of attributes and helper methods so any Moo (or Moose) class can declare the bits of information that power social previews and search snippets, then render them as HTML.
- Open Graph tags (
og:title
,og:type
,og:url
,og:image
, etc.) - Twitter Card tags (
twitter:card
,twitter:title
,twitter:description
,twitter:image
) - Standard SEO:
<title>
, metadescription
, canonical<link rel="canonical">
- A single method to render the whole block with one call
- But also individual methods to give you more control over tag placement
That’s the whole job: define attributes → get valid tags out.
Quick start
Install the role using your favourite CPAN module installation tool.
1 |
cpanm MooX::Role::SEOTags |
Then, in your code, you will need to add some attributes or methods that define the pieces of information the role needs. The role requires four pieces of information – og_title
, og_description
, og_url
and og_type
– and og_image
is optional (but highly recommended).
So a simple class might look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package MyPage; use Moo; with 'MooX::Role::SEOTags'; # minimal OG fields has og_title => (is => 'ro', required => 1); has og_type => (is => 'ro', required => 1); # e.g. 'article' has og_url => (is => 'ro', required => 1); has og_description => (is => 'ro'); # optional niceties has og_image => (is => 'ro'); # absolute URL has twitter_card => (is => 'ro', default => sub { 'summary_large_image' }); 1; |
And then you create the object:
1 2 3 4 5 6 7 |
my $page = MyPage->new( og_title => 'How to Title a Title', og_type => 'article', og_url => 'https://example.com/post/title', og_image => 'https://example.com/img/hero.jpg', og_description => 'A short, human description of the page.', ); |
Then you can call the various *_tag
and *_tags
methods to get the correct HTML for the various tags.
The easiest option is to just produce all of the tags in one go:
1 |
say $page->tags; |
But, for more control, you can call individual methods:
1 2 3 4 |
say $page->title_tag; say $page->canonical_tag; say $page->og_tags; # etc... |
Depending on which combination of method calls you use, the output will look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<title>How to Title a Title</title> <meta name="description" content="A short, human description of the page."> <link rel="canonical" href="https://example.com/post/title"> <meta property="og:title" content="How to Title a Title"> <meta property="og:type" content="article"> <meta property="og:url" content="https://example.com/post/title"> <meta property="og:image" content="https://example.com/img/hero.jpg"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="How to Title a Title"> <meta name="twitter:description" content="A short, human description of the page."> <meta name="twitter:image" content="https://example.com/img/hero.jpg"> |
In many cases, you’ll be pulling the data from a database and displaying the output using a templating system like the Template Toolkit.
1 2 3 4 5 |
my $tt = Template->new; my $object = $resultset->find({ slug => $some_slug }); $tt->process('page.tt', { object => $object }, "$some_slug/index.html"); |
In this case, you’d just add a single call to the <head> of your page template.
1 2 3 4 5 6 7 |
<head> <!-- lots of other HTML --> [% object.tags %] </head> |
A note if you used my earlier Open Graph role
If you spotted MooX::Role::OpenGraph
arrive on MetaCPAN recently: SEOTags
is the “grown-up” superset. It does Open Graph and Twitter and standard tags, so you only need one role. The old module is scheduled for deletion from MetaCPAN.
SEO tags and JSON-LD
These tags are only one item in the SEO toolkit that you’d use to increase the visibility of your website. Another useful tool is JSON-LD – which allows you to add a machine-readable description of the information that your page contains. Google loves JSON-LD. And it just happens that I have another Moo role called MooX::Role::JSON_LD which makes it easy to add that to your page too. I wrote a blog post about using that earlier this year.
In conclusion
If you’ve got even one page that deserves to look smarter in search and social previews, now’s the moment. Pick a page, add a title, description, canonical URL and a decent image, and let MooX::Role::SEOTags
spit out the right tags every time (and, if you fancy richer results, pair it with MooX::Role::JSON_LD
). Share the link in Slack/WhatsApp/Twitter to preview it, fix anything that looks off, and ship. It’s a 20-minute tidy-up that can lift click-throughs for years—so go on, give one of your pages a quick SEO spruce-up today.