Repository: teleporthq/teleport-code-generators
Pull request #341 (2019-08-13 10:57:09)

Feature/stencil project generator
*Stencil Generator* is almost completed, but here are the two limitations that we need to look into

- There are few reserve keywords in Stencil like `title`. we need to make a list and cross-check when the UIDL contains such declarations
- We add attributes directly to the `jsx` tags like 
```
<textaread rows="8" cols="50" />
```
Since we have type-checking enabled in the stencil, binding numeric characters like above is not supported. Instead, it should be

```
<textarea rows={8} cols={50}
```

codecov[bot]: # [Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=h1) Report
> Merging [#341](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=desc) into [master](https://codecov.io/gh/teleporthq/teleport-code-generators/commit/a86f8fd64aae47585da2e65c235af99e8ca774f8?src=pr&el=desc) will **decrease** coverage by `1.63%`.
> The diff coverage is `5.12%`.

[![Impacted file tree graph](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/graphs/tree.svg?width=650&token=LI2WPEFm1R&height=150&src=pr)](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #341      +/-   ##
==========================================
- Coverage   78.51%   76.87%   -1.64%     
==========================================
  Files         104      108       +4     
  Lines        3491     3564      +73     
  Branches      748      753       +5     
==========================================
- Hits         2741     2740       -1     
- Misses        709      783      +74     
  Partials       41       41
```


| [Impacted Files](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...eleport-plugin-stencil-base-component/src/utils.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXN0ZW5jaWwtYmFzZS1jb21wb25lbnQvc3JjL3V0aWxzLnRz) | `91.66% <ø> (-1.67%)` | :arrow_down: |
| [...s/teleport-plugin-stencil-app-routing/src/utils.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXN0ZW5jaWwtYXBwLXJvdXRpbmcvc3JjL3V0aWxzLnRz) | `0% <0%> (ø)` | |
| [...leport-plugin-stencil-app-routing/src/constants.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXN0ZW5jaWwtYXBwLXJvdXRpbmcvc3JjL2NvbnN0YW50cy50cw==) | `0% <0%> (ø)` | |
| [...s/teleport-plugin-stencil-app-routing/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXN0ZW5jaWwtYXBwLXJvdXRpbmcvc3JjL2luZGV4LnRz) | `0% <0%> (ø)` | |
| [...es/teleport-project-generator-stencil/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3Itc3RlbmNpbC9zcmMvaW5kZXgudHM=) | `0% <0%> (ø)` | |
| [...eleport-plugin-stencil-base-component/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXN0ZW5jaWwtYmFzZS1jb21wb25lbnQvc3JjL2luZGV4LnRz) | `96.66% <100%> (+0.11%)` | :arrow_up: |
| [...ackages/teleport-shared/src/utils/ast-jsx-utils.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtc2hhcmVkL3NyYy91dGlscy9hc3QtanN4LXV0aWxzLnRz) | `92.85% <50%> (-3.37%)` | :arrow_down: |
| ... and [1 more](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=footer). Last update [a86f8fd...2bf011a](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/341?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

JayaKrishnaNamburu: @alexnm you can merge this now 👍 🚀 

Patch:
diff --git a/examples/uidl-samples/project.json b/examples/uidl-samples/project.json
index bd8f64f3..aff0a81b 100644
--- a/examples/uidl-samples/project.json
+++ b/examples/uidl-samples/project.json
@@ -37,14 +37,23 @@
             "value": "home",
             "meta": {
               "path": "/",
-              "componentName": "Home"
+              "componentName": "Home",
+              "title": "Home Page"
             }
           },
           {
             "value": "team",
             "meta": {
               "path": "/team",
-              "componentName": "Team"
+              "componentName": "Team",
+              "title": "About Page",
+              "metaTags": [
+                { "name": "description", "content": "What is this about?" },
+                { "name": "keywords", "content": "prea bun, prea ca la tara" },
+                { "name": "viewport", "content": "width=device-width, initial-scale=1.0" },
+                { "property": "og:title", "content": "What is this about?" },
+                { "property": "og:url", "content": "/playground_assets/asdasd.png" }
+              ]
             }
           },
           {
@@ -799,8 +808,8 @@
                         "border-color": "#ccc"
                       },
                       "attrs": {
-                        "rows": "8",
-                        "cols": "30",
+                        "rows": 8,
+                        "cols": 30,
                         "id": "message",
                         "name": "message"
                       }
diff --git a/packages/teleport-component-generator-preact/src/index.ts b/packages/teleport-component-generator-preact/src/index.ts
index 82951754..7bfaeba2 100644
--- a/packages/teleport-component-generator-preact/src/index.ts
+++ b/packages/teleport-component-generator-preact/src/index.ts
@@ -11,7 +11,7 @@ import { createComponentGenerator } from '@teleporthq/teleport-component-generat
 
 import preactMapping from './preact-mapping.json'
 
-import { ComponentGenerator, Mapping } from '@teleporthq/teleport-types'
+import { ComponentGenerator, Mapping, ComponentPlugin } from '@teleporthq/teleport-types'
 
 const cssPlugin = createCSSPlugin({
   templateChunkName: 'jsx-component',
@@ -32,6 +32,7 @@ const stylePlugins = {
 
 export const createPreactComponentGenerator = (
   variation: string = 'CSSModules',
+  plugins: ComponentPlugin[] = [],
   mapping: Mapping = {}
 ): ComponentGenerator => {
   const generator = createComponentGenerator()
@@ -43,6 +44,7 @@ export const createPreactComponentGenerator = (
   generator.addPlugin(preactComponentPlugin)
   generator.addPlugin(stylePlugin)
   generator.addPlugin(proptypesPlugin)
+  plugins.forEach((plugin) => generator.addPlugin(plugin))
   generator.addPlugin(importStatementsPlugin)
 
   generator.addPostProcessor(prettierJS)
diff --git a/packages/teleport-component-generator-react/src/index.ts b/packages/teleport-component-generator-react/src/index.ts
index f0157b29..10539d7c 100644
--- a/packages/teleport-component-generator-react/src/index.ts
+++ b/packages/teleport-component-generator-react/src/index.ts
@@ -14,7 +14,7 @@ import { createComponentGenerator } from '@teleporthq/teleport-component-generat
 
 import reactMapping from './react-mapping.json'
 
-import { ComponentGenerator, Mapping } from '@teleporthq/teleport-types'
+import { ComponentGenerator, Mapping, ComponentPlugin } from '@teleporthq/teleport-types'
 
 const cssPlugin = createCSSPlugin({
   templateChunkName: 'jsx-component',
@@ -36,18 +36,23 @@ const stylePlugins = {
 
 export const createReactComponentGenerator = (
   variation: string = 'CSS',
+  plugins: ComponentPlugin[] = [],
   mapping: Mapping = {}
 ): ComponentGenerator => {
   const stylePlugin = stylePlugins[variation] || cssPlugin
 
   const generator = createComponentGenerator()
 
-  generator.addMapping(reactMapping)
+  generator.addMapping(reactMapping as Mapping)
   generator.addMapping(mapping)
 
   generator.addPlugin(reactComponentPlugin)
   generator.addPlugin(stylePlugin)
   generator.addPlugin(propTypesPlugin)
+  plugins.forEach((plugin) => generator.addPlugin(plugin))
+
+  // Import plugin needs to be last to handle all dependencies
+  // TODO: use a different function to set/interact with the import plugin
   generator.addPlugin(importStatementsPlugin)
 
   generator.addPostProcessor(prettierJS)
diff --git a/packages/teleport-component-generator/src/resolver/index.ts b/packages/teleport-component-generator/src/resolver/index.ts
index 4b91661b..9e90fb5b 100644
--- a/packages/teleport-component-generator/src/resolver/index.ts
+++ b/packages/teleport-component-generator/src/resolver/index.ts
@@ -48,6 +48,9 @@ export default class Resolver {
 
     const name = sanitizeVariableName(uidl.name)
 
+    // There might be urls that need to be prefixed in the metaTags of the component
+    utils.resolveMetaTags(uidl, options)
+
     return {
       ...uidl,
       name,
diff --git a/packages/teleport-component-generator/src/resolver/utils.ts b/packages/teleport-component-generator/src/resolver/utils.ts
index 97f716ba..b90130c8 100644
--- a/packages/teleport-component-generator/src/resolver/utils.ts
+++ b/packages/teleport-component-generator/src/resolver/utils.ts
@@ -17,6 +17,7 @@ import {
   Mapping,
   UIDLStateDefinition,
   GeneratorOptions,
+  ComponentUIDL,
 } from '@teleporthq/teleport-types'
 import { camelCaseToDashCase } from '@teleporthq/teleport-shared/dist/cjs/utils/string-utils'
 import deepmerge from 'deepmerge'
@@ -71,6 +72,18 @@ export const resolveNavlinks = (uidlNode: UIDLNode, routesDefinition: UIDLStateD
   })
 }
 
+export const resolveMetaTags = (uidl: ComponentUIDL, options: GeneratorOptions) => {
+  if (!uidl.meta || !uidl.meta.metaTags || !options.assetsPrefix) {
+    return
+  }
+
+  uidl.meta.metaTags.forEach((tag) => {
+    Object.keys(tag).forEach((key) => {
+      tag[key] = prefixPlaygroundAssetsURL(options.assetsPrefix, tag[key])
+    })
+  })
+}
+
 export const resolveNode = (uidlNode: UIDLNode, options: GeneratorOptions) => {
   traverseNodes(uidlNode, (node, parentNode) => {
     if (node.type === 'element') {
diff --git a/packages/teleport-plugin-angular-base-component/src/constants.ts b/packages/teleport-plugin-angular-base-component/src/constants.ts
index 93f68cb1..d8311188 100644
--- a/packages/teleport-plugin-angular-base-component/src/constants.ts
+++ b/packages/teleport-plugin-angular-base-component/src/constants.ts
@@ -1,3 +1,5 @@
+import { UIDLDependency } from '@teleporthq/teleport-types'
+
 export const DEFAULT_ANGULAR_TEMPLATE_CHUNK_NAME = 'template-chunk'
 
 export const DEFAULT_ANGULAR_TS_CHUNK_NAME = 'angular-ts-chunk'
@@ -6,7 +8,7 @@ export const DEFAULT_ANGULAR_DECORATOR_CHUNK_NAME = 'component-decorator'
 
 export const DEFAULT_TS_CHUNK_AFTER = ['import-lib', 'import-pack', 'import-local']
 
-export const ANGULAR_CORE_DEPENDENCY = {
+export const ANGULAR_CORE_DEPENDENCY: UIDLDependency = {
   type: 'library',
   path: '@angular/core',
   version: '8.1.0',
diff --git a/packages/teleport-plugin-angular-base-component/src/index.ts b/packages/teleport-plugin-angular-base-component/src/index.ts
index 03e2a246..d3677377 100644
--- a/packages/teleport-plugin-angular-base-component/src/index.ts
+++ b/packages/teleport-plugin-angular-base-component/src/index.ts
@@ -5,8 +5,10 @@ import {
   UIDLElementNode,
 } from '@teleporthq/teleport-types'
 import { FILE_TYPE, CHUNK_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import { createComponentDecorator } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-jsx-utils'
+import { getComponentFileName } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 
-import { generateExportAST, generateComponentDecorator } from './utils'
+import { generateExportAST } from './utils'
 
 import {
   DEFAULT_TS_CHUNK_AFTER,
@@ -80,7 +82,11 @@ export const createPlugin: ComponentPluginFactory<AngularPluginConfig> = (config
       linkAfter: [],
     })
 
-    const componentDecoratorAST = generateComponentDecorator(uidl)
+    const params = {
+      selector: 'app-root',
+      templateUrl: `${getComponentFileName(uidl)}.${FILE_TYPE.HTML}`,
+    }
+    const componentDecoratorAST = createComponentDecorator(params)
 
     chunks.push({
       type: CHUNK_TYPE.AST,
diff --git a/packages/teleport-plugin-angular-base-component/src/utils.ts b/packages/teleport-plugin-angular-base-component/src/utils.ts
index 0daa3ad9..1758b344 100644
--- a/packages/teleport-plugin-angular-base-component/src/utils.ts
+++ b/packages/teleport-plugin-angular-base-component/src/utils.ts
@@ -3,23 +3,7 @@ import {
   convertValueToLiteral,
   getTSAnnotationForType,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-js-utils'
-import { getComponentFileName } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
-import { UIDLPropDefinition, UIDLStateDefinition, ComponentUIDL } from '@teleporthq/teleport-types'
-import { FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
-
-export const generateComponentDecorator = (uidl: ComponentUIDL, t = types) => {
-  const decoratorArgs = [
-    t.objectExpression([
-      t.objectProperty(t.identifier('selector'), t.stringLiteral('app-root')),
-      t.objectProperty(
-        t.identifier('templateUrl'),
-        t.stringLiteral(`${getComponentFileName(uidl)}.${FILE_TYPE.HTML}`)
-      ),
-    ]),
-  ]
-
-  return t.decorator(t.callExpression(t.identifier('Component'), decoratorArgs))
-}
+import { UIDLPropDefinition, UIDLStateDefinition } from '@teleporthq/teleport-types'
 
 export const generateExportAST = (
   componentName: string,
diff --git a/packages/teleport-plugin-jsx-head-config/README.md b/packages/teleport-plugin-jsx-head-config/README.md
new file mode 100644
index 00000000..9c60730d
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/README.md
@@ -0,0 +1,14 @@
+# teleport-plugin-jsx-head-config
+
+A plugin for generating `<head>` tags inside regular JSX-based components
+
+> This package is part of the [teleport ecosystem](https://github.com/teleporthq/teleport-code-generators). For a complete guide, check out the [official documentation](https://docs.teleporthq.io/).
+
+## Install
+```bash
+npm install @teleporthq/teleport-plugin-jsx-head-config
+```
+or
+```bash
+yarn add @teleporthq/teleport-plugin-jsx-head-config
+```
diff --git a/packages/teleport-plugin-jsx-head-config/package.json b/packages/teleport-plugin-jsx-head-config/package.json
new file mode 100644
index 00000000..bebac58c
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "@teleporthq/teleport-plugin-jsx-head-config",
+  "version": "0.9.0",
+  "description": "A plugin for generating `<head>` tags inside regular JSX-based components",
+  "author": "teleportHQ",
+  "license": "MIT",
+  "homepage": "https://teleporthq.io/",
+  "main": "dist/cjs/index.js",
+  "types": "dist/cjs/index.d.ts",
+  "module": "dist/esm/index.js",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/teleporthq/teleport-code-generators.git"
+  },
+  "bugs": {
+    "url": "https://github.com/teleporthq/teleport-code-generators/issues"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "scripts": {
+    "clean": "rimraf dist",
+    "build": "npm run build:cjs & npm run build:esm",
+    "build:cjs": "tsc -p tsconfig-cjs.json",
+    "build:esm": "tsc -p tsconfig-esm.json"
+  },
+  "dependencies": {
+    "@babel/types": "^7.3.3",
+    "@teleporthq/teleport-shared": "^0.9.0",
+    "@teleporthq/teleport-types": "^0.9.0"
+  }
+}
diff --git a/packages/teleport-plugin-jsx-head-config/src/index.ts b/packages/teleport-plugin-jsx-head-config/src/index.ts
new file mode 100644
index 00000000..b16fd8d5
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/src/index.ts
@@ -0,0 +1,72 @@
+import { ComponentPluginFactory, ComponentPlugin } from '@teleporthq/teleport-types'
+import {
+  createJSXTag,
+  createSelfClosingJSXTag,
+} from '@teleporthq/teleport-shared/dist/cjs/builders/ast-builders'
+import * as types from '@babel/types'
+import { addChildJSXText } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-jsx-utils'
+import { addAttributeToJSXTag } from '@teleporthq/teleport-shared/src/utils/ast-jsx-utils'
+
+interface JSXHeadPluginConfig {
+  componentChunkName?: string
+  configTagIdentifier?: string
+  configTagDependencyPath?: string
+}
+
+export const createPlugin: ComponentPluginFactory<JSXHeadPluginConfig> = (config) => {
+  const {
+    componentChunkName = 'jsx-component',
+    configTagIdentifier = 'Helmet',
+    configTagDependencyPath = 'react-helmet',
+  } = config || {}
+
+  const propTypesPlugin: ComponentPlugin = async (structure) => {
+    const { uidl, chunks, dependencies } = structure
+
+    const componentChunk = chunks.find((chunk) => chunk.name === componentChunkName)
+    if (!componentChunk) {
+      throw new Error(
+        `JSX component chunk with name ${componentChunkName} was required and not found.`
+      )
+    }
+
+    const headASTTags = []
+
+    if (uidl.meta && uidl.meta.title) {
+      const titleAST = createJSXTag('title')
+      addChildJSXText(titleAST, uidl.meta.title)
+      headASTTags.push(titleAST)
+    }
+
+    if (uidl.meta && uidl.meta.metaTags) {
+      uidl.meta.metaTags.forEach((tag) => {
+        const metaAST = createSelfClosingJSXTag('meta')
+        Object.keys(tag).forEach((key) => {
+          addAttributeToJSXTag(metaAST, key, tag[key])
+        })
+        headASTTags.push(metaAST)
+      })
+    }
+
+    if (headASTTags.length > 0) {
+      const headConfigTag = createJSXTag(configTagIdentifier, headASTTags)
+
+      const rootKey = uidl.node.content.key
+      const rootElement = componentChunk.meta.nodesLookup[rootKey] as types.JSXElement
+
+      // Head config added as the first child of the root element
+      rootElement.children.unshift(headConfigTag)
+
+      dependencies[configTagIdentifier] = {
+        type: 'library',
+        path: configTagDependencyPath,
+      }
+    }
+
+    return structure
+  }
+
+  return propTypesPlugin
+}
+
+export default createPlugin()
diff --git a/packages/teleport-plugin-jsx-head-config/tsconfig-cjs.json b/packages/teleport-plugin-jsx-head-config/tsconfig-cjs.json
new file mode 100755
index 00000000..c66b615a
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/tsconfig-cjs.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/cjs",
+    "module": "commonjs",
+    "target": "es5"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-plugin-jsx-head-config/tsconfig-esm.json b/packages/teleport-plugin-jsx-head-config/tsconfig-esm.json
new file mode 100755
index 00000000..f8bed582
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/tsconfig-esm.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/esm",
+    "module": "esnext",
+    "target": "esnext",
+    "moduleResolution": "node"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-plugin-preact-base-component/src/constants.ts b/packages/teleport-plugin-preact-base-component/src/constants.ts
index a019b684..edf121bb 100644
--- a/packages/teleport-plugin-preact-base-component/src/constants.ts
+++ b/packages/teleport-plugin-preact-base-component/src/constants.ts
@@ -1,4 +1,6 @@
-export const PREACT_COMPONENT_DEPENDENCY = {
+import { UIDLDependency } from '@teleporthq/teleport-types'
+
+export const PREACT_COMPONENT_DEPENDENCY: UIDLDependency = {
   type: 'library',
   path: 'preact',
   version: 'next',
diff --git a/packages/teleport-plugin-react-base-component/src/constants.ts b/packages/teleport-plugin-react-base-component/src/constants.ts
index ab0f1030..123978cf 100644
--- a/packages/teleport-plugin-react-base-component/src/constants.ts
+++ b/packages/teleport-plugin-react-base-component/src/constants.ts
@@ -1,10 +1,12 @@
-export const REACT_LIBRARY_DEPENDENCY = {
+import { UIDLDependency } from '@teleporthq/teleport-types'
+
+export const REACT_LIBRARY_DEPENDENCY: UIDLDependency = {
   type: 'library',
   path: 'react',
   version: '16.8.3',
 }
 
-export const USE_STATE_DEPENDENCY = {
+export const USE_STATE_DEPENDENCY: UIDLDependency = {
   type: 'library',
   path: 'react',
   version: '16.8.3',
diff --git a/packages/teleport-plugin-stencil-app-routing/README.md b/packages/teleport-plugin-stencil-app-routing/README.md
new file mode 100644
index 00000000..9e760366
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/README.md
@@ -0,0 +1,14 @@
+# `@teleporthq/teleport-plugin-stencil-app-routing`
+
+A plugin for generating the routing file for a Stencil project.
+
+> This package is part of the [teleport ecosystem](https://github.com/teleporthq/teleport-code-generators). For a complete guide, check out the [official documentation](https://docs.teleporthq.io/).
+
+## Install
+```bash
+npm install @teleporthq/teleport-plugin-stencil-app-routing
+```
+or
+```bash
+yarn add @teleporthq/teleport-plugin-stencil-app-routing
+```
\ No newline at end of file
diff --git a/packages/teleport-plugin-stencil-app-routing/package.json b/packages/teleport-plugin-stencil-app-routing/package.json
new file mode 100644
index 00000000..cd025b05
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "@teleporthq/teleport-plugin-stencil-app-routing",
+  "version": "0.9.0",
+  "description": "A plugin for handling the routing file of a Stencil Project",
+  "author": "teleportHQ",
+  "homepage": "https://teleporthq.io/",
+  "license": "MIT",
+  "main": "dist/cjs/index.js",
+  "module": "dist/esm/index.js",
+  "types": "dist/cjs/index.d.ts",
+  "bugs": {
+    "url": "https://github.com/teleporthq/teleport-code-generators/issues"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/teleporthq/teleport-code-generators.git"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "scripts": {
+    "clean": "rimraf dist",
+    "build": "npm run build:cjs & npm run build:esm",
+    "build:cjs": "tsc -p tsconfig-cjs.json",
+    "build:esm": "tsc -p tsconfig-esm.json"
+  },
+  "dependencies": {
+    "@babel/types": "^7.3.3",
+    "@teleporthq/teleport-shared": "^0.9.0",
+    "@teleporthq/teleport-types": "^0.9.0"
+  }
+}
diff --git a/packages/teleport-plugin-stencil-app-routing/src/constants.ts b/packages/teleport-plugin-stencil-app-routing/src/constants.ts
new file mode 100644
index 00000000..bd313f7d
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/src/constants.ts
@@ -0,0 +1,14 @@
+export const STENCIL_CORE_DEPENDENCY = {
+  type: 'library',
+  path: '@stencil/core',
+  version: '1.1.4',
+  meta: {
+    namedImport: true,
+  },
+}
+
+export const DEFAULT_COMPONENT_CHUNK_NAME = 'jsx-component'
+
+export const DEFAULT_COMPONENT_DECORATOR_CHUNK_NAME = 'component-decorator'
+
+export const DEFAULT_IMPORT_CHUNK_NAME = 'import-local'
diff --git a/packages/teleport-plugin-stencil-app-routing/src/index.ts b/packages/teleport-plugin-stencil-app-routing/src/index.ts
new file mode 100644
index 00000000..6301e22f
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/src/index.ts
@@ -0,0 +1,67 @@
+import { extractRoutes } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
+import { ComponentPluginFactory, ComponentPlugin } from '@teleporthq/teleport-types'
+import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import { createComponentDecorator } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-jsx-utils'
+import {
+  STENCIL_CORE_DEPENDENCY,
+  DEFAULT_COMPONENT_DECORATOR_CHUNK_NAME,
+  DEFAULT_COMPONENT_CHUNK_NAME,
+  DEFAULT_IMPORT_CHUNK_NAME,
+} from './constants'
+import { createClassDecleration } from './utils'
+
+interface StencilRouterConfig {
+  componentDecoratorChunkName: string
+  componentChunkName: string
+  importChunkName: string
+}
+
+export const createPlugin: ComponentPluginFactory<StencilRouterConfig> = (config) => {
+  const {
+    componentChunkName = DEFAULT_COMPONENT_CHUNK_NAME,
+    componentDecoratorChunkName = DEFAULT_COMPONENT_DECORATOR_CHUNK_NAME,
+    importChunkName = DEFAULT_IMPORT_CHUNK_NAME,
+  } = config || {}
+
+  const stencilAppRouterComponentPlugin: ComponentPlugin = async (structure) => {
+    const { chunks, uidl, dependencies } = structure
+
+    dependencies.Component = STENCIL_CORE_DEPENDENCY
+    dependencies.h = STENCIL_CORE_DEPENDENCY
+
+    const routes = extractRoutes(uidl)
+    const routeDefinitions = uidl.stateDefinitions.route
+
+    /* The name should be injected only with AppRoot only then it acts as entry point,
+    Sending only Root because app is appended while generation of decorators*/
+    const params = {
+      tag: 'app-root',
+      shadow: true,
+    }
+    const decoratorAST = createComponentDecorator(params)
+
+    chunks.push({
+      name: componentDecoratorChunkName,
+      type: CHUNK_TYPE.AST,
+      fileType: FILE_TYPE.TSX,
+      content: [decoratorAST],
+      linkAfter: [importChunkName],
+    })
+
+    const classDeclarationAST = createClassDecleration(routes, routeDefinitions)
+
+    chunks.push({
+      name: componentChunkName,
+      type: CHUNK_TYPE.AST,
+      fileType: FILE_TYPE.TSX,
+      content: classDeclarationAST,
+      linkAfter: [componentDecoratorChunkName],
+    })
+
+    return structure
+  }
+
+  return stencilAppRouterComponentPlugin
+}
+
+export default createPlugin()
diff --git a/packages/teleport-plugin-stencil-app-routing/src/utils.ts b/packages/teleport-plugin-stencil-app-routing/src/utils.ts
new file mode 100644
index 00000000..064bf326
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/src/utils.ts
@@ -0,0 +1,50 @@
+import * as types from '@babel/types'
+import { createJSXTag } from '@teleporthq/teleport-shared/dist/cjs/builders/ast-builders'
+import {
+  addChildJSXTag,
+  addAttributeToJSXTag,
+} from '@teleporthq/teleport-shared/dist/cjs/utils/ast-jsx-utils'
+import { extractPageMetadata } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
+import { camelCaseToDashCase } from '@teleporthq/teleport-shared/src/utils/string-utils'
+
+export const createClassDecleration = (routes, routeDefinitions, t = types) => {
+  const stencilRouterTag = createJSXTag('stencil-router')
+  const stencilRouteSwitchTag = createJSXTag('stencil-route-switch')
+  addChildJSXTag(stencilRouterTag, stencilRouteSwitchTag)
+
+  routes.forEach((routeNode) => {
+    const pageKey = routeNode.content.value.toString()
+    const { componentName, path } = extractPageMetadata(routeDefinitions, pageKey)
+
+    const stencilRouteTag = createJSXTag('stencil-route')
+    addAttributeToJSXTag(stencilRouteTag, 'url', path)
+    if (path === '/') {
+      addAttributeToJSXTag(stencilRouteTag, 'exact', true)
+    }
+    addAttributeToJSXTag(stencilRouteTag, 'component', `app-${camelCaseToDashCase(componentName)}`)
+    addChildJSXTag(stencilRouteSwitchTag, stencilRouteTag)
+  })
+
+  const mainTag = createJSXTag('main')
+  addChildJSXTag(mainTag, stencilRouterTag)
+  const divTag = createJSXTag('div')
+  addChildJSXTag(divTag, mainTag)
+
+  const returnAST = divTag as types.JSXElement
+
+  const classBodyAST = t.classBody([
+    t.classMethod(
+      'method',
+      t.identifier('render'),
+      [],
+      t.blockStatement([t.returnStatement(returnAST)])
+    ),
+  ])
+
+  const exportClass = t.exportNamedDeclaration(
+    t.classDeclaration(t.identifier('AppRoot'), null, classBodyAST),
+    [],
+    null
+  )
+  return exportClass
+}
diff --git a/packages/teleport-plugin-stencil-app-routing/tsconfig-cjs.json b/packages/teleport-plugin-stencil-app-routing/tsconfig-cjs.json
new file mode 100755
index 00000000..c66b615a
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/tsconfig-cjs.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/cjs",
+    "module": "commonjs",
+    "target": "es5"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-plugin-stencil-app-routing/tsconfig-esm.json b/packages/teleport-plugin-stencil-app-routing/tsconfig-esm.json
new file mode 100755
index 00000000..f8bed582
--- /dev/null
+++ b/packages/teleport-plugin-stencil-app-routing/tsconfig-esm.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/esm",
+    "module": "esnext",
+    "target": "esnext",
+    "moduleResolution": "node"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-plugin-stencil-base-component/src/constants.ts b/packages/teleport-plugin-stencil-base-component/src/constants.ts
index f3955375..55b8eca3 100644
--- a/packages/teleport-plugin-stencil-base-component/src/constants.ts
+++ b/packages/teleport-plugin-stencil-base-component/src/constants.ts
@@ -1,4 +1,6 @@
-export const STENCIL_CORE_DEPENDENCY = {
+import { UIDLDependency } from '@teleporthq/teleport-types'
+
+export const STENCIL_CORE_DEPENDENCY: UIDLDependency = {
   type: 'library',
   path: '@stencil/core',
   version: '1.1.4',
diff --git a/packages/teleport-plugin-stencil-base-component/src/index.ts b/packages/teleport-plugin-stencil-base-component/src/index.ts
index 7310cbae..0d72b65d 100644
--- a/packages/teleport-plugin-stencil-base-component/src/index.ts
+++ b/packages/teleport-plugin-stencil-base-component/src/index.ts
@@ -1,4 +1,5 @@
-import { createClassDeclaration, createComponentDecorator } from './utils'
+import { createClassDeclaration } from './utils'
+import { createComponentDecorator } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-jsx-utils'
 import createJSXSyntax from '@teleporthq/teleport-shared/dist/cjs/node-handlers/node-to-jsx'
 import { JSXGenerationOptions } from '@teleporthq/teleport-shared/dist/cjs/node-handlers/node-to-jsx/types'
 import { ComponentPluginFactory, ComponentPlugin } from '@teleporthq/teleport-types'
@@ -10,6 +11,7 @@ import {
   STENCIL_CORE_DEPENDENCY,
 } from './constants'
 import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import { camelCaseToDashCase } from '@teleporthq/teleport-shared/dist/cjs/utils/string-utils'
 
 interface StencilPluginConfig {
   componentChunkName: string
@@ -59,6 +61,7 @@ export const createPlugin: ComponentPluginFactory<StencilPluginConfig> = (config
       dependencyHandling: 'webcomponents',
       stateHandling: 'mutation',
       slotHandling: 'native',
+      customElementTag: (name: string) => `app-${camelCaseToDashCase(name)}`,
     }
 
     const jsxTagStructure = createJSXSyntax(uidl.node, jsxParams, jsxOptions)
@@ -69,7 +72,12 @@ export const createPlugin: ComponentPluginFactory<StencilPluginConfig> = (config
       jsxTagStructure
     )
 
-    const decoratorAST = createComponentDecorator(uidl.name)
+    const params = {
+      tag: `app-${camelCaseToDashCase(uidl.name)}`,
+      shadow: true,
+    }
+
+    const decoratorAST = createComponentDecorator(params)
 
     structure.chunks.push({
       type: CHUNK_TYPE.AST,
diff --git a/packages/teleport-plugin-stencil-base-component/src/utils.ts b/packages/teleport-plugin-stencil-base-component/src/utils.ts
index 40d0dc88..9a429d4a 100644
--- a/packages/teleport-plugin-stencil-base-component/src/utils.ts
+++ b/packages/teleport-plugin-stencil-base-component/src/utils.ts
@@ -1,11 +1,9 @@
 import * as types from '@babel/types'
 
 import {
-  objectToObjectExpression,
   convertValueToLiteral,
   getTSAnnotationForType,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-js-utils'
-import { camelCaseToDashCase } from '@teleporthq/teleport-shared/dist/cjs/utils/string-utils'
 import { UIDLStateDefinition, UIDLPropDefinition } from '@teleporthq/teleport-types'
 
 export const createClassDeclaration = (
@@ -47,14 +45,3 @@ export const createClassDeclaration = (
   const classDeclaration = t.classDeclaration(t.identifier(name), null, classBody, [])
   return t.exportNamedDeclaration(classDeclaration, [])
 }
-
-export const createComponentDecorator = (name: string, t = types) => {
-  return t.decorator(
-    t.callExpression(t.identifier('Component'), [
-      objectToObjectExpression({
-        tag: camelCaseToDashCase(name),
-        shadow: true,
-      }),
-    ])
-  )
-}
diff --git a/packages/teleport-plugin-vue-app-routing/README.md b/packages/teleport-plugin-vue-app-routing/README.md
index c41a68b5..7c18b21c 100644
--- a/packages/teleport-plugin-vue-app-routing/README.md
+++ b/packages/teleport-plugin-vue-app-routing/README.md
@@ -1,4 +1,4 @@
-# teleport-plugin-vue-app-routing
+# `@teleporthq/teleport-plugin-vue-app-routing`
 
 A plugin for generating the routing file for a Vue project.
 
diff --git a/packages/teleport-plugin-vue-app-routing/src/index.ts b/packages/teleport-plugin-vue-app-routing/src/index.ts
index 32fdfc15..9fcedaa6 100644
--- a/packages/teleport-plugin-vue-app-routing/src/index.ts
+++ b/packages/teleport-plugin-vue-app-routing/src/index.ts
@@ -25,11 +25,19 @@ export const createPlugin: ComponentPluginFactory<VueRouterConfig> = (config) =>
       type: 'library',
       path: 'vue-router',
     }
+    dependencies.Meta = {
+      type: 'library',
+      path: 'vue-meta',
+    }
 
-    const declaration = t.expressionStatement(
+    const routerDeclaration = t.expressionStatement(
       t.callExpression(t.identifier('Vue.use'), [t.identifier('Router')])
     )
 
+    const metaDeclaration = t.expressionStatement(
+      t.callExpression(t.identifier('Vue.use'), [t.identifier('Meta')])
+    )
+
     const routes = extractRoutes(uidl)
     const routeDefinitions = uidl.stateDefinitions.route
     const pageDependencyPrefix = options.localDependenciesPrefix || './'
@@ -76,7 +84,7 @@ export const createPlugin: ComponentPluginFactory<VueRouterConfig> = (config) =>
       linkAfter: [importChunkName],
       type: CHUNK_TYPE.AST,
       fileType: FILE_TYPE.JS,
-      content: [declaration, exportStatement],
+      content: [routerDeclaration, metaDeclaration, exportStatement],
     })
 
     return structure
diff --git a/packages/teleport-plugin-vue-head-config/README.md b/packages/teleport-plugin-vue-head-config/README.md
new file mode 100644
index 00000000..26984efa
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/README.md
@@ -0,0 +1,14 @@
+# teleport-plugin-vue-head-config
+
+A plugin for generating `<head>` tags inside regular vue components
+
+> This package is part of the [teleport ecosystem](https://github.com/teleporthq/teleport-code-generators). For a complete guide, check out the [official documentation](https://docs.teleporthq.io/).
+
+## Install
+```bash
+npm install @teleporthq/teleport-plugin-vue-head-config
+```
+or
+```bash
+yarn add @teleporthq/teleport-plugin-vue-head-config
+```
diff --git a/packages/teleport-plugin-vue-head-config/package.json b/packages/teleport-plugin-vue-head-config/package.json
new file mode 100644
index 00000000..39939397
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "@teleporthq/teleport-plugin-vue-head-config",
+  "version": "0.9.0",
+  "description": "A plugin for generating `<head>` tags inside regular vue components",
+  "author": "teleportHQ",
+  "license": "MIT",
+  "homepage": "https://teleporthq.io/",
+  "main": "dist/cjs/index.js",
+  "types": "dist/cjs/index.d.ts",
+  "module": "dist/esm/index.js",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/teleporthq/teleport-code-generators.git"
+  },
+  "bugs": {
+    "url": "https://github.com/teleporthq/teleport-code-generators/issues"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "scripts": {
+    "clean": "rimraf dist",
+    "build": "npm run build:cjs & npm run build:esm",
+    "build:cjs": "tsc -p tsconfig-cjs.json",
+    "build:esm": "tsc -p tsconfig-esm.json"
+  },
+  "dependencies": {
+    "@babel/types": "^7.3.3",
+    "@teleporthq/teleport-shared": "^0.9.0",
+    "@teleporthq/teleport-types": "^0.9.0"
+  }
+}
diff --git a/packages/teleport-plugin-vue-head-config/src/index.ts b/packages/teleport-plugin-vue-head-config/src/index.ts
new file mode 100644
index 00000000..2009574e
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/src/index.ts
@@ -0,0 +1,47 @@
+import { ComponentPluginFactory, ComponentPlugin } from '@teleporthq/teleport-types'
+import * as types from '@babel/types'
+import { objectToObjectExpression } from '@teleporthq/teleport-shared/dist/cjs/utils/ast-js-utils'
+
+interface VueMetaPluginConfig {
+  vueJSChunkName?: string
+  metaObjectKey?: string
+}
+
+export const createPlugin: ComponentPluginFactory<VueMetaPluginConfig> = (config) => {
+  const { vueJSChunkName = 'vue-js-chunk', metaObjectKey = 'head' } = config || {}
+
+  const propTypesPlugin: ComponentPlugin = async (structure) => {
+    const { uidl, chunks } = structure
+
+    const componentChunk = chunks.find((chunk) => chunk.name === vueJSChunkName)
+    if (!componentChunk) {
+      throw new Error(`JS component chunk with name ${vueJSChunkName} was required and not found.`)
+    }
+
+    const headObject: {
+      title?: string
+      meta?: Array<Record<string, string>>
+    } = {}
+
+    if (uidl.meta && uidl.meta.title) {
+      headObject.title = uidl.meta.title
+    }
+
+    if (uidl.meta && uidl.meta.metaTags) {
+      headObject.meta = uidl.meta.metaTags
+    }
+
+    if (Object.keys(headObject).length > 0) {
+      const exportObjectAST = componentChunk.content.declaration as types.ObjectExpression
+      exportObjectAST.properties.push(
+        types.objectProperty(types.identifier(metaObjectKey), objectToObjectExpression(headObject))
+      )
+    }
+
+    return structure
+  }
+
+  return propTypesPlugin
+}
+
+export default createPlugin()
diff --git a/packages/teleport-plugin-vue-head-config/tsconfig-cjs.json b/packages/teleport-plugin-vue-head-config/tsconfig-cjs.json
new file mode 100755
index 00000000..c66b615a
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/tsconfig-cjs.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/cjs",
+    "module": "commonjs",
+    "target": "es5"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-plugin-vue-head-config/tsconfig-esm.json b/packages/teleport-plugin-vue-head-config/tsconfig-esm.json
new file mode 100755
index 00000000..f8bed582
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/tsconfig-esm.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/esm",
+    "module": "esnext",
+    "target": "esnext",
+    "moduleResolution": "node"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-project-generator-next/package.json b/packages/teleport-project-generator-next/package.json
index b00105bf..08bf633d 100644
--- a/packages/teleport-project-generator-next/package.json
+++ b/packages/teleport-project-generator-next/package.json
@@ -28,6 +28,7 @@
     "@babel/types": "^7.3.3",
     "@teleporthq/teleport-component-generator": "^0.9.0",
     "@teleporthq/teleport-component-generator-react": "^0.9.0",
+    "@teleporthq/teleport-plugin-jsx-head-config": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-js": "^0.9.0",
     "@teleporthq/teleport-project-generator": "^0.9.0",
     "@teleporthq/teleport-shared": "^0.9.0",
diff --git a/packages/teleport-project-generator-next/src/index.ts b/packages/teleport-project-generator-next/src/index.ts
index 689e679b..00435c7f 100644
--- a/packages/teleport-project-generator-next/src/index.ts
+++ b/packages/teleport-project-generator-next/src/index.ts
@@ -2,6 +2,7 @@ import { createProjectGenerator } from '@teleporthq/teleport-project-generator'
 import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
 import { createReactComponentGenerator } from '@teleporthq/teleport-component-generator-react'
 
+import { createPlugin as createHeadConfigPlugin } from '@teleporthq/teleport-plugin-jsx-head-config'
 import prettierJS from '@teleporthq/teleport-postprocessor-prettier-js'
 
 import { Mapping } from '@teleporthq/teleport-types'
@@ -9,10 +10,18 @@ import { Mapping } from '@teleporthq/teleport-types'
 import { createDocumentFileChunks } from './utils'
 import nextMapping from './next-mapping.json'
 
+const headConfigPlugin = createHeadConfigPlugin({
+  configTagIdentifier: 'Head',
+  configTagDependencyPath: 'next/head',
+})
+
 export const createNextProjectGenerator = () => {
   const reactComponentGenerator = createReactComponentGenerator('StyledJSX')
   reactComponentGenerator.addMapping(nextMapping as Mapping)
 
+  const reactPageGenerator = createReactComponentGenerator('StyledJSX', [headConfigPlugin])
+  reactPageGenerator.addMapping(nextMapping as Mapping)
+
   const documentFileGenerator = createComponentGenerator()
   documentFileGenerator.addPostProcessor(prettierJS)
 
@@ -22,7 +31,7 @@ export const createNextProjectGenerator = () => {
       path: ['components'],
     },
     pages: {
-      generator: reactComponentGenerator,
+      generator: reactPageGenerator,
       path: ['pages'],
       options: {
         usePathAsFileName: true,
diff --git a/packages/teleport-project-generator-nuxt/package.json b/packages/teleport-project-generator-nuxt/package.json
index e6b44735..e09418ad 100644
--- a/packages/teleport-project-generator-nuxt/package.json
+++ b/packages/teleport-project-generator-nuxt/package.json
@@ -27,6 +27,7 @@
   "dependencies": {
     "@teleporthq/teleport-component-generator": "^0.9.0",
     "@teleporthq/teleport-component-generator-vue": "^0.9.0",
+    "@teleporthq/teleport-plugin-vue-head-config": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-html": "^0.9.0",
     "@teleporthq/teleport-project-generator": "^0.9.0",
     "@teleporthq/teleport-shared": "^0.9.0",
diff --git a/packages/teleport-project-generator-nuxt/src/index.ts b/packages/teleport-project-generator-nuxt/src/index.ts
index d71c218c..f0a46858 100644
--- a/packages/teleport-project-generator-nuxt/src/index.ts
+++ b/packages/teleport-project-generator-nuxt/src/index.ts
@@ -1,7 +1,7 @@
 import { createProjectGenerator } from '@teleporthq/teleport-project-generator'
 import { createVueComponentGenerator } from '@teleporthq/teleport-component-generator-vue'
 import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
-
+import vueHeadConfigPlugin from '@teleporthq/teleport-plugin-vue-head-config'
 import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
 
 import { Mapping } from '@teleporthq/teleport-types'
@@ -10,6 +10,8 @@ import nuxtMapping from './nuxt-mapping.json'
 
 export const createNuxtProjectGenerator = () => {
   const vueComponentGenerator = createVueComponentGenerator(nuxtMapping as Mapping)
+  const vuePageGenerator = createVueComponentGenerator(nuxtMapping as Mapping)
+  vuePageGenerator.addPlugin(vueHeadConfigPlugin)
 
   const htmlFileGenerator = createComponentGenerator()
   htmlFileGenerator.addPostProcessor(prettierHTML)
@@ -20,7 +22,7 @@ export const createNuxtProjectGenerator = () => {
       path: ['components'],
     },
     pages: {
-      generator: vueComponentGenerator,
+      generator: vuePageGenerator,
       path: ['pages'],
       options: {
         usePathAsFileName: true,
@@ -29,9 +31,11 @@ export const createNuxtProjectGenerator = () => {
     },
     entry: {
       generator: htmlFileGenerator,
-      appRootOverride: '{{APP}}',
       fileName: 'app',
       path: [],
+      options: {
+        appRootOverride: '{{APP}}',
+      },
     },
     static: {
       prefix: '/static',
diff --git a/packages/teleport-project-generator-preact/package.json b/packages/teleport-project-generator-preact/package.json
index 64a5fe84..58ad24a4 100644
--- a/packages/teleport-project-generator-preact/package.json
+++ b/packages/teleport-project-generator-preact/package.json
@@ -2,10 +2,12 @@
   "name": "@teleporthq/teleport-project-generator-preact",
   "version": "0.9.0",
   "description": "Project generator for a standard Preact project",
-  "author": "JayaKrishna <namburu1995@gmail.com>",
+  "author": "teleportHQ",
   "homepage": "https://teleporthq.io/",
   "license": "MIT",
   "main": "dist/cjs/index.js",
+  "module": "dist/esm/index.js",
+  "types": "dist/cjs/index.d.ts",
   "publishConfig": {
     "access": "public"
   },
@@ -26,6 +28,7 @@
     "@teleporthq/teleport-component-generator": "^0.9.0",
     "@teleporthq/teleport-component-generator-preact": "^0.9.0",
     "@teleporthq/teleport-plugin-import-statements": "^0.9.0",
+    "@teleporthq/teleport-plugin-jsx-head-config": "^0.9.0",
     "@teleporthq/teleport-plugin-react-app-routing": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-html": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-js": "^0.9.0",
diff --git a/packages/teleport-project-generator-preact/src/index.ts b/packages/teleport-project-generator-preact/src/index.ts
index d49306e3..dee1b2d0 100644
--- a/packages/teleport-project-generator-preact/src/index.ts
+++ b/packages/teleport-project-generator-preact/src/index.ts
@@ -3,6 +3,7 @@ import { createPreactComponentGenerator } from '@teleporthq/teleport-component-g
 import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
 
 import { createPlugin as createRouterPlugin } from '@teleporthq/teleport-plugin-react-app-routing'
+import headConfigPlugin from '@teleporthq/teleport-plugin-jsx-head-config'
 import importStatementsPlugin from '@teleporthq/teleport-plugin-import-statements'
 import prettierJS from '@teleporthq/teleport-postprocessor-prettier-js'
 import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
@@ -12,9 +13,12 @@ import { Mapping } from '@teleporthq/teleport-types'
 import preactProjectMapping from './preact-project-mapping.json'
 
 export const createPreactProjectGenerator = () => {
-  const preactComponentGenerator = createPreactComponentGenerator()
+  const preactComponentGenerator = createPreactComponentGenerator('CSSModules')
   preactComponentGenerator.addMapping(preactProjectMapping as Mapping)
 
+  const preactPageGenerator = createPreactComponentGenerator('CSSModules', [headConfigPlugin])
+  preactPageGenerator.addMapping(preactProjectMapping as Mapping)
+
   const routerPlugin = createRouterPlugin({ flavor: 'preact' })
   const routingComponentGenerator = createComponentGenerator()
   routingComponentGenerator.addPlugin(routerPlugin)
@@ -30,7 +34,7 @@ export const createPreactProjectGenerator = () => {
       path: ['src', 'components'],
     },
     pages: {
-      generator: preactComponentGenerator,
+      generator: preactPageGenerator,
       path: ['src', 'routes'],
       options: {
         createFolderForEachComponent: true,
diff --git a/packages/teleport-project-generator-react/package.json b/packages/teleport-project-generator-react/package.json
index d27ea25f..d465417b 100644
--- a/packages/teleport-project-generator-react/package.json
+++ b/packages/teleport-project-generator-react/package.json
@@ -27,6 +27,7 @@
   "dependencies": {
     "@teleporthq/teleport-component-generator": "^0.9.0",
     "@teleporthq/teleport-component-generator-react": "^0.9.0",
+    "@teleporthq/teleport-plugin-jsx-head-config": "^0.9.0",
     "@teleporthq/teleport-plugin-import-statements": "^0.9.0",
     "@teleporthq/teleport-plugin-react-app-routing": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-html": "^0.9.0",
diff --git a/packages/teleport-project-generator-react/src/index.ts b/packages/teleport-project-generator-react/src/index.ts
index 77a28640..c458434f 100644
--- a/packages/teleport-project-generator-react/src/index.ts
+++ b/packages/teleport-project-generator-react/src/index.ts
@@ -4,6 +4,7 @@ import { createComponentGenerator } from '@teleporthq/teleport-component-generat
 
 import reactAppRoutingPlugin from '@teleporthq/teleport-plugin-react-app-routing'
 import importStatementsPlugin from '@teleporthq/teleport-plugin-import-statements'
+import headConfigPlugin from '@teleporthq/teleport-plugin-jsx-head-config'
 import prettierJS from '@teleporthq/teleport-postprocessor-prettier-js'
 import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
 
@@ -15,6 +16,9 @@ export const createReactProjectGenerator = () => {
   const reactComponentGenerator = createReactComponentGenerator('CSSModules')
   reactComponentGenerator.addMapping(reactProjectMapping as Mapping)
 
+  const reactPagesGenerator = createReactComponentGenerator('CSSModules', [headConfigPlugin])
+  reactPagesGenerator.addMapping(reactProjectMapping as Mapping)
+
   const routingComponentGenerator = createComponentGenerator()
   routingComponentGenerator.addPlugin(reactAppRoutingPlugin)
   routingComponentGenerator.addPlugin(importStatementsPlugin)
@@ -29,7 +33,7 @@ export const createReactProjectGenerator = () => {
       path: ['src', 'components'],
     },
     pages: {
-      generator: reactComponentGenerator,
+      generator: reactPagesGenerator,
       path: ['src', 'views'],
     },
     router: {
diff --git a/packages/teleport-project-generator-stencil/README.md b/packages/teleport-project-generator-stencil/README.md
new file mode 100644
index 00000000..6a5d7b55
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/README.md
@@ -0,0 +1,14 @@
+# teleport-project-generator-stencil
+
+Project generator using Stencil, on top of a standard stencil config.
+
+> This package is part of the [teleport ecosystem](https://github.com/teleporthq/teleport-code-generators). For a complete guide, check out the [official documentation](https://docs.teleporthq.io/).
+
+## Install
+```bash
+npm install @teleporthq/teleport-project-generator-stencil
+```
+or
+```bash
+yarn add @teleporthq/teleport-project-generator-stencil
+```
diff --git a/packages/teleport-project-generator-stencil/package.json b/packages/teleport-project-generator-stencil/package.json
new file mode 100644
index 00000000..f08fde4b
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/package.json
@@ -0,0 +1,38 @@
+{
+  "name": "@teleporthq/teleport-project-generator-stencil",
+  "version": "0.9.0",
+  "description": "Project generator for a standard stencil project",
+  "author": "teleportHQ",
+  "homepage": "https://teleporthq.io/",
+  "license": "MIT",
+  "main": "dist/cjs/index.js",
+  "module": "dist/esm/index.js",
+  "types": "dist/cjs/index.d.ts",
+  "publishConfig": {
+    "access": "public"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/teleporthq/teleport-code-generators.git"
+  },
+  "scripts": {
+    "clean": "rimraf dist",
+    "build": "npm run build:cjs & npm run build:esm",
+    "build:cjs": "tsc -p tsconfig-cjs.json",
+    "build:esm": "tsc -p tsconfig-esm.json"
+  },
+  "bugs": {
+    "url": "https://github.com/teleporthq/teleport-code-generators/issues"
+  },
+  "dependencies": {
+    "@teleporthq/teleport-types": "^0.9.0",
+    "@teleporthq/teleport-shared": "^0.9.0",
+    "@teleporthq/teleport-project-generator": "^0.9.0",
+    "@teleporthq/teleport-component-generator": "^0.9.0",
+    "@teleporthq/teleport-plugin-import-statements": "^0.9.0",
+    "@teleporthq/teleport-postprocessor-prettier-js": "^0.9.0",
+    "@teleporthq/teleport-plugin-stencil-app-routing": "^0.9.0",
+    "@teleporthq/teleport-component-generator-stencil": "^0.9.0",
+    "@teleporthq/teleport-postprocessor-prettier-html": "^0.9.0"
+  }
+}
diff --git a/packages/teleport-project-generator-stencil/src/index.ts b/packages/teleport-project-generator-stencil/src/index.ts
new file mode 100644
index 00000000..38f4131a
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/src/index.ts
@@ -0,0 +1,72 @@
+import { createProjectGenerator } from '@teleporthq/teleport-project-generator'
+import { createStencilComponentGenerator } from '@teleporthq/teleport-component-generator-stencil'
+import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
+import { FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import { createPostProcessor } from '@teleporthq/teleport-postprocessor-prettier-js'
+import { createPlugin as createImportPlugin } from '@teleporthq/teleport-plugin-import-statements'
+
+import stencilAppRouting from '@teleporthq/teleport-plugin-stencil-app-routing'
+import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
+
+import { Mapping } from '@teleporthq/teleport-types'
+
+import stencilProjectMapping from './stencil-mapping.json'
+
+export const createStencilProjectGenerator = () => {
+  const prettierJS = createPostProcessor({ fileType: FILE_TYPE.TSX })
+  const importStatementsPlugin = createImportPlugin({ fileType: FILE_TYPE.TSX })
+
+  const stencilComponentGenerator = createStencilComponentGenerator()
+  stencilComponentGenerator.addMapping(stencilProjectMapping as Mapping)
+
+  const routingComponentGenerator = createComponentGenerator()
+  routingComponentGenerator.addPlugin(stencilAppRouting)
+  routingComponentGenerator.addPlugin(importStatementsPlugin)
+  routingComponentGenerator.addPostProcessor(prettierJS)
+
+  const htmlFileGenerator = createComponentGenerator()
+  htmlFileGenerator.addPostProcessor(prettierHTML)
+
+  const generator = createProjectGenerator({
+    components: {
+      generator: stencilComponentGenerator,
+      path: ['src', 'components'],
+      options: {
+        createFolderForEachComponent: true,
+      },
+    },
+    pages: {
+      generator: stencilComponentGenerator,
+      path: ['src', 'components'],
+      options: {
+        createFolderForEachComponent: true,
+      },
+    },
+    router: {
+      generator: routingComponentGenerator,
+      path: ['src', 'components'],
+      fileName: 'app-root',
+    },
+    entry: {
+      generator: htmlFileGenerator,
+      path: ['src'],
+      fileName: 'index',
+      options: {
+        appRootOverride: `<app-root></app-root>`,
+        customScriptTags: [
+          { type: 'module', path: '/build/app.esm.js' },
+          { type: 'nomodule', path: '/buid/app.js' },
+        ],
+        customLinkTags: [{ type: 'stylesheet', path: '/build/app.css' }],
+      },
+    },
+    static: {
+      prefix: '/assets',
+      path: ['src', 'assets'],
+    },
+  })
+
+  return generator
+}
+
+export default createStencilProjectGenerator()
diff --git a/packages/teleport-project-generator-stencil/src/stencil-mapping.json b/packages/teleport-project-generator-stencil/src/stencil-mapping.json
new file mode 100644
index 00000000..d7dc0900
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/src/stencil-mapping.json
@@ -0,0 +1,18 @@
+{
+  "elements": {
+    "navlink": {
+      "elementType": "stencil-route-link",
+      "dependency": {
+        "type": "library",
+        "path": "@stencil/router",
+        "version": "1.0.1",
+        "meta": {
+          "namedImport": true
+        }
+      },
+      "attrs": {
+        "url":{ "type": "dynamic", "content": { "referenceType": "attr", "id": "transitionTo" } }
+      }
+    }
+  }
+}
diff --git a/packages/teleport-project-generator-stencil/tsconfig-cjs.json b/packages/teleport-project-generator-stencil/tsconfig-cjs.json
new file mode 100755
index 00000000..c66b615a
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/tsconfig-cjs.json
@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/cjs",
+    "module": "commonjs",
+    "target": "es5"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-project-generator-stencil/tsconfig-esm.json b/packages/teleport-project-generator-stencil/tsconfig-esm.json
new file mode 100755
index 00000000..f8bed582
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/tsconfig-esm.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "outDir": "dist/esm",
+    "module": "esnext",
+    "target": "esnext",
+    "moduleResolution": "node"
+  },
+  "include": [
+    "./src"
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-project-generator-vue/package.json b/packages/teleport-project-generator-vue/package.json
index 443ac693..e810aab3 100644
--- a/packages/teleport-project-generator-vue/package.json
+++ b/packages/teleport-project-generator-vue/package.json
@@ -29,6 +29,7 @@
     "@teleporthq/teleport-component-generator-vue": "^0.9.0",
     "@teleporthq/teleport-plugin-import-statements": "^0.9.0",
     "@teleporthq/teleport-plugin-vue-app-routing": "^0.9.0",
+    "@teleporthq/teleport-plugin-vue-head-config": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-html": "^0.9.0",
     "@teleporthq/teleport-postprocessor-prettier-js": "^0.9.0",
     "@teleporthq/teleport-project-generator": "^0.9.0",
diff --git a/packages/teleport-project-generator-vue/src/index.ts b/packages/teleport-project-generator-vue/src/index.ts
index 073ba92a..c19ff0b2 100644
--- a/packages/teleport-project-generator-vue/src/index.ts
+++ b/packages/teleport-project-generator-vue/src/index.ts
@@ -3,6 +3,7 @@ import { createVueComponentGenerator } from '@teleporthq/teleport-component-gene
 import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
 
 import vueRoutingPlugin from '@teleporthq/teleport-plugin-vue-app-routing'
+import { createPlugin as createHeadConfigPlugin } from '@teleporthq/teleport-plugin-vue-head-config'
 import importStatementsPlugin from '@teleporthq/teleport-plugin-import-statements'
 import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
 import prettierJS from '@teleporthq/teleport-postprocessor-prettier-js'
@@ -11,8 +12,12 @@ import { Mapping } from '@teleporthq/teleport-types'
 
 import vueProjectMapping from './vue-project-mapping.json'
 
+const vueHeadConfigPlugin = createHeadConfigPlugin({ metaObjectKey: 'metaInfo' })
+
 export const createVueProjectGenerator = () => {
   const vueComponentGenerator = createVueComponentGenerator(vueProjectMapping as Mapping)
+  const vuePagesGenerator = createVueComponentGenerator(vueProjectMapping as Mapping)
+  vuePagesGenerator.addPlugin(vueHeadConfigPlugin)
 
   const vueRouterGenerator = createComponentGenerator()
   vueRouterGenerator.addPlugin(vueRoutingPlugin)
@@ -28,7 +33,7 @@ export const createVueProjectGenerator = () => {
       path: ['src', 'components'],
     },
     pages: {
-      generator: vueComponentGenerator,
+      generator: vuePagesGenerator,
       path: ['src', 'views'],
     },
     router: {
diff --git a/packages/teleport-project-generator/src/file-handlers.ts b/packages/teleport-project-generator/src/file-handlers.ts
index a2b9a236..83be8743 100644
--- a/packages/teleport-project-generator/src/file-handlers.ts
+++ b/packages/teleport-project-generator/src/file-handlers.ts
@@ -19,6 +19,8 @@ import {
   GeneratorOptions,
   ProjectStrategy,
   EntryFileOptions,
+  CustomScriptTag,
+  CustomLinkTag,
 } from '@teleporthq/teleport-types'
 
 import { DEFAULT_PACKAGE_JSON, DEFAULT_ROUTER_FILE_NAME } from './constants'
@@ -68,9 +70,18 @@ export const createEntryFile = async (
   // If no function is provided in the strategy, the createHTMLEntryFileChunks is used by default
   const chunkGenerationFunction =
     strategy.entry.chunkGenerationFunction || createHTMLEntryFileChunks
-  const appRootOverride = strategy.entry.appRootOverride || null
+  const { options } = strategy.entry
+
+  const appRootOverride = (options && options.appRootOverride) || null
   const entryFileName = strategy.entry.fileName || 'index'
-  const chunks = chunkGenerationFunction(uidl, { assetsPrefix, appRootOverride })
+  const customScriptTags = (options && options.customScriptTags) || []
+  const customLinkTags = (options && options.customLinkTags) || []
+  const chunks = chunkGenerationFunction(uidl, {
+    assetsPrefix,
+    appRootOverride,
+    customScriptTags,
+    customLinkTags,
+  })
 
   const [entryFile] = strategy.entry.generator.linkCodeChunks(chunks, entryFileName)
   return entryFile
@@ -78,7 +89,7 @@ export const createEntryFile = async (
 
 // Default function used to generate the html file based on the global settings in the ProjectUIDL
 const createHTMLEntryFileChunks = (uidl: ProjectUIDL, options: EntryFileOptions) => {
-  const { assetsPrefix = '', appRootOverride } = options
+  const { assetsPrefix = '', appRootOverride, customScriptTags, customLinkTags } = options
   const { settings, meta, assets, manifest } = uidl.globals
 
   const htmlNode = createHTMLNode('html')
@@ -108,6 +119,31 @@ const createHTMLEntryFileChunks = (uidl: ProjectUIDL, options: EntryFileOptions)
     addChildNode(headNode, titleTag)
   }
 
+  // For frameworks that need to inject and point out the generated build files
+  if (customScriptTags.length > 0) {
+    customScriptTags.forEach((tag: CustomScriptTag) => {
+      const { type, path } = tag
+      const scriptTag = createHTMLNode('script')
+      if (type === 'module') {
+        addAttributeToNode(scriptTag, 'type', type)
+      } else {
+        addAttributeToNode(scriptTag, 'nomodule', '')
+      }
+      addAttributeToNode(scriptTag, 'src', path)
+      addChildNode(headNode, scriptTag)
+    })
+  }
+
+  if (customLinkTags.length > 0) {
+    customLinkTags.forEach((tag: CustomLinkTag) => {
+      const { path, type } = tag
+      const linkTag = createHTMLNode('link')
+      addAttributeToNode(linkTag, 'href', path)
+      addAttributeToNode(linkTag, 'rel', type)
+      addChildNode(headNode, linkTag)
+    })
+  }
+
   if (manifest) {
     const linkTag = createHTMLNode('link') // , { selfClosing: true })
     addAttributeToNode(linkTag, 'rel', 'manifest')
diff --git a/packages/teleport-project-generator/src/utils.ts b/packages/teleport-project-generator/src/utils.ts
index 7a53b64e..fdbe41e5 100644
--- a/packages/teleport-project-generator/src/utils.ts
+++ b/packages/teleport-project-generator/src/utils.ts
@@ -47,7 +47,7 @@ const createPageUIDL = (
   const createPathInOwnFile =
     !pagesStrategyOptions.usePathAsFileName && pagesStrategyOptions.createFolderForEachComponent
 
-  const meta = createPathInOwnFile
+  const fileMeta = createPathInOwnFile
     ? {
         fileName: pagesStrategyOptions.customComponentFileName || 'index',
         styleFileName: pagesStrategyOptions.customStyleFileName || 'style',
@@ -61,6 +61,12 @@ const createPageUIDL = (
         path: [],
       }
 
+  // Looking into the state definition, we take the seo information for the corresponding page
+  const pageDefinition = routeDefintion.values.find((stateDef) => stateDef.value === pageName)
+  const seoMeta = pageDefinition.meta
+    ? { title: pageDefinition.meta.title, metaTags: pageDefinition.meta.metaTags }
+    : null
+
   // Because conditional nodes accept any type of UIDLNode as a child
   // we need to ensure that the page is always of type 'element'
   // The solution is to wrap a non-element node with a 'group' element
@@ -69,7 +75,7 @@ const createPageUIDL = (
   return {
     name: componentName,
     node: pageContent,
-    meta,
+    meta: { ...fileMeta, ...seoMeta },
   }
 }
 
diff --git a/packages/teleport-shared/src/builders/uidl-builders.ts b/packages/teleport-shared/src/builders/uidl-builders.ts
index f334489d..57efe32b 100644
--- a/packages/teleport-shared/src/builders/uidl-builders.ts
+++ b/packages/teleport-shared/src/builders/uidl-builders.ts
@@ -81,7 +81,7 @@ export const element = (
 }
 
 export const componentDependency = (
-  type?: string,
+  type: 'library' | 'package' | 'local',
   path?: string,
   version?: string,
   meta?: Record<string, string | boolean>
diff --git a/packages/teleport-shared/src/node-handlers/node-to-jsx/index.ts b/packages/teleport-shared/src/node-handlers/node-to-jsx/index.ts
index cf3c27b1..314613e9 100644
--- a/packages/teleport-shared/src/node-handlers/node-to-jsx/index.ts
+++ b/packages/teleport-shared/src/node-handlers/node-to-jsx/index.ts
@@ -26,10 +26,8 @@ import {
   addChildJSXTag,
   addAttributeToJSXTag,
   addDynamicAttributeToJSXTag,
-  renameJSXTag,
 } from '../../utils/ast-jsx-utils'
 import { createJSXTag, createSelfClosingJSXTag } from '../../builders/ast-builders'
-import { camelCaseToDashCase } from '../../utils/string-utils'
 import { DEFAULT_JSX_OPTIONS } from './constants'
 
 const generateElementNode: NodeToJSX<UIDLElementNode, types.JSXElement> = (
@@ -40,7 +38,12 @@ const generateElementNode: NodeToJSX<UIDLElementNode, types.JSXElement> = (
   const options = { ...DEFAULT_JSX_OPTIONS, ...jsxOptions }
   const { dependencies, nodesLookup } = params
   const { elementType, children, key, attrs, dependency, events } = node.content
-  const elementTag = children ? createJSXTag(elementType) : createSelfClosingJSXTag(elementType)
+
+  const elementName =
+    dependency && dependency.type === 'local' && options.customElementTag
+      ? options.customElementTag(elementType)
+      : elementType
+  const elementTag = children ? createJSXTag(elementName) : createSelfClosingJSXTag(elementName)
 
   if (attrs) {
     Object.keys(attrs).forEach((attrKey) => {
@@ -71,11 +74,6 @@ const generateElementNode: NodeToJSX<UIDLElementNode, types.JSXElement> = (
     if (options.dependencyHandling === 'import') {
       // Make a copy to avoid reference leaking
       dependencies[elementType] = { ...dependency }
-    } else {
-      // Convert
-      const rootElementIdentifier = elementTag.openingElement.name as types.JSXIdentifier
-      const webComponentName = camelCaseToDashCase(rootElementIdentifier.name)
-      renameJSXTag(elementTag, webComponentName)
     }
   }
 
diff --git a/packages/teleport-shared/src/node-handlers/node-to-jsx/types.ts b/packages/teleport-shared/src/node-handlers/node-to-jsx/types.ts
index 1c237ab8..19d3a224 100644
--- a/packages/teleport-shared/src/node-handlers/node-to-jsx/types.ts
+++ b/packages/teleport-shared/src/node-handlers/node-to-jsx/types.ts
@@ -46,6 +46,7 @@ export interface JSXGenerationOptions {
     - 'props' will render a `props.children` node and needs some workarounds for multiple slots per component
   */
   slotHandling?: 'native' | 'props'
+  customElementTag?: (name: string) => string
 }
 
 export type NodeToJSX<NodeType, ReturnType> = (
diff --git a/packages/teleport-shared/src/utils/ast-jsx-utils.ts b/packages/teleport-shared/src/utils/ast-jsx-utils.ts
index 21fb4ff5..de0e7d0e 100644
--- a/packages/teleport-shared/src/utils/ast-jsx-utils.ts
+++ b/packages/teleport-shared/src/utils/ast-jsx-utils.ts
@@ -1,5 +1,5 @@
 import * as types from '@babel/types'
-import { convertValueToLiteral } from './ast-js-utils'
+import { convertValueToLiteral, objectToObjectExpression } from './ast-js-utils'
 
 /**
  * Adds a class definition string to an existing string of classes
@@ -146,3 +146,9 @@ export const renameJSXTag = (jsxTag: types.JSXElement, newName: string, t = type
     jsxTag.closingElement.name = t.jsxIdentifier(newName)
   }
 }
+
+export const createComponentDecorator = (params, t = types) => {
+  return t.decorator(
+    t.callExpression(t.identifier('Component'), [objectToObjectExpression(params)])
+  )
+}
diff --git a/packages/teleport-types/src/generators.ts b/packages/teleport-types/src/generators.ts
index bffe1a76..db7cdb52 100644
--- a/packages/teleport-types/src/generators.ts
+++ b/packages/teleport-types/src/generators.ts
@@ -141,7 +141,11 @@ export interface ProjectStrategy {
       uidl: ProjectUIDL,
       options: EntryFileOptions
     ) => Record<string, ChunkDefinition[]>
-    appRootOverride?: string
+    options?: {
+      appRootOverride?: string
+      customScriptTags?: CustomScriptTag[]
+      customLinkTags?: CustomLinkTag[]
+    }
   }
   static: {
     prefix?: string
@@ -149,9 +153,21 @@ export interface ProjectStrategy {
   }
 }
 
+export interface CustomLinkTag {
+  type: string
+  path: string
+}
+
+export interface CustomScriptTag {
+  type: string
+  path: string
+}
+
 export interface EntryFileOptions {
   assetsPrefix?: string
   appRootOverride?: string
+  customScriptTags?: CustomScriptTag[]
+  customLinkTags?: CustomLinkTag[]
 }
 
 export interface GeneratedFolder {
diff --git a/packages/teleport-types/src/uidl.ts b/packages/teleport-types/src/uidl.ts
index 78a1480b..f59cd02b 100755
--- a/packages/teleport-types/src/uidl.ts
+++ b/packages/teleport-types/src/uidl.ts
@@ -37,6 +37,8 @@ export interface ComponentUIDL {
     styleFileName?: string
     templateFileName?: string
     path?: string[]
+    title?: string
+    metaTags?: Array<Record<string, string>>
   }
   propDefinitions?: Record<string, UIDLPropDefinition>
   stateDefinitions?: Record<string, UIDLStateDefinition>
@@ -54,9 +56,12 @@ export interface UIDLStateDefinition {
   values?: Array<{
     value: string | number | boolean
     meta?: {
+      // Used when the StateDefinition is used as the router
       componentName?: string
       path?: string
       fileName?: string
+      title?: string
+      metaTags?: Array<Record<string, string>>
     }
     transitions?: any
   }>
@@ -168,7 +173,7 @@ export interface UIDLEventHandlerStatement {
 }
 
 export interface UIDLDependency {
-  type: string
+  type: 'library' | 'package' | 'local'
   path?: string
   version?: string
   meta?: {
diff --git a/packages/teleport-uidl-validator/src/uidl-schemas/component.json b/packages/teleport-uidl-validator/src/uidl-schemas/component.json
index 7230e4d5..4c5c67ee 100644
--- a/packages/teleport-uidl-validator/src/uidl-schemas/component.json
+++ b/packages/teleport-uidl-validator/src/uidl-schemas/component.json
@@ -105,10 +105,23 @@
                   "value": {},
                   "meta": { 
                     "type": "object",
+                    "additionalProperties": false,
                     "properties": {
                       "componentName": { "type": "string" },
                       "path": { "type": "string" },
-                      "fileName": { "type": "string" }
+                      "fileName": { "type": "string" },
+                      "title": { "type": "string" },
+                      "metaTags": {
+                        "type": "array",
+                        "items": {
+                          "type": "object",
+                          "patternProperties": {
+                            ".*": {
+                              "type": "string"
+                            }
+                          }
+                        }
+                      }
                     }
                   },
                   "transitions": { "type": "object" }


Test patch:
diff --git a/packages/teleport-component-generator-react/__tests__/end2end/index.ts b/packages/teleport-component-generator-react/__tests__/end2end/index.ts
index 336ebcb6..f51e3182 100644
--- a/packages/teleport-component-generator-react/__tests__/end2end/index.ts
+++ b/packages/teleport-component-generator-react/__tests__/end2end/index.ts
@@ -73,7 +73,7 @@ describe('React Component Generator', () => {
 
   describe('with Custom Mapping', () => {
     const mapping = { elements: { container: { elementType: 'fakediv' } } }
-    const generator = createReactComponentGenerator('InlineStyles', mapping)
+    const generator = createReactComponentGenerator('InlineStyles', [], mapping)
 
     it('should render <fakediv> tags', async () => {
       const result = await generator.generateComponent(uidlSample)
diff --git a/packages/teleport-plugin-jsx-head-config/__tests__/index.ts b/packages/teleport-plugin-jsx-head-config/__tests__/index.ts
new file mode 100644
index 00000000..339e4e57
--- /dev/null
+++ b/packages/teleport-plugin-jsx-head-config/__tests__/index.ts
@@ -0,0 +1,122 @@
+import * as types from '@babel/types'
+import { createPlugin } from '../src'
+import { component, elementNode } from '@teleporthq/teleport-shared/dist/cjs/builders/uidl-builders'
+import { ComponentStructure } from '@teleporthq/teleport-types'
+import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+
+describe('plugin-jsx-head-config', () => {
+  const plugin = createPlugin()
+  const jsxChunk = {
+    type: CHUNK_TYPE.AST,
+    fileType: FILE_TYPE.JS,
+    name: 'jsx-component',
+    content: {},
+    linkAfter: [],
+    meta: {
+      nodesLookup: {
+        container: {
+          type: 'JSXElement',
+          openingElement: {
+            type: 'JSXOpeningElement',
+            name: { type: 'JSXIdentifier', name: 'div' },
+            attributes: [],
+            selfClosing: false,
+          },
+          closingElement: {
+            type: 'JSXClosingElement',
+            name: { type: 'JSXIdentifier', name: 'div' },
+          },
+          children: [],
+        },
+      },
+    },
+  }
+
+  it('Should throw error when the chunk is supplied', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [],
+      dependencies: {},
+    }
+    try {
+      await plugin(structure)
+    } catch (e) {
+      expect(e.message).toContain('JSX component chunk with name')
+    }
+  })
+
+  it('Should set the title in the <Helmet> component', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    uidlSample.node.content.key = 'container'
+    uidlSample.meta = {
+      title: 'Test Title',
+    }
+
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [jsxChunk],
+      dependencies: {},
+    }
+
+    await plugin(structure)
+
+    const astNode = structure.chunks[0].meta.nodesLookup.container as types.JSXElement
+    expect(astNode.children.length).toBe(1)
+
+    const helmetNode = astNode.children[0] as types.JSXElement
+    expect((helmetNode.openingElement.name as types.JSXIdentifier).name).toBe('Helmet')
+
+    const titleNode = helmetNode.children[0] as types.JSXElement
+    const titleText = titleNode.children[0] as types.JSXText
+    expect((titleNode.openingElement.name as types.JSXIdentifier).name).toBe('title')
+    expect(titleText.value).toBe('Test Title')
+  })
+
+  it('Should set the meta tags in the <Helmet> component', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    uidlSample.node.content.key = 'container'
+    uidlSample.meta = {
+      metaTags: [
+        {
+          name: 'description',
+          value: 'test',
+        },
+        {
+          randomKey: 'randomValue',
+        },
+      ],
+    }
+
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [jsxChunk],
+      dependencies: {},
+    }
+
+    await plugin(structure)
+
+    const astNode = structure.chunks[0].meta.nodesLookup.container as types.JSXElement
+    expect(astNode.children.length).toBe(2)
+
+    const helmetNode = astNode.children[0] as types.JSXElement
+    expect((helmetNode.openingElement.name as types.JSXIdentifier).name).toBe('Helmet')
+
+    const firstMetaNode = helmetNode.children[0] as types.JSXElement
+    const secondMetaNode = helmetNode.children[1] as types.JSXElement
+
+    const nameAttribute = firstMetaNode.openingElement.attributes[0] as types.JSXAttribute
+    const valueAttribute = firstMetaNode.openingElement.attributes[1] as types.JSXAttribute
+    expect((nameAttribute.name as types.JSXIdentifier).name).toBe('name')
+    expect((nameAttribute.value as types.StringLiteral).value).toBe('description')
+    expect((valueAttribute.name as types.JSXIdentifier).name).toBe('value')
+    expect((valueAttribute.value as types.StringLiteral).value).toBe('test')
+
+    const randomKeyAttribute = secondMetaNode.openingElement.attributes[0] as types.JSXAttribute
+    expect((randomKeyAttribute.name as types.JSXIdentifier).name).toBe('randomKey')
+    expect((randomKeyAttribute.value as types.StringLiteral).value).toBe('randomValue')
+  })
+})
diff --git a/packages/teleport-plugin-vue-app-routing/__tests__/index.ts b/packages/teleport-plugin-vue-app-routing/__tests__/index.ts
index aba66607..f2e9e3ad 100644
--- a/packages/teleport-plugin-vue-app-routing/__tests__/index.ts
+++ b/packages/teleport-plugin-vue-app-routing/__tests__/index.ts
@@ -13,7 +13,7 @@ describe('plugin-vue-app-routing', () => {
     codeChunkName: 'code-chunk',
   })
 
-  it('outputs two AST chunks with the corresponding chunk names', async () => {
+  it('outputs three AST chunks with the corresponding chunk names', async () => {
     const structure = {
       chunks: [],
       options: {},
@@ -40,7 +40,7 @@ describe('plugin-vue-app-routing', () => {
     expect(result.chunks.length).toBe(1)
     expect(result.chunks[0].type).toBe(CHUNK_TYPE.AST)
     expect(result.chunks[0].content).toBeDefined()
-    expect(result.chunks[0].content.length).toBe(2)
+    expect(result.chunks[0].content.length).toBe(3)
     expect(result.chunks[0].name).toBe('code-chunk')
 
     // Dependencies
diff --git a/packages/teleport-plugin-vue-head-config/__tests__/index.ts b/packages/teleport-plugin-vue-head-config/__tests__/index.ts
new file mode 100644
index 00000000..70606658
--- /dev/null
+++ b/packages/teleport-plugin-vue-head-config/__tests__/index.ts
@@ -0,0 +1,107 @@
+import * as types from '@babel/types'
+import { createPlugin } from '../src'
+import { component, elementNode } from '@teleporthq/teleport-shared/dist/cjs/builders/uidl-builders'
+import { ComponentStructure } from '@teleporthq/teleport-types'
+import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+
+describe('plugin-vue-head-config', () => {
+  const plugin = createPlugin()
+
+  it('Should throw error when the chunk is supplied', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [],
+      dependencies: {},
+    }
+    try {
+      await plugin(structure)
+    } catch (e) {
+      expect(e.message).toContain('JS component chunk with name')
+    }
+  })
+
+  it('Should set the title in head object of the component', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    uidlSample.node.content.key = 'container'
+    uidlSample.meta = {
+      title: 'Test Title',
+    }
+
+    const jsChunk = {
+      type: CHUNK_TYPE.AST,
+      fileType: FILE_TYPE.JS,
+      name: 'vue-js-chunk',
+      content: {
+        declaration: {
+          properties: [],
+        },
+      },
+      linkAfter: [],
+    }
+
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [jsChunk],
+      dependencies: {},
+    }
+
+    await plugin(structure)
+
+    const headProperty = jsChunk.content.declaration.properties[0] as types.ObjectProperty
+    expect((headProperty.key as types.Identifier).name).toBe('head')
+
+    const headObject = headProperty.value as types.ObjectExpression
+    const titleProperty = headObject.properties[0] as types.ObjectProperty
+
+    expect(titleProperty.key.value).toBe('title')
+    expect(titleProperty.value.value).toBe('Test Title')
+  })
+
+  it('Should set the meta tags in the <Helmet> component', async () => {
+    const uidlSample = component('SimpleComponent', elementNode('container'))
+    uidlSample.node.content.key = 'container'
+    uidlSample.meta = {
+      metaTags: [
+        {
+          name: 'description',
+          value: 'test',
+        },
+        {
+          randomKey: 'randomValue',
+        },
+      ],
+    }
+
+    const jsChunk = {
+      type: CHUNK_TYPE.AST,
+      fileType: FILE_TYPE.JS,
+      name: 'vue-js-chunk',
+      content: {
+        declaration: {
+          properties: [],
+        },
+      },
+      linkAfter: [],
+    }
+
+    const structure: ComponentStructure = {
+      uidl: uidlSample,
+      options: {},
+      chunks: [jsChunk],
+      dependencies: {},
+    }
+
+    await plugin(structure)
+
+    const headProperty = jsChunk.content.declaration.properties[0] as types.ObjectProperty
+    expect((headProperty.key as types.Identifier).name).toBe('head')
+
+    const headObject = headProperty.value as types.ObjectExpression
+    const metaProperty = headObject.properties[0] as types.ObjectProperty
+
+    expect(metaProperty.key.value).toBe('meta')
+  })
+})
diff --git a/packages/teleport-project-generator-stencil/__tests__/end2end/index.ts b/packages/teleport-project-generator-stencil/__tests__/end2end/index.ts
new file mode 100644
index 00000000..21fa0bae
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/__tests__/end2end/index.ts
@@ -0,0 +1,39 @@
+// @ts-ignore
+import uidlSample from '../../../../examples/test-samples/project-sample.json'
+// @ts-ignore
+import invalidUidlSample from '../../../../examples/test-samples/project-invalid-sample.json'
+// @ts-ignore
+import template from './template-definition.json'
+import { createStencilProjectGenerator } from '../../src'
+import { ProjectUIDL } from '@teleporthq/teleport-types'
+
+const projectUIDL = uidlSample as ProjectUIDL
+
+describe('Preact Project Generator', () => {
+  const generator = createStencilProjectGenerator()
+
+  it('runs without crashing', async () => {
+    const outputFolder = await generator.generateProject(projectUIDL, template)
+    const assetsPath = generator.getAssetsPath()
+
+    expect(assetsPath).toBeDefined()
+    expect(outputFolder.name).toBe(template.name)
+    expect(outputFolder.files[0].name).toBe('package')
+
+    const srcFolder = outputFolder.subFolders[0]
+    const rootFiles = srcFolder.subFolders[0].files
+
+    expect(srcFolder.files[0].name).toBe('index')
+    expect(srcFolder.files[0].fileType).toBe('html')
+    expect(srcFolder.files[0].content).toContain('<script type="module" src="/build/app.esm.js">')
+    expect(rootFiles[0].name).toBe('app-root')
+    expect(rootFiles[0].fileType).toBe('tsx')
+    expect(srcFolder.subFolders[0].name).toBe('components')
+  })
+
+  it('throws error when invalid UIDL sample is used', async () => {
+    const result = generator.generateProject(invalidUidlSample, template)
+
+    await expect(result).rejects.toThrow(Error)
+  })
+})
diff --git a/packages/teleport-project-generator-stencil/__tests__/end2end/template-definition.json b/packages/teleport-project-generator-stencil/__tests__/end2end/template-definition.json
new file mode 100644
index 00000000..580a3a23
--- /dev/null
+++ b/packages/teleport-project-generator-stencil/__tests__/end2end/template-definition.json
@@ -0,0 +1,17 @@
+{
+  "name": "stencil",
+  "files": [],
+  "subFolders": [
+    {
+      "name": "src",
+      "files": [],
+      "subFolders": [
+        {
+          "name": "components",
+          "files": [],
+          "subFolders": []              
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/teleport-project-packer-test/package.json b/packages/teleport-project-packer-test/package.json
index bdd5dda5..574cc26f 100644
--- a/packages/teleport-project-packer-test/package.json
+++ b/packages/teleport-project-packer-test/package.json
@@ -22,11 +22,12 @@
     "start": "TS_NODE_FILES=true ts-node --project tsconfig.json ./src/index.ts"
   },
   "dependencies": {
+    "@teleporthq/teleport-project-generator-react": "^0.9.0",
     "@teleporthq/teleport-project-generator-next": "^0.9.0",
+    "@teleporthq/teleport-project-generator-vue": "^0.9.0",
     "@teleporthq/teleport-project-generator-nuxt": "^0.9.0",
     "@teleporthq/teleport-project-generator-preact": "^0.9.0",
-    "@teleporthq/teleport-project-generator-react": "^0.9.0",
-    "@teleporthq/teleport-project-generator-vue": "^0.9.0",
+    "@teleporthq/teleport-project-generator-stencil": "^0.9.0",
     "@teleporthq/teleport-project-packer": "^0.9.0",
     "@teleporthq/teleport-publisher-disk": "^0.9.0",
     "@teleporthq/teleport-types": "^0.9.0"
diff --git a/packages/teleport-project-packer-test/src/constants.ts b/packages/teleport-project-packer-test/src/constants.ts
index be8b2fc6..5c4de589 100644
--- a/packages/teleport-project-packer-test/src/constants.ts
+++ b/packages/teleport-project-packer-test/src/constants.ts
@@ -9,3 +9,5 @@ export const VUE_GITHUB_PROJECT = 'teleport-project-template-vue'
 export const NUXT_GITHUB_PROJECT = 'teleport-project-template-nuxt'
 
 export const PREACT_GITHUB_PROJECT = 'teleport-project-template-preact'
+
+export const STENCIL_GITHUB_PROJECT = 'teleport-project-template-stencil'
diff --git a/packages/teleport-project-packer-test/src/index.ts b/packages/teleport-project-packer-test/src/index.ts
index d9defbc3..87ac98d9 100644
--- a/packages/teleport-project-packer-test/src/index.ts
+++ b/packages/teleport-project-packer-test/src/index.ts
@@ -5,6 +5,7 @@ import nextGenerator from '@teleporthq/teleport-project-generator-next'
 import vueGenerator from '@teleporthq/teleport-project-generator-vue'
 import nuxtGenerator from '@teleporthq/teleport-project-generator-nuxt'
 import preactGenerator from '@teleporthq/teleport-project-generator-preact'
+import stencilGenerator from '@teleporthq/teleport-project-generator-stencil'
 
 import { createDiskPublisher } from '@teleporthq/teleport-publisher-disk'
 import { RemoteTemplateDefinition } from '@teleporthq/teleport-types'
@@ -18,6 +19,7 @@ import {
   VUE_GITHUB_PROJECT,
   NUXT_GITHUB_PROJECT,
   PREACT_GITHUB_PROJECT,
+  STENCIL_GITHUB_PROJECT,
 } from './constants'
 
 import projectUIDL from '../../../examples/uidl-samples/project.json'
@@ -28,6 +30,7 @@ const generators = {
   vue: vueGenerator,
   nuxt: nuxtGenerator,
   preact: preactGenerator,
+  stencil: stencilGenerator,
 }
 
 const getGithubRemoteDefinition = (username: string, repo: string): RemoteTemplateDefinition => {
@@ -40,6 +43,7 @@ const templates = {
   vue: getGithubRemoteDefinition(GITHUB_TEMPLATE_OWNER, VUE_GITHUB_PROJECT),
   nuxt: getGithubRemoteDefinition(GITHUB_TEMPLATE_OWNER, NUXT_GITHUB_PROJECT),
   preact: getGithubRemoteDefinition(GITHUB_TEMPLATE_OWNER, PREACT_GITHUB_PROJECT),
+  stencil: getGithubRemoteDefinition(GITHUB_TEMPLATE_OWNER, STENCIL_GITHUB_PROJECT),
 }
 
 const publisher = createDiskPublisher({
@@ -69,6 +73,7 @@ const run = async () => {
     await packProject('vue')
     await packProject('nuxt')
     await packProject('preact')
+    await packProject('stencil')
   } catch (e) {
     console.info(e)
   }