Can I add to the <head> tag from the server?

I’m writing a package in order to poke around at some other aspects of Meteor development, and I’ve run into a roadblock. The end-goal is to insert a bit of dynamic JSON data into the <head> tag of an app. Doing this from the client is no big deal, since I can just call document.appendChild(), and then I can see it when I right-click and inspect the page from a browser.

But what I would really like is for it to append that data somehow before the page is rendered, from the server, so that I could right-click and ‘view page source’ and see it in the source code that’s delivered directly to the browser.

I got my fingers somewhat dirty pulling apart Build Plugins before realizing that it didn’t make sense a <head> tag from there, since the data is user-specific. It seems like Meteor.startup() inside the /server directory would be the logical place for it instead, but I don’t see how to append to the existing head if I don’t have access to document. Is there a way to do this from the server’s startup() method, or another route I should look into?

This is likely what you need: https://github.com/meteorhacks/inject-data It is published to atmosphere: https://atmospherejs.com/meteorhacks/inject-data

Thanks, @lassombra! After a bit of finagling, that was exactly what I was looking for. I’ll release the package in a day or two after I get my code cleaned up and tested a bit.

I’ll also point out that Meteor 1.3 added (under the hood) the dynamicHead hook in WebApp.rawConnectHandlers (run before all Meteor handlers).

Server Side Code

WebApp.rawConnectHandlers.use(
  Meteor.bindEnvironment(function (req, res, next) {
    req.dynamicHead = (req.dynamicHead || "") + '<meta name="abernixbot1" content="follow">';
    next();
  })
);

This is similar to what react-leaderboard is doing.

Though, you’re probably best using a package if you found one that works – I believe @arunoda was was going to change those meteorhacks packages to use dynamicHead internally at some point anyhow.

This came about in https://github.com/meteor/meteor/pull/3860

5 Likes

@abernix, that’s brilliant, and a way less hacky solution than I was about to implement. Inject-data offers a lot more flexibility as to exactly where the data is inserted (I could switch it to above head or below body with a small modification of the _hijackWriteIfNeeded() method, for example). But using dynamicHead is a whole lot smaller, and saves me from shipping a modified version of Picker alongside so that it doesn’t interfere with a user’s existing SSR package.

@alexpete See similar dynamicBody, body and head as well, though I’m not sure where all those actually surface. :wink:

@abernix can I dynamically change title tag based on url request with this? Have an example for this?

Thank you

You’re better off just setting it to something basic and setting document.title using JavaScript!

I tried. It doesn’t fit with my purpose, SEO.

I know spiderable. But I just need dynamic title and meta.

You definitely can. Just get the URL they’re requesting and use a conditional statement to set the title.

I add head using picker and injectInitial.

Here is sample of my code :

//server/main.js

import {Inject} from 'meteor/meteorhacks:inject-initial';
import {Picker} from 'meteor/meteorhacks:picker';

Picker.route('/', (params, req, res, next) => {
	const head =
		`
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
		<title>AdminLTE 2 | Starter</title>
		<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
		<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
		<!--[if lt IE 9]>
		<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
		<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
		<![endif]-->		
		`;
		
	injectHead(head, res);		
	
	next();
});

Here is the result of view source :

1 Like

Thanks @agusputra you save me from fighting with spiderable and phantomjs

haha, thanks to meteorhacks for creating the package :slight_smile: