skip to Main Content

I was trying to find the right way to implement SEO in your Meteor app, but can’t find any good examples. I feel like what I’m doing is working, but to some extent and could be way better. This is what I’m doing for SEO in my Meteor app:

  1. Packages I use: spiderable, gadicohen:sitemaps, manuelschoebel:ms-seo
  2. Head Tag :
<head>
	<meta charset="UTF-8" />
	<meta http-equiv="Content-Language" content="en-us" />
	<meta name="google" value="notranslate" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
	<meta name="mobile-web-app-capable" content="yes" />
	<meta name="google-site-verification" content="google-verification-id" />
	<meta name="msvalidate.01" content="bing-verification-id" />
</head>
  1. This is what I’m doing using ms-seo package: In seo.js file:
SeoCollection = new Mongo.Collection('SeoCollection');

Meteor.startup(function() {
    if (Meteor.isClient) {
        return SEO.config({
            title: ’title',
            meta: {
                    'description': ’siteDescription',
                    'keywords': ‘keyword, keyword, keyword',
                    'charset': 'utf-8',
                    'site_name': ’siteName',
                    'url':'http://siteName.com',
                    'viewport': 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no',
                    'X-UA-Compatible': 'IE=edge,chrome=1',
                    'HandheldFriendly': 'true',
                    'apple-mobile-web-app-capable' : 'yes',
                    'apple-mobile-web-app-status-bar-style': 'black',
                    'referrer': 'never',
              },
              og: {
                    'title': ’siteTitle',
                    'description': ’siteDescription',
                    'image': 'image.png',
                    'type': 'website',
                    'locale': 'en_us',
                    'site_name': 'siteName',
                    'url': 'http://sitename.com'
              },
            rel_author: 'https://plus.google.com/+appPage/'
        });
    }


    SeoCollection.update(
        {
            route_name: 'homepage'
        },
        {
            $set: {
                route_name: 'homepage',
                title: ’title',
                meta: {
                    'description': ’siteDescription',
                    'keywords': ‘keyword, keyword, keyword',
                    'charset': 'utf-8',
                    'site_name': ’siteName',
                    'url':'http://siteName.com',
                    'viewport': 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no',
                    'X-UA-Compatible': 'IE=edge,chrome=1',
                    'HandheldFriendly': 'true',
                    'apple-mobile-web-app-capable' : 'yes',
                    'apple-mobile-web-app-status-bar-style': 'black',
                    'referrer': 'never',
                },
                og: {
                    'title': ’siteTitle',
                    'description': ’siteDescription',
                    'image': 'image.png',
                    'type': 'website',
                    'locale': 'en_us',
                    'site_name': 'siteName',
                    'url': 'http://sitename.com'
                },
                rel_author: 'https://plus.google.com/+appPage/'
            }
        },
        {
            upsert: true
        }
    );

});

and dynamically using Iron:router –

this.route('page:data', {
    path: '/page',
    onBeforeAction: function() {

      SEO.set({
        title: data.title,
        meta: {
          'description': 'description',
        },
        og: {
          'title': data.title,
          'description': 'description',
          'image': data.image
        }
      })
      
      this.next();
    }
  })
  1. Submitting sitemaps using gadicohen:sitemaps:
sitemaps.add('/items.xml', function() {
  var out = [], pages = Collection.find().fetch();
  _.each(pages, function(page) {
    out.push({
      page: 'page/' + page.encodedUrl,
      lastmod: page.date,
    });
  });
  return out;
});
  1. Using meteor-up to deploy the app, it installs phantomJS. It combined with spiderable package enable google to crawl your app.

Problems I encounter:

  1. Spiderable package works only in Google. It’s true that it has the largest market share, but this way for 30% of SEO traffic from other search engines it works really bad.

  2. I’m not sure if I should have all that stuff in seo.js file also in head tag. I know seo.js overwrites it, but when I request title from url on Reddit, it says no title tag find. It might be similar with other search engines from my perspective. But then there’d be multiple same tags, that is not good either.

  3. What I’m doing good or wrong?

2

Answers


  1. The best way to handle this (atleast on my case), was using prerender.io + manuelschoebel:ms-seo in this way.

    How?

    Installing prerender, here you can use.

    meteor add dfischer:prerenderio
    

    NOTE If you get

    res.send(status, body): Use res.status(status).send(body) instead
    

    You will have to use the npm package itself.

    Now Meta Tags.

    for this you can create a function like this.

    setSEO = function(seoData){
      SEO.set({
              title: seoData.title,
              meta: {
                'description': seoData.description
              },
              og: {
                'title': seoData.title,
                'description': seoData.description,
                'image':seoData.image,
                'fb:app_id':seoData.appId
              }
            });
    };
    

    And then just call it on the onAfterAction hook

     Router.map(function() {
      self.route('route.name', {
        path: '/some',
        template: 'test',
        name: 'route.name',
        onAfterAction: function() {
          setSEO(pageData.seoData);
        }
      });
    });
    

    And thats it, on my side its working on twitter, g+, facebook, linkedin, pinterest.

    Login or Signup to reply.
  2. You might want to consider FlowRouter SSR, which uses server-side rendering for HTTP requests. It generates the entire DOM on the server, and sends it as an initial static HTML <body>, thereby enabling all web spiders to crawl your site, not just Google. After that, your app will just continue to function as a real-time webapp, overriding the initial DOM.

    It also supports subscriptions, thus you can use Mongo collections to render crawable content, too. But unfortunately it only works with React for now.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search