diff --git a/README.md b/README.md
index f9ba2994621374ba9dce50a2ee1cbe44a8ad6127..0ce41812793dcdc9a5d149f4e653fd32c0300a0e 100644
--- a/README.md
+++ b/README.md
@@ -68,37 +68,37 @@ import {MetaTagsContext} from 'react-meta-tags';
 */
 
 app.use((req, res) => {
-    //make sure you get a new metatags instance for each request
-    const metaTagsInstance = MetaTagsServer();
-
-    //react router match
-    match({
-      routes, location: req.url
-    }, (error, redirectLocation, renderProps) => {
-      let reactString;
-
-      try{
-        reactString = ReactDomServer.renderToString(
-        <Provider store={store}> {/*** If you are using redux ***/}
-        {/* You have to pass extract method through MetaTagsContext so it can catch meta tags */}
-          <MetaTagsContext extract = {metaTagsInstance.extract}>
-            <RouterContext {...renderProps}/>
-          </MetaTagsContext>
-        </Provider>
-        );
-      }
-      catch(e){
-        res.status(500).send(e.stack);
-        return;
-      }
-
-      //get all title and metatags as string
-      const meta = metaTagsInstance.renderToString();
-
-      //append metatag string to your template
-      const template = (`
-        <!doctype html>
-        <html lang="en-us">
+  //make sure you get a new metatags instance for each request
+  const metaTagsInstance = MetaTagsServer();
+
+  //react router match
+  match({
+    routes, location: req.url
+  }, (error, redirectLocation, renderProps) => {
+    let reactString;
+
+    try{
+      reactString = ReactDomServer.renderToString(
+      <Provider store={store}> {/*** If you are using redux ***/}
+      {/* You have to pass extract method through MetaTagsContext so it can catch meta tags */}
+        <MetaTagsContext extract = {metaTagsInstance.extract}>
+          <RouterContext {...renderProps}/>
+        </MetaTagsContext>
+      </Provider>
+      );
+    }
+    catch(e){
+      res.status(500).send(e.stack);
+      return;
+    }
+
+    //get all title and metatags as string
+    const meta = metaTagsInstance.renderToString();
+
+    //append metatag string to your layout
+    const htmlStr = (`
+      <!doctype html>
+      <html lang="en-us">
         <head>
           <meta charSet="utf-8"/>
           ${meta}
@@ -107,11 +107,12 @@ app.use((req, res) => {
           <div id="content">
             ${reactString}
           </div>
-        </body>  
-      `);
+        </body>
+      </html>  
+    `);
 
-      res.status(200).send(template);
-    });
+    res.status(200).send(layout);
+  });
 });
 ```
 
@@ -123,6 +124,32 @@ So as per above code we have to do following for server rendering
 4. Extract meta string using renderToString of MetaTagsServer instance
 5. Append meta string to your html template.
 
+#### JSX Layout
+You might also be using React to define your layout, in which case you can use `getTags` method from `metaTagsInstance`. The layout part of above server side example will look like this.
+```jsx
+//get all title and metatags as React elements
+const metaTags = metaTagsInstance.getTags();
+
+//append metatag string to your layout
+const layout = (
+  <html lang="en-us">
+    <head>
+      <meta charSet="utf-8"/>
+      {metaTags}
+    </head>
+    <body>
+      <div id="app" dangerouslySetInnerHTML={{__html: reactString}} />
+    </body>
+  </html>  
+);
+
+const htmlStr = ReactDomServer.renderToString(layout);
+
+res.status(200).send(htmlStr);
+```
+
+
+
 ## Meta Tag Uniqueness
 - The module uniquely identifies meta tag by id / property / name / itemProp attribute.
 - Multiple meta tags with same property / name is valid in html. If you need such case. Define a different id to both so that it can be uniquely differentiate.
diff --git a/dist/react-meta-tags.es.js b/dist/react-meta-tags.es.js
index 859f8842d417e5e9dbf9cddeecb343e82bd21af5..0e9cb83cacc45bea4c3e8fd6efb1ef48da4fa604 100644
--- a/dist/react-meta-tags.es.js
+++ b/dist/react-meta-tags.es.js
@@ -1,5 +1,5 @@
 /**
- * react-meta-tags - 0.6.0
+ * react-meta-tags - 0.7.0
  * Author : Sudhanshu Yadav
  * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license.
  * https://github.com/s-yadav/react-meta-tags
@@ -292,7 +292,13 @@ _defineProperty(MetaTagsContext, "childContextTypes", {
   extract: propTypes.func
 });
 
-var uniqueIdentifiers = ['property', 'name', 'itemprop'];
+var uniqueIdentifiersI = ['property', 'name', 'itemprop'];
+/**
+  Note:
+  1. In server side we will add meta tags and title at last after fitering
+  2. In client we will match and replace meta tagString
+  3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement
+**/
 
 function filterOutMetaWithId(metas) {
   metas = Array.prototype.slice.call(metas || []);
@@ -300,6 +306,7 @@ function filterOutMetaWithId(metas) {
     return !meta.id;
   });
 }
+
 function getDuplicateTitle() {
   return document.head.querySelectorAll('title');
 }
@@ -315,7 +322,7 @@ function getDuplicateMeta(meta) {
   } //for any other unique identifier check if metas already available with same identifier which doesn't have id
 
 
-  return uniqueIdentifiers.reduce(function (duplicates, identifier) {
+  return uniqueIdentifiersI.reduce(function (duplicates, identifier) {
     var identifierValue = meta.getAttribute(identifier);
     return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates;
   }, []);
diff --git a/dist/react-meta-tags.js b/dist/react-meta-tags.js
index 803434ac3790c8e2ca4ed69186b74903fb56cfdf..bfca0680780cb706eebbadfc23940ed2f51dbb07 100644
--- a/dist/react-meta-tags.js
+++ b/dist/react-meta-tags.js
@@ -1,5 +1,5 @@
 /**
- * react-meta-tags - 0.6.0
+ * react-meta-tags - 0.7.0
  * Author : Sudhanshu Yadav
  * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license.
  * https://github.com/s-yadav/react-meta-tags
@@ -298,7 +298,13 @@
     extract: propTypes.func
   });
 
-  var uniqueIdentifiers = ['property', 'name', 'itemprop'];
+  var uniqueIdentifiersI = ['property', 'name', 'itemprop'];
+  /**
+    Note:
+    1. In server side we will add meta tags and title at last after fitering
+    2. In client we will match and replace meta tagString
+    3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement
+  **/
 
   function filterOutMetaWithId(metas) {
     metas = Array.prototype.slice.call(metas || []);
@@ -306,6 +312,7 @@
       return !meta.id;
     });
   }
+
   function getDuplicateTitle() {
     return document.head.querySelectorAll('title');
   }
@@ -321,7 +328,7 @@
     } //for any other unique identifier check if metas already available with same identifier which doesn't have id
 
 
-    return uniqueIdentifiers.reduce(function (duplicates, identifier) {
+    return uniqueIdentifiersI.reduce(function (duplicates, identifier) {
       var identifierValue = meta.getAttribute(identifier);
       return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates;
     }, []);
diff --git a/dist/react-meta-tags.min.js b/dist/react-meta-tags.min.js
index 823ccfb7b07e7b263a9ead5c225d3cddda63c13e..cdcf5f0269db844142bea3420fc6d422dc795de8 100644
--- a/dist/react-meta-tags.min.js
+++ b/dist/react-meta-tags.min.js
@@ -1,5 +1,5 @@
 /**
- * react-meta-tags - 0.6.0
+ * react-meta-tags - 0.7.0
  * Author : Sudhanshu Yadav
  * Copyright (c) 2016, 2018 to Sudhanshu Yadav, released under the MIT license.
  * https://github.com/s-yadav/react-meta-tags
diff --git a/examples/app.js b/examples/app.js
index 2713ca6b8a529fd95de2c799b43c32bce2de44f4..ac8ef5ef949f1e4ca041f84c4c5d4f81904ee007 100644
--- a/examples/app.js
+++ b/examples/app.js
@@ -8,7 +8,53 @@ import {MetaTagsContext} from '../src/index';
 
 import routes from './shared/routes';
 
-const app = express();
+const app = express()
+
+function renderToString(metaTagsInstance, reactString) {
+  const meta = metaTagsInstance.renderToString();
+
+  const html = `
+    <html lang="en">
+      <head>
+        <meta charSet="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <meta http-equiv="x-ua-compatible" content="ie=edge">
+
+        ${meta}
+        <!-- Bootstrap CSS -->
+        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossorigin="anonymous">
+
+      </head>
+      <body>
+        <div id="app">${reactString}</div>
+        <script src="http://localhost:9010/bundle.js"></script>
+      </body>
+    </html>
+  `;
+
+  return html;
+}
+
+function renderToStringFromJSx(metaTagsInstance, reactString) {
+  const tags = metaTagsInstance.getTags();
+  const wholeHtml = (
+    <html lang="en">
+      <head>
+        <meta charSet="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
+        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
+        {tags}
+        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossOrigin="anonymous" />
+      </head>
+      <body>
+        <div id="app" dangerouslySetInnerHTML={{__html: reactString}} />
+        <script src="http://localhost:9010/bundle.js"></script>
+      </body>
+    </html>
+  );
+
+  return ReactDomServer.renderToString(wholeHtml)
+}
 
 app.use((req, res) => {
   match({
@@ -19,46 +65,27 @@ app.use((req, res) => {
       } else if (redirectLocation) {
         res.redirect(302, redirectLocation.pathname + redirectLocation.search)
       } else if (renderProps) {
-          let reactString;
-
-          const metaTagsInstance = MetaTagsServer();
-
-          try{
-            reactString = ReactDomServer.renderToString(
-              <MetaTagsContext extract = {metaTagsInstance.extract}>
-                <RouterContext {...renderProps}/>
-              </MetaTagsContext>
-            );
-          }
-          catch(e){
-            console.log(e);
-            res.status(500).send(e.stack);
-            return;
-          }
-
-          //const meta = metaTagsInstance.renderToString();
-          const meta = '';
-
-          const template = `
-            <!doctype html>
-            <html lang="en">
-            <head>
-              <meta charSet="utf-8"/>
-              <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-              <meta http-equiv="x-ua-compatible" content="ie=edge">
-
-              ${meta}
-              <!-- Bootstrap CSS -->
-              <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossorigin="anonymous">
-
-            </head>
-            <body>
-              <div id="app">${reactString}</div>
-              <script src="http://localhost:9000/bundle.js"></script>
-            </body>
-          `;
-
-        res.status(200).send(template)
+        let reactString;
+
+        const metaTagsInstance = MetaTagsServer();
+
+        try{
+          reactString = ReactDomServer.renderToString(
+            <MetaTagsContext extract = {metaTagsInstance.extract}>
+              <RouterContext {...renderProps}/>
+            </MetaTagsContext>
+          );
+        }
+        catch(e){
+          console.log(e);
+          res.status(500).send(e.stack);
+          return;
+        }
+
+        //const html = renderToString(metaTagsInstance, reactString);
+        const html = renderToStringFromJSx(metaTagsInstance, reactString);
+
+        res.status(200).send(html);
       } else {
         console.log('redirected');
         res.status(301).redirect('/')
@@ -66,6 +93,6 @@ app.use((req, res) => {
     });
 });
 
-app.listen(8070, function() {
+app.listen(8070, () => {
   console.log('Listening on 8070');
 })
diff --git a/lib/meta_tags_server.js b/lib/meta_tags_server.js
index 3f29c5b67c4dfd92cd2651b3053d1aec8b121304..a03b74f5ee79a32cb4d9e81b729ab4ce4d42af86 100644
--- a/lib/meta_tags_server.js
+++ b/lib/meta_tags_server.js
@@ -17,12 +17,18 @@ function MetaTagsServer() {
   var headElms = [];
   return {
     extract: function extract(elms) {
-      headElms.push(elms);
+      if (!(elms instanceof Array)) {
+        elms = [elms];
+      }
+
+      headElms = headElms.concat(elms);
     },
     renderToString: function renderToString() {
+      var filteredElms = (0, _utils.filterAndArrangeTags)(headElms);
+
       var headComponent = _react.default.createElement("div", {
         className: "react-head-temp"
-      }, headElms);
+      }, filteredElms);
 
       var componentStr = _server.default.renderToStaticMarkup(headComponent); //remove wrapper div from string
 
@@ -30,19 +36,10 @@ function MetaTagsServer() {
       componentStr = componentStr.replace(/^<div[^<>]*class="react-head-temp"[^<>]*>(.*)<\/div>$/, function ($1, $2) {
         return $2;
       });
-
-      var _extractMetaAndTitle = (0, _utils.extractMetaAndTitle)(componentStr),
-          _extractMetaAndTitle$ = _extractMetaAndTitle.title,
-          title = _extractMetaAndTitle$ === void 0 ? '' : _extractMetaAndTitle$,
-          metas = _extractMetaAndTitle.metas,
-          _extractMetaAndTitle$2 = _extractMetaAndTitle.canonicalLink,
-          canonicalLink = _extractMetaAndTitle$2 === void 0 ? '' : _extractMetaAndTitle$2,
-          rest = _extractMetaAndTitle.rest;
-
-      var metasStr = (0, _utils.removeDuplicateMetas)(metas).map(function (meta) {
-        return meta._tagString;
-      }).join('');
-      return "\n        ".concat(title, "\n        ").concat(metasStr, "\n        ").concat(canonicalLink, "\n        ").concat(rest, "\n      ");
+      return componentStr;
+    },
+    getTags: function getTags() {
+      return (0, _utils.filterAndArrangeTags)(headElms);
     }
   };
 }
diff --git a/lib/utils.js b/lib/utils.js
index 044301539e2ad958f3663774eae481919d59b0ca..407de8062893d71c73387905ce9d7096a51cb8fa 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -3,8 +3,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.extractMetaAndTitle = extractMetaAndTitle;
-exports.removeDuplicateMetas = removeDuplicateMetas;
+exports.filterAndArrangeTags = filterAndArrangeTags;
 exports.getDuplicateTitle = getDuplicateTitle;
 exports.getDuplicateCanonical = getDuplicateCanonical;
 exports.getDuplicateMeta = getDuplicateMeta;
@@ -12,15 +11,18 @@ exports.appendChild = appendChild;
 exports.removeChild = removeChild;
 exports.getDomAsString = getDomAsString;
 
-function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
+function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
 
-function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
+
+function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
+
+function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
+
+var camelCaseProps = ['itemProp'];
+var uniqueIdentifiersI = ['property', 'name', 'itemprop'];
+var uniqueIdentifiers = uniqueIdentifiersI.concat(camelCaseProps); //case sensitive props is defined in case anyone defined the lowercase prop
 
-var metaRegex = /<meta[^<>]*?=(['"].*?['"]|[^<>]*?)*?\/?>/g;
-var canonicalLinkRegex = /<link[^<>]*?rel=['"]canonical['"].*?(\/>|<\/link>)/g;
-var titleRegex = /<title[^<>]*?>(.*?)<\/title>/g;
-var attributesRegex = /(\S*?)=("(.*?)"|'(.*?)'|([^<>\s]*))/g;
-var uniqueIdentifiers = ['property', 'name', 'itemprop'];
 var uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']);
 /**
   Note:
@@ -29,19 +31,6 @@ var uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']);
   3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement
 **/
 
-function getAttributes(tagString) {
-  var attr = {};
-  if (!tagString) return attr;
-  var match = attributesRegex.exec(tagString);
-
-  while (match !== null) {
-    attr[match[1].toLowerCase()] = match[3] || match[4] || match[5];
-    match = attributesRegex.exec(tagString);
-  }
-
-  return attr;
-}
-
 function filterOutMetaWithId(metas) {
   metas = Array.prototype.slice.call(metas || []);
   return metas.filter(function (meta) {
@@ -49,32 +38,26 @@ function filterOutMetaWithId(metas) {
   });
 }
 
-function extractMetaAndTitle(domString) {
-  var title, canonicalLink;
-  var metas = []; //extract title, only take the last title, remove title from the string
-
-  domString = domString.replace(titleRegex, function (titleStr) {
-    title = titleStr;
-    return '';
-  }); //extract canonical
-
-  domString = domString.replace(canonicalLinkRegex, function (canonicalLinkStr) {
-    canonicalLink = canonicalLinkStr;
-    return '';
-  }); //extract metas
-
-  domString = domString.replace(metaRegex, function (_tagString) {
-    metas.push(_objectSpread({}, getAttributes(_tagString), {
-      _tagString: _tagString
-    }));
-    return '';
+function filterAndArrangeTags(headElms) {
+  var title = null,
+      canonicalLink = null;
+  var metas = [],
+      rest = [];
+  headElms.forEach(function (elm) {
+    var type = elm.type,
+        props = elm.props;
+
+    if (type === 'title') {
+      title = elm;
+    } else if (type === 'link' && props.rel === 'canonical') {
+      canonicalLink = elm;
+    } else if (type === 'meta') {
+      metas.push(elm);
+    } else {
+      rest.push(elm);
+    }
   });
-  return {
-    title: title,
-    metas: metas,
-    canonicalLink: canonicalLink,
-    rest: domString
-  };
+  return [title].concat(_toConsumableArray(removeDuplicateMetas(metas)), [canonicalLink], rest);
 }
 
 function removeDuplicateMetas(metas) {
@@ -87,15 +70,16 @@ function removeDuplicateMetas(metas) {
 
   var _loop = function _loop(i) {
     var meta = metas[i];
-    var id = meta.id;
+    var id = meta.props.id;
     var addMeta = false; //if has id and element with id is not present than always add meta
 
     if (id) {
       addMeta = !addedMeta.id[id]; //for any other unique identifier check if meta already available with same identifier which doesn't have id
     } else {
       addMeta = uniqueIdentifiers.filter(function (identifier) {
-        var existing = addedMeta[identifier][meta[identifier]];
-        return existing && !existing.id;
+        var identifierValue = meta.props[identifier];
+        var existing = addedMeta[identifier][identifierValue];
+        return existing && !existing.props.id;
       }).length === 0;
     }
 
@@ -103,7 +87,7 @@ function removeDuplicateMetas(metas) {
       filteredMetas.unshift(meta); //add meta as added 
 
       uniqueIdentifiersAll.forEach(function (identifier) {
-        var identifierValue = meta[identifier];
+        var identifierValue = meta.props[identifier];
         if (identifierValue) addedMeta[identifier][identifierValue] = meta;
       });
     }
@@ -133,7 +117,7 @@ function getDuplicateMeta(meta) {
   } //for any other unique identifier check if metas already available with same identifier which doesn't have id
 
 
-  return uniqueIdentifiers.reduce(function (duplicates, identifier) {
+  return uniqueIdentifiersI.reduce(function (duplicates, identifier) {
     var identifierValue = meta.getAttribute(identifier);
     return identifierValue ? duplicates.concat(filterOutMetaWithId(head.querySelectorAll("[".concat(identifier, " = \"").concat(identifierValue, "\"]")))) : duplicates;
   }, []);
diff --git a/package.json b/package.json
index aaa52d52bb59cd2fd43df22b6fcb93c9e3ec815c..d20622014cdfd5a23a6158c42eae3241382bfb0e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "react-meta-tags",
   "description": "Handle document meta/head tags in isomorphic react with ease.",
-  "version": "0.6.0",
+  "version": "0.7.0",
   "main": "lib/index.js",
   "author": "Sudhanshu Yadav",
   "license": "MIT",
@@ -25,7 +25,7 @@
     "react-meta-tags"
   ],
   "scripts": {
-    "start": "cross-env nodemon example.js | npm run start-dev",
+    "start": "cross-env npm run start-dev | nodemon example.js",
     "start-dev": "cross-env webpack-dev-server --content-base example/ --config webpack.dev.config.js --watch-poll --progress --inline --hot",
     "bundle": "cross-env NODE_ENV=production rollup -c rollup.config.js && npm run compile",
     "compile": "cross-env babel src --out-dir lib"
diff --git a/src/meta_tags_server.js b/src/meta_tags_server.js
index 136d34342384704593364ccc3b025a3bc93f0d6e..8e53f8d12391da51d6918a56d0b65c27357ec717 100644
--- a/src/meta_tags_server.js
+++ b/src/meta_tags_server.js
@@ -1,16 +1,20 @@
 import React from 'react';
 import ReactDomServer from 'react-dom/server';
-import {extractMetaAndTitle, removeDuplicateMetas} from './utils';
+import {filterAndArrangeTags} from './utils';
 
 function MetaTagsServer(){
-  const headElms = [];
+  let headElms = [];
 
   return {
-    extract: function(elms){
-      headElms.push(elms);
+    extract(elms) {
+      if (!(elms instanceof Array)) {
+        elms = [elms];
+      }
+      headElms = headElms.concat(elms);
     },
-    renderToString: function(){
-      const headComponent = <div className="react-head-temp">{headElms}</div>;
+    renderToString() {
+      const filteredElms = filterAndArrangeTags(headElms);
+      const headComponent = <div className="react-head-temp">{filteredElms}</div>;
       let componentStr = ReactDomServer.renderToStaticMarkup(headComponent);
 
       //remove wrapper div from string
@@ -18,16 +22,10 @@ function MetaTagsServer(){
         return $2;
       });
 
-      const {title = '', metas, canonicalLink = '', rest} = extractMetaAndTitle(componentStr);
-
-      const metasStr = removeDuplicateMetas(metas).map(meta => meta._tagString).join('');
-
-      return `
-        ${title}
-        ${metasStr}
-        ${canonicalLink}
-        ${rest}
-      `;
+      return componentStr;
+    },
+    getTags() {
+      return filterAndArrangeTags(headElms);
     }
   }
 }
diff --git a/src/utils.js b/src/utils.js
index c261ec4804c5840d1d5f43a4de4a5886d5286a5b..8ee2a24e313ae6720d9d1e3aa2f36a7ed1dc09f2 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,8 +1,6 @@
-const metaRegex = /<meta[^<>]*?=(['"].*?['"]|[^<>]*?)*?\/?>/g;
-const canonicalLinkRegex = /<link[^<>]*?rel=['"]canonical['"].*?(\/>|<\/link>)/g;
-const titleRegex = /<title[^<>]*?>(.*?)<\/title>/g;
-const attributesRegex = /(\S*?)=("(.*?)"|'(.*?)'|([^<>\s]*))/g;
-const uniqueIdentifiers = ['property', 'name', 'itemprop'];
+const camelCaseProps = ['itemProp']; 
+const uniqueIdentifiersI = ['property', 'name', 'itemprop']; 
+const uniqueIdentifiers = uniqueIdentifiersI.concat(camelCaseProps); //case sensitive props is defined in case anyone defined the lowercase prop
 const uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']);
 
 /**
@@ -12,56 +10,34 @@ const uniqueIdentifiersAll = uniqueIdentifiers.concat(['id']);
   3. For now we will not support link and other tags properly, they can be added but we will not check for uniqueness and will not decide placement
 **/
 
-function getAttributes(tagString) {
-  const attr = {};
-  if (!tagString) return attr;
-  let match = attributesRegex.exec(tagString);
-  while (match !== null) {
-    attr[match[1].toLowerCase()] = match[3] || match[4] || match[5];
-    match = attributesRegex.exec(tagString);
-  }
-
-  return attr;
-}
-
 function filterOutMetaWithId(metas) {
   metas = Array.prototype.slice.call(metas || []);
   return metas.filter(meta => !meta.id);
 }
 
-export function extractMetaAndTitle(domString) {
-  let title, canonicalLink;
-  const metas = [];
-
-  //extract title, only take the last title, remove title from the string
-  domString = domString.replace(titleRegex, (titleStr) => {
-    title = titleStr;
-    return '';
-  });
-
 
-  //extract canonical
-  domString = domString.replace(canonicalLinkRegex, (canonicalLinkStr) => {
-    canonicalLink = canonicalLinkStr;
-    return '';
-  });
-
-
-  //extract metas
-  domString = domString.replace(metaRegex, (_tagString) => {
-    metas.push({...getAttributes(_tagString), _tagString});
-    return '';
-  });
-
-  return {
-    title,
-    metas,
-    canonicalLink,
-    rest: domString
+export function filterAndArrangeTags(headElms) {
+ let title = null, canonicalLink = null;
+ const metas = [], rest = [];
+
+ headElms.forEach((elm) => {
+  const { type, props } = elm;
+  if (type === 'title') {
+    title = elm;
+  } else if (type === 'link' && props.rel === 'canonical') {
+    canonicalLink = elm;
+  } else if (type === 'meta') {
+    metas.push(elm);
+  } else {
+    rest.push(elm);
   }
+ });
+
+ return [title, ...removeDuplicateMetas(metas), canonicalLink, ...rest];
 }
 
-export function removeDuplicateMetas(metas) {
+
+function removeDuplicateMetas(metas) {
   const addedMeta = {};
   
   //initialize all the identifiers with empty array
@@ -73,7 +49,7 @@ export function removeDuplicateMetas(metas) {
   for (let i = metas.length - 1; i >=0 ; i--) {
     const meta = metas[i];
 
-    const { id } = meta;
+    const { id } = meta.props;
     let addMeta = false;
 
     //if has id and element with id is not present than always add meta
@@ -83,8 +59,9 @@ export function removeDuplicateMetas(metas) {
     //for any other unique identifier check if meta already available with same identifier which doesn't have id
     } else {
       addMeta = uniqueIdentifiers.filter((identifier) => {
-        const existing = addedMeta[identifier][meta[identifier]];
-        return existing && !existing.id;
+        const identifierValue = meta.props[identifier];
+        const existing = addedMeta[identifier][identifierValue];
+        return existing && !existing.props.id;
       }).length === 0;
     }
 
@@ -93,7 +70,7 @@ export function removeDuplicateMetas(metas) {
 
       //add meta as added 
       uniqueIdentifiersAll.forEach((identifier) => {
-        const identifierValue = meta[identifier];
+        const identifierValue = meta.props[identifier];
         if (identifierValue) addedMeta[identifier][identifierValue] = meta; 
       });
     }
@@ -120,7 +97,7 @@ export function getDuplicateMeta(meta) {
   } 
 
   //for any other unique identifier check if metas already available with same identifier which doesn't have id
-  return uniqueIdentifiers.reduce((duplicates, identifier) => {
+  return uniqueIdentifiersI.reduce((duplicates, identifier) => {
     const identifierValue = meta.getAttribute(identifier);
     return (identifierValue ? 
       duplicates.concat(filterOutMetaWithId(head.querySelectorAll(`[${identifier} = "${identifierValue}"]`))) :
diff --git a/webpack.dev.config.js b/webpack.dev.config.js
index 37af3bfd73a01b83ecf12ff4f267de712d8fe430..f0445487441eba00a23132febde17264404e2323 100644
--- a/webpack.dev.config.js
+++ b/webpack.dev.config.js
@@ -12,7 +12,7 @@ module.exports = {
   },
   devServer: {
     //contentBase: path.join(__dirname, 'dist'),
-    port: 9000,
+    port: 9010,
   },
   module: {
     rules: [