Reactive meta tags, JavaScript and CSSs
Change meta tags on the fly in Meteor.js apps via flow-router-extra
API. This package can create meta
tags, script
and link
tags as well.
Features:
- 👷♂️ 100% tests coverage;
- 🎛 Per route, per group, and default (all routes)
meta
tags; - 🎛 Per route, per group, and default (all routes)
script
s; - 🎛 Per route, per group, and default (all routes)
link
, like CSS files.
Various ways to set meta
, script
and link
tags, ordered by prioritization:
FlowRouter.route()
[overrides all below]FlowRouter.group()
FlowRouter.globals
- Head template
<meta/>
,<link/>
,<script/>
tags [might be overridden by any above]
ToC:
- Installation
- Demo application
- Set CSS and JS per route
- Set
application/ld+json
- Use function as value
- Use function's context
- Bootstrap configuration
- Other examples
- Support this project
Install:
meteor add ostrio:flow-router-meta
Note: this package implies ostrio:flow-router-title
package.
Demos / Tests:
ES6 Import:
1import { FlowRouterMeta } from 'meteor/ostrio:flow-router-meta'; 2// This library implies ostrio:flow-router-title package, and both can be imported in single line: 3import { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta';
Related Packages:
- flow-router-title - Change document.title on the fly within FlowRouter-Extra
- flow-router-extra - Carefully extended FlowRouter
Usage:
You need to initialize FlowRouterMeta
and FlowRouterTitle
classes by passing FlowRouter
object. Right after creating all your routes:
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2import { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta'; 3 4FlowRouter.route('/', { 5 action() { /* ... */ }, 6 title: 'Title' 7 /* ... */ 8}); 9 10new FlowRouterMeta(FlowRouter); 11new FlowRouterTitle(FlowRouter);
Set CSS and JS per route:
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2 3// Set default JS and CSS for all routes 4FlowRouter.globals.push({ 5 link: { 6 twbs: { 7 rel: 'stylesheet', 8 href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' 9 } 10 }, 11 script: { 12 twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js' 13 } 14}); 15 16// Rewrite default JS and CSS, for second route, via controller: 17FlowRouter.route('/secondPage', { 18 name: 'secondPage', 19 action() { 20 return this.render('layout', 'secondPage'); 21 }, 22 link: { 23 twbs: { 24 rel: 'stylesheet', 25 href: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/css/bootstrap.min.css' 26 } 27 }, 28 script: { 29 twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/js/bootstrap.min.js' 30 } 31}); 32 33// Unset defaults, via controller: 34FlowRouter.route('/secondPage', { 35 name: 'secondPage', 36 action() { 37 return this.render('layout', 'secondPage'); 38 }, 39 link: { 40 twbs: null 41 }, 42 script: { 43 twbs: null 44 } 45}); 46 47// Rewrite default JS and CSS, for route group: 48const group = FlowRouter.group({ 49 link: { 50 twbs: { 51 rel: 'stylesheet', 52 href: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/css/bootstrap.min.css' 53 } 54 }, 55 script: { 56 twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/js/bootstrap.min.js' 57 } 58}); 59 60group.route('/groupPage1', { 61 name: 'groupPage1', 62 action() { 63 return this.render('layout', 'groupPage1'); 64 } 65});
ldjson:
This method uses special property named innerHTML
which set script's content instead of attribute. This method and property can be used in the any other case when you need to set script's contents.
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2 3FlowRouter.route('/fourthPage', { 4 name: 'fourthPage', 5 title: 'Fourth Page title', 6 script: { 7 ldjson: { 8 type: 'application/ld+json', 9 innerHTML: JSON.stringify({ 10 '@context': 'http://schema.org/', 11 '@type': 'Recipe', 12 name: 'Grandma\'s Holiday Apple Pie', 13 author: 'Elaine Smith', 14 image: 'http://images.edge-generalmills.com/56459281-6fe6-4d9d-984f-385c9488d824.jpg', 15 description: 'A classic apple pie.', 16 aggregateRating: { 17 '@type': 'AggregateRating', 18 ratingValue: '4', 19 reviewCount: '276', 20 bestRating: '5', 21 worstRating: '1' 22 } 23 }) 24 } 25 }, 26 action() { /*...*/ } 27});
Use function as value:
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2 3FlowRouter.route('/routePath', { 4 name: 'routeName', 5 meta: { 6 url: { 7 property: 'og:url', 8 itemprop: 'url', 9 content() { 10 return document.location.href; 11 } 12 } 13 }, 14 link: { 15 canonical() { 16 return document.location.href; 17 } 18 } 19});
Use function context:
Read about data
hook.
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2 3FlowRouter.route('/post/:_id', { 4 name: 'post', 5 waitOn(params) { 6 return [Meteor.subscribe('post', params._id)]; 7 }, 8 data(params) { 9 return Collection.Posts.findOne(params._id); 10 }, 11 meta: { 12 keywords: { 13 name: 'keywords', 14 itemprop: 'keywords', 15 content(params, query, data = {}) { 16 return data.keywords; 17 } 18 } 19 }, 20 title(params, query, data = {}) { 21 if (data) { 22 return data.title; 23 } 24 return '404: Page not found'; 25 } 26});
Other examples:
Set only name
and content
attributes on meta
tag:
1FlowRouter.route('/routePath', { 2 name: 'routeName', 3 meta: { 4 name: 'content' 5 } 6});
Set only rel
and href
attributes on link
tag:
1FlowRouter.route('/routePath', { 2 name: 'routeName', 3 link: { 4 rel: 'http://example.com' 5 } 6});
Set multiple attributes on meta
tag:
1FlowRouter.route('/routePath', { 2 name: 'routeName', 3 meta: { 4 uniqueName: { 5 name: 'name', 6 content: 'value', 7 property: 'og:name', 8 itemprop: 'name' 9 } 10 } 11});
Set multiple attributes on link
tag:
1FlowRouter.route('/routePath', { 2 name: 'routeName', 3 link: { 4 uniqueName: { 5 rel: 'name', 6 sizes: 'value', 7 href: 'value', 8 type: 'value' 9 } 10 } 11});
Bootstrap configuration:
1import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; 2 3FlowRouter.route('/routePath', { 4 name: 'routeName', 5 meta: { 6 // <meta charset="UTF-8"> 7 charset: { 8 charset: 'UTF-8' 9 }, 10 11 // <meta name="keywords" content="Awes.."> 12 keywords: { 13 name: 'keywords', 14 itemprop: 'keywords', 15 content: 'Awesome, Meteor, based, app' 16 }, 17 18 // <meta name="description" itemprop="description" property="og:description" content="Default desc.."> 19 description: { 20 name: 'description', 21 itemprop: 'description', 22 property: 'og:description', 23 content: 'Default description' 24 }, 25 image: { 26 name: 'twitter:image', 27 itemprop: 'image', 28 property: 'og:image', 29 content: 'http://example.com' 30 }, 31 'og:type': 'website', 32 'og:title'() { 33 return document.title; 34 }, 35 'og:site_name': 'My Awesome Site', 36 url: { 37 property: 'og:url', 38 itemprop: 'url', 39 content() { 40 return window.location.href; 41 } 42 }, 43 'twitter:card': 'summary', 44 'twitter:title'() { 45 return document.title; 46 }, 47 'twitter:description': 'Default description', 48 'twitter:site': { 49 name: 'twitter:site', 50 value: '@twitterAccountName' 51 }, 52 'twitter:creator': { 53 name: 'twitter:creator', 54 value: '@twitterAccountName' 55 }, 56 'http-equiv': { 57 'http-equiv': 'X-UA-Compatible', 58 content: 'IE=edge,chrome=1' 59 }, 60 robots: 'index, follow', 61 google: 'notranslate' 62 }, 63 link: { 64 // <link href="https://maxcdn.bootstrapcdn.com/..." rel="stylesheet"> 65 stylesheet: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css', 66 67 // <link rel="canonical" href="http://example.com"> 68 canonical() { 69 return document.location.href; 70 }, 71 72 // <link rel="image" sizes="500x500" href="http://example.com"> 73 image: { 74 rel: 'image', 75 sizes: '500x500', 76 href: 'http://example.com' 77 }, 78 publisher: 'http://plus.google...', 79 'shortcut icon': { 80 rel: 'shortcut icon', 81 type: 'image/x-icon', 82 href: 'http://example.com' 83 }, 84 'icon': { 85 rel: 'icon', 86 type: 'image/png', 87 href: 'http://example.com' 88 }, 89 'apple-touch-icon-144': { 90 rel: 'apple-touch-icon', 91 sizes: '144x144', 92 href: 'http://example.com' 93 }, 94 'apple-touch-icon-114': { 95 rel: 'apple-touch-icon', 96 sizes: '114x114', 97 href: 'http://example.com' 98 }, 99 'apple-touch-icon-72': { 100 rel: 'apple-touch-icon', 101 sizes: '72x72', 102 href: 'http://example.com' 103 }, 104 'apple-touch-icon-57': { 105 rel: 'apple-touch-icon', 106 sizes: '57x57', 107 href: 'http://example.com' 108 } 109 }, 110 script: { 111 twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js', 112 d3: { 113 src: 'https://d3js.org/d3.v3.min.js', 114 charset: 'utf-8' 115 } 116 } 117});
Running Tests
- Clone this package
- In Terminal (Console) go to directory where package is cloned
- Then run:
Meteor/Tinytest
# Default meteor test-packages ./ # With custom port meteor test-packages ./ --port 8888 # With local MongoDB and custom port MONGO_URL="mongodb://127.0.0.1:27017/flow-router-meta-tests" meteor test-packages ./ --port 8888
Support this project:
- Upload and share files using ☄️ meteor-files.com — Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination
- Use ▲ ostr.io for Server Monitoring, Web Analytics, WebSec, Web-CRON and SEO Pre-rendering of a website
- Star on GitHub
- Star on Atmosphere
- Sponsor via GitHub
- Support via PayPal