{"id":15013405,"url":"https://github.com/veliovgroup/meteor-flow-router-meta","last_synced_at":"2025-04-12T04:43:16.573Z","repository":{"id":146650150,"uuid":"48200806","full_name":"veliovgroup/Meteor-flow-router-meta","owner":"veliovgroup","description":"Reactive meta tags, JavaScript links and CSS for meteor within flow-router-extra","archived":false,"fork":false,"pushed_at":"2024-03-28T00:37:40.000Z","size":104,"stargazers_count":38,"open_issues_count":0,"forks_count":3,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-02-20T17:12:51.295Z","etag":null,"topics":["css","flow-router","js","meta-tags","metadata","meteor","meteor-package","meteorjs","opengraph","reactive-meta-tags","schema-org","seo","seo-meta","seo-middleware"],"latest_commit_sha":null,"homepage":"https://packosphere.com/ostrio/flow-router-meta","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veliovgroup.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"dr-dimitru","custom":"https://paypal.me/veliovgroup"}},"created_at":"2015-12-17T22:07:56.000Z","updated_at":"2024-07-24T12:43:25.000Z","dependencies_parsed_at":"2024-02-11T23:30:18.738Z","dependency_job_id":null,"html_url":"https://github.com/veliovgroup/Meteor-flow-router-meta","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2FMeteor-flow-router-meta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2FMeteor-flow-router-meta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2FMeteor-flow-router-meta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2FMeteor-flow-router-meta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veliovgroup","download_url":"https://codeload.github.com/veliovgroup/Meteor-flow-router-meta/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239918780,"owners_count":19718403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","flow-router","js","meta-tags","metadata","meteor","meteor-package","meteorjs","opengraph","reactive-meta-tags","schema-org","seo","seo-meta","seo-middleware"],"created_at":"2024-09-24T19:44:14.529Z","updated_at":"2025-04-12T04:43:16.560Z","avatar_url":"https://github.com/veliovgroup.png","language":"JavaScript","funding_links":["https://github.com/sponsors/dr-dimitru","https://paypal.me/veliovgroup"],"categories":[],"sub_categories":[],"readme":"[![support](https://img.shields.io/badge/support-GitHub-white)](https://github.com/sponsors/dr-dimitru)\n[![support](https://img.shields.io/badge/support-PayPal-white)](https://paypal.me/veliovgroup)\n\u003ca href=\"https://ostr.io/info/built-by-developers-for-developers?ref=github-flowroutermeta-repo-top\"\u003e\u003cimg src=\"https://ostr.io/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\u003ca href=\"https://meteor-files.com/?ref=github-flowroutermeta-repo-top\"\u003e\u003cimg src=\"https://meteor-files.com/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\n# Reactive `meta`, `link`, and `script` tags\n\nChange meta tags on the fly in [Meteor.js](https://docs.meteor.com/?utm_source=dr.dimitru\u0026utm_medium=online\u0026utm_campaign=Q2-2022-Ambassadors) apps via [`flow-router-extra`](https://github.com/veliovgroup/flow-router) API. This package manages `meta` tags, `script` and `link` via simple router object definitions.\n\n## Features:\n\n- 👷‍♂️ 100% tests coverage;\n- 🎛 Per route, per group, and default (*all routes*) `meta` tags;\n- 🎛 Per route, per group, and default (*all routes*) `script`s;\n- 🎛 Per route, per group, and default (*all routes*) `link`, like CSS files.\n\nVarious ways to set `meta`, `script` and `link` tags, *ordered by priority*:\n\n- `FlowRouter.route()` [*overrides all below*]\n- `FlowRouter.group()`\n- `FlowRouter.globals`\n- Head template `\u003cmeta/\u003e`, `\u003clink/\u003e`, `\u003cscript/\u003e` tags [*superseded by any above*]\n\n## ToC\n\n- [Installation](#install)\n- [Demos](#demos)\n- [ES6 Import](#es6-import)\n- [Related Packages](#related-packages)\n- [API](#api)\n- [Usage](#usage)\n  - [Basic examples](#basic-examples)\n  - [Set `application/ld+json`](#ldjson)\n  - [Use function as value](#use-function-as-value)\n  - [Use function's context](#use-function-context)\n  - [Set CSS and JS per route](#set-css-and-js-per-route)\n  - [Bootstrap configuration](#bootstrap-configuration)\n- [Running tests](#running-tests)\n- [Support this project](#support-this-project)\n\n## Install\n\n```shell\nmeteor add ostrio:flow-router-meta\n```\n\n\u003e [!NOTE]\n\u003e This package implies [`ostrio:flow-router-title`](https://atmospherejs.com/ostrio/flow-router-title) package.\n\n## Demos\n\n- [Real app usage example](https://github.com/veliovgroup/meteor-files-website/blob/master/imports/client/router/router.js#L18)\n- [Demo source](https://github.com/veliovgroup/Meteor-flow-router-meta/tree/master/demo)\n- [Tests](https://github.com/veliovgroup/Meteor-flow-router-meta/tree/master/tests.js)\n\n## ES6 Import\n\n```js\nimport { FlowRouterMeta } from 'meteor/ostrio:flow-router-meta';\n// This library implies ostrio:flow-router-title package, and both can be imported in single line:\nimport { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta';\n```\n\n## Related Packages\n\n`flow-router-meta` performs the best when used with the next packages:\n\n- [flow-router-title](https://github.com/veliovgroup/Meteor-flow-router-title#reactive-page-title) - Change document.title on the fly within FlowRouter-Extra\n- [flow-router-extra](https://github.com/veliovgroup/flow-router#flowrouter-extra) - Carefully extended FlowRouter\n\n## API\n\n- `new FlowRouterMeta(FlowRouter)` — The main `FlowRouterMeta` constructor that accepts `FlowRouter` as the only argument\n\nAfter `new FlowRouterMeta(FlowRouter)` instance is initiated it extends `FlowRouter.router()` and `FlowRouter.group()` methods and `FlowRouter.globals` with support of:\n\n- `meta: Object` — Object with meta-tags\n- `meta: function(params, qs, data) =\u003e object` — Method returning object with meta-tags\n- `link: Object` — Object with link-tags\n- `link: function(params, qs, data) =\u003e object` — Method returning object with link-tags\n- `script: Object` — Object with script-tags\n- `script: function(params, qs, data) =\u003e object` — Method returning object with script-tags\n\n## Usage\n\nYou need to initialize `FlowRouterMeta` and `FlowRouterTitle` classes by passing `FlowRouter` object. Right after creating all your routes:\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\nimport { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta';\n\nFlowRouter.route('/', {\n  action() { /* ... */ },\n  title: 'Title'\n  /* ... */\n});\n\nnew FlowRouterMeta(FlowRouter);\nnew FlowRouterTitle(FlowRouter);\n```\n\n### Basic examples\n\nSet only `name` and `content` attributes on `meta` tag:\n\n```js\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  meta: {\n    name: 'content'\n  }\n});\n// Will generate\n// \u003cmeta name=\"name\" content=\"content\"\u003e\n\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  meta: {\n    'og:title': 'Page title'\n  }\n});\n// Will generate\n// \u003cmeta name=\"og:title\" content=\"Page-title\"\u003e\n```\n\nSet only `rel` and `href` attributes on `link` tag:\n\n```js\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  link: {\n    canonical: 'http://example.com'\n  }\n});\n// Will generate\n// \u003clink rel=\"canonical\" href=\"http://example.com\"\u003e\n\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  link: {\n    rel: 'canonical',\n    href: 'http://example.com'\n  }\n});\n// Will generate\n// \u003clink rel=\"canonical\" href=\"http://example.com\"\u003e\n```\n\nSet multiple attributes on `meta` tag:\n\n```js\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  meta: {\n    uniqueName: {\n      name: 'name',\n      content: 'value',\n      property: 'og:name',\n      itemprop: 'name'\n    }\n  }\n});\n// Will generate\n// \u003cmeta name=\"name\" content=\"value\" property=\"og:name\" itemprop=\"name\"\u003e\n```\n\nSet multiple attributes on `link` tag:\n\n```js\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  link: {\n    uniqueName: {\n      rel: 'name',\n      sizes: 'value',\n      href: 'http://value',\n      type: 'value-type'\n    }\n  }\n});\n// Will generate\n// \u003clink rel=\"name\" sizes=\"value\" href=\"http://value\" type=\"value-type\"\u003e\n```\n\n### ldjson\n\nThis 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.\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\n\nFlowRouter.route('/fourthPage', {\n  name: 'fourthPage',\n  title: 'Fourth Page title',\n  script: {\n    ldjson: {\n      type: 'application/ld+json',\n      innerHTML: JSON.stringify({\n        '@context': 'http://schema.org/',\n        '@type': 'Recipe',\n        name: 'Grandma\\'s Holiday Apple Pie',\n        author: 'Elaine Smith',\n        image: 'http://images.edge-generalmills.com/56459281-6fe6-4d9d-984f-385c9488d824.jpg',\n        description: 'A classic apple pie.',\n        aggregateRating: {\n          '@type': 'AggregateRating',\n          ratingValue: '4',\n          reviewCount: '276',\n          bestRating: '5',\n          worstRating: '1'\n        }\n      })\n    }\n  },\n  action() { /*...*/ }\n});\n```\n\n### Use function as value\n\nProperties of `meta`, `link`, and `script` tags can be a function that will execute upon navigation\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\n\nFlowRouter.route('/routePath', {\n  name: 'routeName',\n  meta: {\n    url: {\n      property: 'og:url',\n      itemprop: 'url',\n      content() {\n        return document.location.href;\n      }\n    }\n  },\n  link: {\n    canonical() {\n      return document.location.href;\n    }\n  }\n});\n```\n\n### Use function context\n\n`data` can get passed from `data()` hook. *Read about [`data`](https://github.com/veliovgroup/flow-router/blob/master/docs/hooks/data.md) hook.*\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\n\nFlowRouter.route('/post/:_id', {\n  name: 'post',\n  waitOn(params) {\n    return [Meteor.subscribe('post', params._id)];\n  },\n  async data(params) {\n    return await Collection.Posts.findOneAsync(params._id);\n  },\n  meta: {\n    keywords: {\n      name: 'keywords',\n      itemprop: 'keywords',\n      content(params, query, data) {\n        return data?.keywords || 'default, key, words';\n      }\n    }\n  },\n  title(params, query, data) {\n    if (data) {\n      return data.title;\n    }\n    return '404: Page not found';\n  }\n});\n```\n\n### Set CSS and JS per route\n\nLoad CSS and JS files on per route and per group basis.\n\n\u003e [!IMPORTANT]\n\u003e Once CSS or JS is loaded there's no way to \"unload\" its code. This package will remove tags from head when navigated to other routes, but contents of loaded JS and CSS files will remain in browser's memory\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\n\n// Set default JS and CSS for all routes\nFlowRouter.globals.push({\n  link: {\n    twbs: {\n      rel: 'stylesheet',\n      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'\n    }\n  },\n  script: {\n    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'\n  }\n});\n\n// Rewrite default JS and CSS, for second route, via controller:\nFlowRouter.route('/secondPage', {\n  name: 'secondPage',\n  action() {\n    return this.render('layout', 'secondPage');\n  },\n  link: {\n    twbs: {\n      rel: 'stylesheet',\n      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/css/bootstrap.min.css'\n    }\n  },\n  script: {\n    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/js/bootstrap.min.js'\n  }\n});\n\n// Unset defaults, via controller:\nFlowRouter.route('/secondPage', {\n  name: 'secondPage',\n  action() {\n    return this.render('layout', 'secondPage');\n  },\n  link: {\n    twbs: null\n  },\n  script: {\n    twbs: null\n  }\n});\n\n// Rewrite default JS and CSS, for route group:\nconst group = FlowRouter.group({\n  link: {\n    twbs: {\n      rel: 'stylesheet',\n      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/css/bootstrap.min.css'\n    }\n  },\n  script: {\n    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/js/bootstrap.min.js'\n  }\n});\n\ngroup.route('/groupPage1', {\n  name: 'groupPage1',\n  action() {\n    return this.render('layout', 'groupPage1');\n  }\n});\n```\n\n### Bootstrap configuration\n\nPush default `meta`, `link`, or `script` tags to `FlowRouter.globals`\n\n```js\nimport { FlowRouter } from 'meteor/ostrio:flow-router-extra';\n\nFlowRouter.globals.push({\n  name: 'routeName',\n  meta: {\n    // \u003cmeta charset=\"UTF-8\"\u003e\n    charset: {\n      charset: 'UTF-8'\n    },\n\n    // \u003cmeta name=\"keywords\" content=\"Awes..\"\u003e\n    keywords: {\n      name: 'keywords',\n      itemprop: 'keywords',\n      content: 'Awesome, Meteor, based, app'\n    },\n\n    // \u003cmeta name=\"description\" itemprop=\"description\" property=\"og:description\" content=\"Default desc..\"\u003e\n    description: {\n      name: 'description',\n      itemprop: 'description',\n      property: 'og:description',\n      content: 'Default description'\n    },\n    image: {\n      name: 'twitter:image',\n      itemprop: 'image',\n      property: 'og:image',\n      content: 'http://example.com'\n    },\n    'og:type': 'website',\n    'og:title'() {\n      return document.title;\n    },\n    'og:site_name': 'My Awesome Site',\n    url: {\n      property: 'og:url',\n      itemprop: 'url',\n      content() {\n        return window.location.href;\n      }\n    },\n    'twitter:card': 'summary',\n    'twitter:title'() {\n      return document.title;\n    },\n    'twitter:description': 'Default description',\n    'twitter:site': {\n      name: 'twitter:site',\n      value: '@twitterAccountName'\n    },\n    'twitter:creator': {\n      name: 'twitter:creator',\n      value: '@twitterAccountName'\n    },\n    'http-equiv': {\n      'http-equiv': 'X-UA-Compatible',\n      content: 'IE=edge,chrome=1'\n    },\n    robots: 'index, follow',\n    google: 'notranslate'\n  },\n  link: {\n    // \u003clink href=\"https://maxcdn.bootstrapcdn.com/...\" rel=\"stylesheet\"\u003e\n    stylesheet: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css',\n\n    // \u003clink rel=\"canonical\" href=\"http://example.com\"\u003e\n    canonical() {\n      return document.location.href;\n    },\n\n    // \u003clink rel=\"image\" sizes=\"500x500\" href=\"http://example.com\"\u003e\n    image: {\n      rel: 'image',\n      sizes: '500x500',\n      href: 'http://example.com'\n    },\n    publisher: 'http://plus.google...',\n    'shortcut icon': {\n      rel: 'shortcut icon',\n      type: 'image/x-icon',\n      href: 'http://example.com'\n    },\n    'icon': {\n      rel: 'icon',\n      type: 'image/png',\n      href: 'http://example.com'\n    },\n    'apple-touch-icon-144': {\n      rel: 'apple-touch-icon',\n      sizes: '144x144',\n      href: 'http://example.com'\n    },\n    'apple-touch-icon-114': {\n      rel: 'apple-touch-icon',\n      sizes: '114x114',\n      href: 'http://example.com'\n    },\n    'apple-touch-icon-72': {\n      rel: 'apple-touch-icon',\n      sizes: '72x72',\n      href: 'http://example.com'\n    },\n    'apple-touch-icon-57': {\n      rel: 'apple-touch-icon',\n      sizes: '57x57',\n      href: 'http://example.com'\n    }\n  },\n  script: {\n    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js',\n    d3: {\n      src: 'https://d3js.org/d3.v3.min.js',\n      charset: 'utf-8'\n    }\n  }\n});\n```\n\n## Running Tests\n\n1. Clone this package\n2. In Terminal (*Console*) go to directory where package is cloned\n3. Then run:\n\n### Meteor/Tinytest\n\n```shell\n# Default\nmeteor test-packages ./\n\n# With custom port\nmeteor test-packages ./ --port 8888\n\n# With local MongoDB and custom port\nMONGO_URL=\"mongodb://127.0.0.1:27017/flow-router-meta-tests\" meteor test-packages ./ --port 8888\n```\n\n## Support this project:\n\n- Upload and share files using [☄️ meteor-files.com](https://meteor-files.com/?ref=github-flowroutermeta-repo-footer) — Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination\n- Use [▲ ostr.io](https://ostr.io?ref=github-flowroutermeta-repo-footer) for [Server Monitoring](https://snmp-monitoring.com), [Web Analytics](https://ostr.io/info/web-analytics?ref=github-flowroutermeta-repo-footer), [WebSec](https://domain-protection.info), [Web-CRON](https://web-cron.info) and [SEO Pre-rendering](https://prerendering.com) of a website\n- Star on [GitHub](https://github.com/veliovgroup/Meteor-flow-router-meta)\n- Star on [Atmosphere](https://atmospherejs.com/ostrio/flow-router-meta)\n- [Sponsor via GitHub](https://github.com/sponsors/dr-dimitru)\n- [Support via PayPal](https://paypal.me/veliovgroup)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveliovgroup%2Fmeteor-flow-router-meta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fveliovgroup%2Fmeteor-flow-router-meta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveliovgroup%2Fmeteor-flow-router-meta/lists"}