Integrating a rails blog with Facebook instant articles
Publishing blog articles to Facebook
On a recent project, a client asked for a dynamic website and a blog. Since most of their interaction with customers happened through their Facebook Page, they asked for a number of integrations between their new site and Facebook. Aside from the usual like, share, and comment stuff, it was an opportunity to play with the new Instant Article format.
After reading Facebook's documentation, it seemed the simplest way was to create a good old RSS feed and hand it to the Facebook Page. Here is the corresponding article from their documentation.
In this post I will focus only on creating that feed from an existing blog. If you're interested, this minimal example is on GitHub.
Essentially the steps were:
- Create an
instant_articles
controller with anindex
and ashow
action - Create the
show
template with all the markup necessary for Facebook to create an Instant Article. - Create an
index
template for the RSS feed containing every article.
The models
Before diving in, this example will use an Article
and an Author
model with
the following attributes:
Article
belongs_to
an author- Has a
title
, asubtitle
, akicker
, and abody
Thebody
should be in HTML, so you should probably use a WYSIWYG editor if you're building for a client. One caveat is that Facebook basically only parses<p>
and<figure>
tags for the body of the article. In my case I used trix, which defaults to using<div>
tags for the content. It took me some time to figure out why Facebook was complaining that the Instant Articles were empty. The solution was to build trix from source with the correct config flag. If you use another editor, YMMV. - Has a
cover_url
and acover_description
For simplicity here, I defined acover_url
method on theArticle
model which points to unsplash.it. Attaching an image to a Rails model is beyond the scope of this post. Checkout Paperclip or Carrierwave if you need that.
Author
has_many
articles- has a
name
and abio
The controller
# app/controllers/instant_articles
class InstantArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
article = Article.find(params[:id])
render layout: false, locals: { article: article }
end
end
Pretty conventional, except for the show
action. We need to pass the article
as a local variable to the render method instead of using an instance variable.
The reason for that will be clear in the index.rss.builder
template where we will need
to render the show
template again.
The show template
This is based on a sample code from Facebook docs. You can find it here.
<!-- app/views/show.html.erb -->`
<!doctype html>
<html lang="en" prefix="op: http://media.facebook.com/op#">
<head>
<title>My Blog | <%= article.title %></title>
<meta charset="utf-8">
<link rel="canonical" href="<%= article_url(article.id) %>">
<meta property="op:markup_version" content="v1.0">
</head>
<body>
<article>
<header>
<h1><%= article.title %></h1>
<h2><%= article.subtitle %></h2>
<time class="op-published" datetime="<%= article.created_at.iso8601 %>">
<%= article.created_at %>
</time>
<address>
<%= link_to article.author.name, article.author %>
</address>
<figure>
<img src="<%= article.cover_url %>" alt="<%= article.cover_description %>">
<figcaption><%= article.cover_description %></figcaption>
</figure>
<h3 class="op-kicker"><%= article.kicker %></h3>
</header>
<%= article.body.html_safe %>
<footer>
<aside>
<p><%= article.author.name %></p>
<%= article.author.bio.html_safe %>
</aside>
</footer>
</article>
</body>
</html>
A few things to note here. First, make sure there is a link to the original
content in the <head>
. Second, Facebook expects <time>
to have its
datetime
attribute in a specific format (ISO 8601).
Again, to make things simple here, I used the created_at
time stamp for the
date, but you'll probably want to create a published_at
attribute instead,
and add another time stamp for updated_at
, so that Facebook knows when an
article is modified.
Now let's wrap this all together and create our RSS feed.
The index template
This is what we want. The goal is to create the same XML structure as this.
# app/views/index.rss.builder
xml.rss version: '2.0', 'xmlns:content' => 'http://purl.org/rss/1.0/modules/content' do
xml.channel do
xml.title 'My Blog'
xml.link articles_url
xml.description 'Instant Articles from my blog.'
xml.language 'en-us'
@articles.each do |article|
xml.item do
xml.title article.title
xml.link article_url(article.id)
xml.content :encoded do
xml.cdata! render(template: 'instant_articles/show', locals: { article: article }, formats: :html, layout: false)
end
xml.guid article_url(article.id)
xml.pubDate article.created_at.iso8601
xml.author article.author.name
end
end
end
end
The magic happens at line 13
, where we render the show
template above.
Watch out for the format of the time stamps, ISO 8601 again.
Takeaway
There are more things you could/should do to complete the integration, such as creating rich media elements, configuring a newsfeed preview, or creating a style. I definitely recommend checking the documentation, it's pretty extensive and it has code samples.
Using the RSS feed to import articles is not optimal if you want immediate feedback on the import process. For that you could use the Instant Articles API. But the RSS feed integrates nicely with the publishing tools, giving you a nice and easy way to edit your Instant Articles on Facebook and scheduling them to be shared on the page.