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


#1

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?


#2

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


#3

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.


#4

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


<head> code gets injected in <body> instead
#5

@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.


#6

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


#7

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

Thank you


#8

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


#9

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

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


#10

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


#11

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 :


#12

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


#13

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