├── .gitignore
├── LICENSE
├── README.md
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Shawn Lim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React Doc Meta
2 | ====================
3 |
4 | Render meta tags on the server & client for ReactJS. You can render meta tags with any kind of attributes / properties.
5 |
6 | Built with [React Side Effect](https://github.com/gaearon/react-side-effect).
7 |
8 | ====================
9 |
10 | ## Installation
11 |
12 | ```
13 | npm install --save react-doc-meta
14 | ```
15 |
16 | Dependencies: React >= 0.12.0
17 |
18 | ## Example
19 |
20 | ```javascript
21 | var App = React.createClass({
22 | render: function () {
23 | var tags = [
24 | {name: "description", content: "lorem ipsum dolor"},
25 | {itemProp: "name", content: "The Name or Title Here"},
26 | {itemProp: "description", content: "This is the page description"},
27 | {itemProp: "image", content: "http://www.example.com/image.jpg"},
28 | {name: "twitter:card", content: "product"},
29 | {name: "twitter:site", content: "@publisher_handle"},
30 | {name: "twitter:title", content: "Page Title"},
31 | {name: "twitter:description", content: "Page description less than 200 characters"},
32 | {name: "twitter:creator", content: "@author_handle"},
33 | {name: "twitter:image", content: "http://www.example.com/image.html"},
34 | {name: "twitter:data1", content: "$3"},
35 | {name: "twitter:label1", content: "Price"},
36 | {name: "twitter:data2", content: "Black"},
37 | {name: "twitter:label2", content: "Color"},
38 | {property: "og:title", content: "Title Here"},
39 | {property: "og:type", content: "article"},
40 | {property: "og:url", content: "http://www.example.com/"},
41 | {property: "og:image", content: "http://example.com/image.jpg"},
42 | {property: "og:description", content: "Description Here"},
43 | {property: "og:site_name", content: "Site Name, i.e. Moz"},
44 | {property: "og:price:amount", content: "15.00"},
45 | {property: "og:price:currency", content: "USD"},
46 | {weirdfield: "something", content: "really really cool", hello:"world", meh: "hahaha"}
47 | ]
48 |
49 | // DocMeta will construct meta tags with properties & values mirroring the above key-value pairs
50 | return (
51 |
52 |
53 |
54 | );
55 | }
56 | });
57 | ```
58 |
59 | On the browser the above will produce:
60 |
61 | ```
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | ```
86 |
87 | Works for nested components too!
88 |
89 | Also, you don't need to wrap components with ```...```, writing `````` should work just fine.
90 |
91 |
92 | ```javascript
93 |
94 | class JoinPage extends Component {
95 | static propTypes = {
96 | status: PropTypes.string,
97 | user: PropTypes.object
98 | }
99 |
100 | render() {
101 | var tags = [
102 | {name: "description", content: "test"}
103 | ]
104 |
105 | // only this meta should render in the DOM
106 | var tags2 = [
107 | {name: "description", content: "test 2"}
108 | ]
109 |
110 | return (
111 |
112 |
113 |
114 |
115 |
116 | );
117 | }
118 | }
119 |
120 | class JoinForm extends Component {
121 |
122 | constructor(props) {
123 | super(props);
124 | this.state = {
125 | };
126 | }
127 |
128 | render(){
129 | var tags = [
130 | {name: "description", content: "a nested doc meta"}
131 | ]
132 |
133 |
134 | return (
135 |
136 |
137 |
Join
138 |
Test
139 |
140 | )
141 | }
142 | }
143 |
144 | ```
145 |
146 | On the browser the above will produce
147 |
148 | ```
149 |
150 | ```
151 |
152 |
153 | ## Server Usage
154 |
155 | If you use it on server, call `DocMeta.rewind()` **after rendering components to string** to retrieve the array of meta tags given to the innermost `DocMeta`. You can then embed this meta tags into HTML page template like this using es6 spread props:
156 |
157 | ```javascript
158 |
159 | //... the rest of the html-document.jsx...
160 |
161 | render() {
162 | const { state, markup, script, css, lang } = this.props;
163 | let metaTags = DocMeta.rewind();
164 |
165 | return (
166 |
167 |
168 | {
169 | metaTags.map((tag, index) =>
170 | )
171 | }
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | );
181 | }
182 |
183 | ```
184 |
185 | Because this component keeps track of mounted instances, **you have to make sure to call `rewind` on server**, or you'll get a memory leak.
186 |
187 | ## TODO
188 |
189 | 1. TEST!
190 |
191 | 2. Better diffing for meta tag changes on the client side
192 |
193 | ## Contributing
194 |
195 | Contributions welcomed! Raise issues / submit pull requests thank you!
196 |
197 | ## Release History
198 |
199 | * 0.1.0 Adapted solution for react-document-title for meta tags
200 |
201 | * 0.1.1 Removed undeclared underscore dependency. Using native array prototype functions instead.
202 |
203 | * 0.2.0 Merged a critical fix that causes memory leaks on _serverMeta
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react'),
4 | Component = require('react').Component,
5 | withSideEffect = require('react-side-effect'),
6 | PropTypes = require('prop-types');
7 |
8 | class DocumentMeta extends Component {
9 | render() {
10 | if (this.props.children) {
11 | return Children.only(this.props.children);
12 | } else {
13 | return null;
14 | }
15 | }
16 | }
17 |
18 | DocumentMeta.propTypes = {
19 | tags: PropTypes.array
20 | }
21 |
22 | /**
23 | * TO HANDLE CHANGE
24 | */
25 | function getMetaFromPropsList(propsList) {
26 | var innermostProps = propsList[propsList.length - 1];
27 | if (innermostProps) {
28 | return innermostProps.tags;
29 | }
30 | }
31 |
32 | function removeMetaNodes() {
33 | if (typeof document !== 'undefined') {
34 | var nodes = document.querySelectorAll('meta[data-doc-meta="true"]');
35 | Array.prototype.slice.call(nodes).forEach(function (node) {
36 | node.parentNode.removeChild(node);
37 | });
38 | }
39 | }
40 |
41 | function insertMetaNode(tag) {
42 | if (tag){
43 | var newNode = document.createElement('meta');
44 | for (var property in tag) {
45 | if (tag.hasOwnProperty(property)) {
46 | newNode.setAttribute(property, tag[property]);
47 | }
48 | }
49 | newNode.setAttribute('data-doc-meta', 'true');
50 | document.getElementsByTagName('head')[0].appendChild(newNode);
51 | }
52 | }
53 |
54 | function insertMetaNodes(tags) {
55 | if (typeof document !== 'undefined' && tags) {
56 | Array.prototype.slice.call(tags).forEach(function (tag) {
57 | insertMetaNode(tag);
58 | });
59 | }
60 | }
61 |
62 | /**
63 | * FUNCTIONS TO EXPORT
64 | */
65 |
66 | function reducePropsToState(propsList) {
67 | var innermostProps = propsList[propsList.length - 1];
68 | if (innermostProps) {
69 | return innermostProps.tags;
70 | }
71 | }
72 |
73 | function handleStateChangeOnClient(meta) {
74 | let _serverMeta = meta ? getMetaFromPropsList(meta) : [];
75 | removeMetaNodes();
76 |
77 | insertMetaNodes(_serverMeta);
78 | }
79 |
80 | module.exports = withSideEffect(
81 | reducePropsToState,
82 | handleStateChangeOnClient
83 | )(DocumentMeta);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-doc-meta",
3 | "version": "0.2.0",
4 | "description": "Render meta tags on the server & client for ReactJS",
5 | "main": "index.js",
6 | "peerDependencies": {
7 | "react": ">=0.12.0 || >=0.13.0-beta.1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/geekyme/react-doc-meta.git"
12 | },
13 | "keywords": [
14 | "react",
15 | "component",
16 | "react-component",
17 | "meta",
18 | "tags",
19 | "jsx"
20 | ],
21 | "author": {
22 | "name": "Shawn Lim",
23 | "email": "shawnlim89@live.com",
24 | "url": "http://github.com/geekyme"
25 | },
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/geekyme/react-doc-meta/issues"
29 | },
30 | "homepage": "https://github.com/geekyme/react-doc-meta",
31 | "devDependencies": {
32 | "react": "^0.13.0-beta.1"
33 | },
34 | "dependencies": {
35 | "react-side-effect": "~1.1.0",
36 | "prop-types": "^15.6.0"
37 | },
38 | "maintainers": [
39 | {
40 | "name": "Shawn Lim",
41 | "email": "shawnlim89@live.com"
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------