Facebook share setup

Hey all, Im trying to setup facebook sharing for my site and I am having no luck with using dynamic data with any of the atmosphere packages. I have tried onrendered to remove and add new meta tags and this little line to refresh the button when traversing through products.

Template.social_div.rendered = function() {
try {
FB.XFBML.parse();
}catch(e) {}
};

FlowRouter.route(’/’, {
action: function(params, queryParams) {
BlazeLayout.render(“main_layout”, {content: “home”});

    $("meta[property='og\\:type']").remove();
    $("meta[property='og\\:url']").remove();
    $("meta[property='og\\:title']").remove();
    $("meta[property='og\\:site_name']").remove();
    $("meta[property='og\\:image']").remove();
    $("meta[property='og\\:description']").remove();


    $('<meta>', {property: 'og:type', content: 'article'}).appendTo('head');
    $('<meta>', {property: 'og:url', content: window.location}).appendTo('head');
    $('<meta>', {property: 'og:title', content: 'pHusion Beauty'}).appendTo('head');
    $('<meta>', {property: 'og:site_name', content: location.hostname}).appendTo('head');
    $('<meta>', {property: 'og:image', content: 'http://ivanthemeh.com/upload/logo.png'}).appendTo('head');
    $('<meta>', {property: 'og:description', content: 'pHusion Beauty Products:  Bonnets, Wraps, Scarves and sleep masks!'}).appendTo('head');

});

Im either not getting the right data when I use the fb debugger or when I do it live It will share the first template and then if I go to the next product it keeps the data from before, unless I refresh the page then it changes to the right…even thought I can see the meta tags changing in the source.

Any hlp would be greatly appreciated. This is the last issue I need to resolve before Im able to make my first meteor site live! :smiley:

1 Like

I would recommend skipping entirely over any of the SEO packages, onRendered/onCreated, spiderable and all of that.

Spiderable is pretty hard to get right and if you do it wrong it can crash your servers for some really hard to debug reasons.

IMO the easiest and best solution is to do server side rendering.

With meteorhacks:ssr and meteorhacks:picker you can do all of that stuff incredibly easily.

Here is the approach that I use in production:

Make sure in your main.html (where ever you keep your head tags) you add:

<meta name="fragment" content="!">

otherwise the crawlers wont know it’s an ajax type site.

Then you can add the following.

<!--  private/layout.html -->
{{{getDocType}}}
<html lang="en">
  <head>
      <meta name="description" content="description"/>
     {{> Template.dynamic template=template data=data}}
  </head>
</html>

Then create the file to help on the server

// server/seo/layout.js
SSR.compileTemplate('seoLayout', Assets.getText('layout.html'));
// Blaze does not allow to render templates with DOCTYPE in it.
// This is a trick to made it possible
Template.seoLayout.helpers({
  getDocType: function() {
    return "<!DOCTYPE html>";
  }
});

Once you have that setup you can then add individually the pages you want SEO on and you can specify exactly how you want the meta tags set.

Example:

<!--  private/users.html -->
<title>{{user.profile.firstname}} {{user.profile.lastname}}</title>
<!-- Open Graph Meta Tags -->
<meta property="og:type" content="website"/>
<meta property="og:title" content="{{user.profile.firstname}} {{user.profile.lastname}}"/>
<meta property="og:description" content="{{userDescription user.profile.briefInfo}}"/>
<meta property="og:site_name" content="Your Website Here"/>
<meta property="og:url" content="https://yoururlhere.com/{{user.username}}"/>
<meta property="og:image" content="{{user.profile.profilePictureURL}}"/>
<!-- just testing this getting in here -->
<!-- Twitter Card Meta Tags -->
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="{{user.profile.firstname}} {{user.profile.lastname}}"/>
<meta name="twitter:description" content="{{userDescription user.profile.briefInfo}}"/>
<meta name="twitter:image" content="{{user.profile.profilePictureURL}}"/>
<meta name="twitter:site" content="@yourhandle"/>
<meta name="twitter:creator" content="@{{userTwitter user.profile.twitterHandle}}"/>

Then you add the server side javascript to give you helpers.

// server/seo/users.js
SSR.compileTemplate('seoUser', Assets.getText('users.html'));
Template.seoUser.helpers({
    userDescription:function(description){
        if(description) {
            return description;
        } else {
            return "default description";
        }
    },
    userTwitter:function(handle){
        if(handle) {
            return handle;
        } else {
            return "default handle"
        }
    }
})

Then after that then you can set up your server side routes.

//server/serverRoutes.js
// ----------------------------------------------------------------------------
// picker definition. Use this to figure out if a crawler is hitting you
// ----------------------------------------------------------------------------
var seoPicker = Picker.filter(function(req, res) {
  var isCrawler = [];
  var string = req.headers['user-agent'];
  isCrawler.push(/_escaped_fragment_/.test(req.url));
  if(string){
      isCrawler.push(string.indexOf('facebookexternalhit') >= 0);
      isCrawler.push(string.indexOf('Slack') >= 0);
      isCrawler.push(string.indexOf('Twitterbot') >= 0);
  }
  return isCrawler.indexOf(true) >= 0;
});

// Indexing user pages
seoPicker.route('/:username', function(params, req, res){
    var user = Meteor.users.findOne({username:params.username});
    var html = SSR.render('seoLayout',{
        template:'seoUser',
        data: {user:user}
    });
    res.end(html);
});

Then any time your route /:username gets hit by a crawler it will automatically send the correct parsable html.

7 Likes

Wow thanks a lot for this, Im going to work on it tonight. If I run into any issues If you dont mind Ill be back. :smiley:

1 Like

Thanks for this - it works.

One thing I am struggling with though is the use of Template.dynamic template=template data=data

It throws the following error for me - any ideas?

W20160215-11:31:17.134(11)? (STDERR) Error: you must specify template argument in UI.dynamic
W20160215-11:31:17.134(11)? (STDERR) at [object Object]._render (packages/meteorhacks_ssr/lib/dynamic.js:5:1)
W20160215-11:31:17.135(11)? (STDERR) at packages/blaze/view.js:425:1
W20160215-11:31:17.135(11)? (STDERR) at Object.Blaze._withCurrentView (packages/blaze/view.js:538:1)
W20160215-11:31:17.135(11)? (STDERR) at Object.Blaze._expandView (packages/blaze/view.js:424:1)
W20160215-11:31:17.135(11)? (STDERR) at [object Object].Blaze._HTMLJSExpander.def.visitObject (packages/blaze/view.js:449:1)
W20160215-11:31:17.135(11)? (STDERR) at [object Object].HTML.Visitor.def.visit (packages/htmljs/visitors.js:61:1)
W20160215-11:31:17.135(11)? (STDERR) at Object.Blaze._expand (packages/blaze/view.js:483:1)
W20160215-11:31:17.135(11)? (STDERR) at Object.Blaze._expandView (packages/blaze/view.js:429:1)
W20160215-11:31:17.135(11)? (STDERR) at [object Object].Blaze._HTMLJSExpander.def.visitObject (packages/blaze/view.js:449:1)
W20160215-11:31:17.135(11)? (STDERR) at [object Object].HTML.Visitor.def.visit (packages/htmljs/visitors.js:61:1)

Take a look here @dagvon

More recently, we’ve had success using DocHead and PreRender. Less coding to get setup. We reset and then rewrite the head dynamically for ever product page. We’ve moved to React from Blaze and invoke the update from DidMount and again on DidUpdate (which doesn’t run often for us).

This worked like a charm for me. No need to worry about prerendering costs againl
Thanks a lot

1 Like

Demo Example. Flow Router includes and SSR implementation