diff --git a/.gitignore b/.gitignore
index fd4f2b0..a45a18c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 node_modules
 .DS_Store
+.idea/
+package-lock.json
\ No newline at end of file
diff --git a/abas/README2.md b/abas/README2.md
new file mode 100644
index 0000000..3cbd50c
--- /dev/null
+++ b/abas/README2.md
@@ -0,0 +1,22 @@
+# This readme is only for abas
+
+1. Checkout this wrapper repo into the same level of the project what you want to convert. e.g. abas-elements
+2. To fix the icons styles
+   Put 'WcIconMixin.js' file into abas-elements/src/mixins
+   Add 'import WcIconMixin from '../mixins/WcIconMixin';' in Icon.vue
+   Add mixin 'mixins: [WcIconMixin],' in Icon.vue
+3. Adjust 'vue.config.js' in abas-elements project by adding alias for the '@vue/web-component-wrapper'
+
+    '''
+      configureWebpack: {
+        resolve: {
+          alias: {
+            '@vue/web-component-wrapper': path.join(__dirname, '../vue-web-component-wrapper'),
+          },
+        },
+      },
+    '''
+4. add dependency into package.json in block 'devDependencies'
+    '''
+    "eslint-plugin-vue-libs": "^2.1.0"
+    '''
diff --git a/abas/WcIconMixin.js b/abas/WcIconMixin.js
new file mode 100644
index 0000000..fc720dc
--- /dev/null
+++ b/abas/WcIconMixin.js
@@ -0,0 +1,28 @@
+import { dom } from '@fortawesome/fontawesome-svg-core'
+
+/**
+ * @mixin
+ */
+export default {
+  mounted () {
+    const id = 'fa-styles'
+    const shadowRoot = this.getShadowRoot(this)
+    if (shadowRoot && !shadowRoot.getElementById(id)) {
+      const faStyles = document.createElement('style')
+      faStyles.setAttribute('id', id)
+      faStyles.textContent = dom.css()
+      shadowRoot.appendChild(faStyles)
+    }
+  },
+  methods: {
+    getShadowRoot (obj) {
+      if (obj.$parent) {
+        if (obj.$parent.$options && obj.$parent.$options.shadowRoot) {
+          return obj.$parent.$options.shadowRoot
+        }
+        return this.getShadowRoot(obj.$parent)
+      }
+      return null
+    }
+  }
+}
diff --git a/dist/vue-wc-wrapper.global.js b/dist/vue-wc-wrapper.global.js
index db8253a..ab07537 100644
--- a/dist/vue-wc-wrapper.global.js
+++ b/dist/vue-wc-wrapper.global.js
@@ -98,6 +98,54 @@ function getAttributes (node) {
   return res
 }
 
+function spreadProps (component) {
+  const result = {};
+  spreadNext(result, component);
+  return result
+}
+
+function spreadNext (result, component) {
+  if (component.props) {
+    appendProps(result, component.props);
+  }
+
+  if (component.mixins) {
+    component.mixins.forEach(function (mixin) {
+      spreadNext(result, mixin);
+    });
+  }
+  if (component.extends) {
+    spreadNext(result, component.extends);
+  }
+}
+
+function appendProps (result, props) {
+  if (Array.isArray(props)) {
+    processArrayProps(result, props);
+  } else {
+    processObjectProps(result, props);
+  }
+}
+
+function processObjectProps (result, props) {
+  for (const key in props) {
+    const camelKey = camelize(key);
+    if (!(camelKey in result)) {
+      result[camelKey] = props[key];
+    }
+  }
+}
+function processArrayProps (result, props) {
+  props.forEach(function (prop) {
+    if (typeof prop === 'string') {
+      const camelKey = camelize(prop);
+      if (!(camelKey in result)) {
+        result[camelKey] = undefined;
+      }
+    }
+  });
+}
+
 function wrap (Vue, Component) {
   const isAsync = typeof Component === 'function' && !Component.cid;
   let isInitialized = false;
@@ -112,13 +160,13 @@ function wrap (Vue, Component) {
       ? Component.options
       : Component;
 
+    // spread props
+    options.props = spreadProps(options);
     // extract props info
-    const propsList = Array.isArray(options.props)
-      ? options.props
-      : Object.keys(options.props || {});
+    const propsList = Object.keys(options.props || {});
     hyphenatedPropsList = propsList.map(hyphenate);
     camelizedPropsList = propsList.map(camelize);
-    const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
+    const originalPropsAsObject = options.props || {};
     camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
       map[key] = originalPropsAsObject[propsList[i]];
       return map
@@ -169,13 +217,13 @@ function wrap (Vue, Component) {
 
   class CustomElement extends HTMLElement {
     constructor () {
-      super();
-      this.attachShadow({ mode: 'open' });
+      const self = super();
+      self.attachShadow({ mode: 'open' });
 
-      const wrapper = this._wrapper = new Vue({
+      const wrapper = self._wrapper = new Vue({
         name: 'shadow-root',
-        customElement: this,
-        shadowRoot: this.shadowRoot,
+        customElement: self,
+        shadowRoot: self.shadowRoot,
         data () {
           return {
             props: {},
@@ -195,8 +243,8 @@ function wrap (Vue, Component) {
         let hasChildrenChange = false;
         for (let i = 0; i < mutations.length; i++) {
           const m = mutations[i];
-          if (isInitialized && m.type === 'attributes' && m.target === this) {
-            syncAttribute(this, m.attributeName);
+          if (isInitialized && m.type === 'attributes' && m.target === self) {
+            syncAttribute(self, m.attributeName);
           } else {
             hasChildrenChange = true;
           }
@@ -204,11 +252,11 @@ function wrap (Vue, Component) {
         if (hasChildrenChange) {
           wrapper.slotChildren = Object.freeze(toVNodes(
             wrapper.$createElement,
-            this.childNodes
+            self.childNodes
           ));
         }
       });
-      observer.observe(this, {
+      observer.observe(self, {
         childList: true,
         subtree: true,
         characterData: true,
diff --git a/dist/vue-wc-wrapper.js b/dist/vue-wc-wrapper.js
index 36b4469..ba33aef 100644
--- a/dist/vue-wc-wrapper.js
+++ b/dist/vue-wc-wrapper.js
@@ -95,6 +95,54 @@ function getAttributes (node) {
   return res
 }
 
+function spreadProps (component) {
+  const result = {};
+  spreadNext(result, component);
+  return result
+}
+
+function spreadNext (result, component) {
+  if (component.props) {
+    appendProps(result, component.props);
+  }
+
+  if (component.mixins) {
+    component.mixins.forEach(function (mixin) {
+      spreadNext(result, mixin);
+    });
+  }
+  if (component.extends) {
+    spreadNext(result, component.extends);
+  }
+}
+
+function appendProps (result, props) {
+  if (Array.isArray(props)) {
+    processArrayProps(result, props);
+  } else {
+    processObjectProps(result, props);
+  }
+}
+
+function processObjectProps (result, props) {
+  for (const key in props) {
+    const camelKey = camelize(key);
+    if (!(camelKey in result)) {
+      result[camelKey] = props[key];
+    }
+  }
+}
+function processArrayProps (result, props) {
+  props.forEach(function (prop) {
+    if (typeof prop === 'string') {
+      const camelKey = camelize(prop);
+      if (!(camelKey in result)) {
+        result[camelKey] = undefined;
+      }
+    }
+  });
+}
+
 function wrap (Vue, Component) {
   const isAsync = typeof Component === 'function' && !Component.cid;
   let isInitialized = false;
@@ -109,13 +157,13 @@ function wrap (Vue, Component) {
       ? Component.options
       : Component;
 
+    // spread props
+    options.props = spreadProps(options);
     // extract props info
-    const propsList = Array.isArray(options.props)
-      ? options.props
-      : Object.keys(options.props || {});
+    const propsList = Object.keys(options.props || {});
     hyphenatedPropsList = propsList.map(hyphenate);
     camelizedPropsList = propsList.map(camelize);
-    const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
+    const originalPropsAsObject = options.props || {};
     camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
       map[key] = originalPropsAsObject[propsList[i]];
       return map
@@ -166,13 +214,13 @@ function wrap (Vue, Component) {
 
   class CustomElement extends HTMLElement {
     constructor () {
-      super();
-      this.attachShadow({ mode: 'open' });
+      const self = super();
+      self.attachShadow({ mode: 'open' });
 
-      const wrapper = this._wrapper = new Vue({
+      const wrapper = self._wrapper = new Vue({
         name: 'shadow-root',
-        customElement: this,
-        shadowRoot: this.shadowRoot,
+        customElement: self,
+        shadowRoot: self.shadowRoot,
         data () {
           return {
             props: {},
@@ -192,8 +240,8 @@ function wrap (Vue, Component) {
         let hasChildrenChange = false;
         for (let i = 0; i < mutations.length; i++) {
           const m = mutations[i];
-          if (isInitialized && m.type === 'attributes' && m.target === this) {
-            syncAttribute(this, m.attributeName);
+          if (isInitialized && m.type === 'attributes' && m.target === self) {
+            syncAttribute(self, m.attributeName);
           } else {
             hasChildrenChange = true;
           }
@@ -201,11 +249,11 @@ function wrap (Vue, Component) {
         if (hasChildrenChange) {
           wrapper.slotChildren = Object.freeze(toVNodes(
             wrapper.$createElement,
-            this.childNodes
+            self.childNodes
           ));
         }
       });
-      observer.observe(this, {
+      observer.observe(self, {
         childList: true,
         subtree: true,
         characterData: true,
diff --git a/package.json b/package.json
index f7ec743..6abe05e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@vue/web-component-wrapper",
-  "version": "1.2.0",
+  "version": "1.3.0",
   "description": "wrap a vue component as a web component.",
   "main": "dist/vue-wc-wrapper.js",
   "unpkg": "dist/vue-wc-wrapper.global.js",
diff --git a/src/index.js b/src/index.js
index 4409bd7..4788a1a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,7 +6,8 @@ import {
   injectHook,
   getInitialProps,
   createCustomEvent,
-  convertAttributeValue
+  convertAttributeValue,
+  spreadProps
 } from './utils.js'
 
 export default function wrap (Vue, Component) {
@@ -23,13 +24,13 @@ export default function wrap (Vue, Component) {
       ? Component.options
       : Component
 
+    // spread props
+    options.props = spreadProps(options)
     // extract props info
-    const propsList = Array.isArray(options.props)
-      ? options.props
-      : Object.keys(options.props || {})
+    const propsList = Object.keys(options.props || {})
     hyphenatedPropsList = propsList.map(hyphenate)
     camelizedPropsList = propsList.map(camelize)
-    const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {}
+    const originalPropsAsObject = options.props || {}
     camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
       map[key] = originalPropsAsObject[propsList[i]]
       return map
diff --git a/src/utils.js b/src/utils.js
index 0834f03..f202c68 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -94,3 +94,52 @@ function getAttributes (node) {
   }
   return res
 }
+
+export function spreadProps (component) {
+  const result = {}
+  spreadNext(result, component)
+  return result
+}
+
+function spreadNext (result, component) {
+  if (component.props) {
+    appendProps(result, component.props)
+  }
+
+  if (component.mixins) {
+    component.mixins.forEach(function (mixin) {
+      spreadNext(result, mixin)
+    })
+  }
+  if (component.extends) {
+    spreadNext(result, component.extends)
+  }
+}
+
+function appendProps (result, props) {
+  if (Array.isArray(props)) {
+    processArrayProps(result, props)
+  } else {
+    processObjectProps(result, props)
+  }
+}
+
+function processObjectProps (result, props) {
+  for (const key in props) {
+    const camelKey = camelize(key)
+    if (!(camelKey in result)) {
+      result[camelKey] = props[key]
+    }
+  }
+}
+function processArrayProps (result, props) {
+  props.forEach(function (prop) {
+    if (typeof prop === 'string') {
+      const camelKey = camelize(prop)
+      if (!(camelKey in result)) {
+        result[camelKey] = undefined
+      }
+    }
+  })
+}
+
diff --git a/test/fixtures/spreadedProperties.html b/test/fixtures/spreadedProperties.html
new file mode 100644
index 0000000..e2d500f
--- /dev/null
+++ b/test/fixtures/spreadedProperties.html
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/test/setup.js b/test/setup.js
index 24e59e1..a69ac1b 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -20,6 +20,7 @@ module.exports = async function launchPage (name) {
 }
 
 beforeAll(async () => {
+  jest.setTimeout(10000)
   browser = await puppeteer.launch(puppeteerOptions)
   server = createServer({ root: process.cwd() })
   await new Promise((resolve, reject) => {
diff --git a/test/test.js b/test/test.js
index 9423d7a..665d0fd 100644
--- a/test/test.js
+++ b/test/test.js
@@ -16,12 +16,76 @@ test('properties', async () => {
     el.foo = 234
     el.someProp = 'lol'
   })
-  const newFoo = await page.evaluate(()  => el.vueComponent.foo)
+  const newFoo = await page.evaluate(() => el.vueComponent.foo)
   expect(newFoo).toBe(234)
-  const newBar = await page.evaluate(()  => el.vueComponent.someProp)
+  const newBar = await page.evaluate(() => el.vueComponent.someProp)
   expect(newBar).toBe('lol')
 })
 
+test('spreadedProperties', async () => {
+  const { page } = await launchPage(`spreadedProperties`)
+
+  // props from 'extends'
+  const p0 = await page.evaluate(() => el.p0)
+  expect(p0).toBe(undefined)
+
+  // props from 'extends'
+  const p1 = await page.evaluate(() => el.p1)
+  expect(p1).toBe('p1')
+
+  // props from 'mixins'
+  const m0 = await page.evaluate(() => el.m0)
+  expect(m0).toBe('m0')
+
+  // props from 'mixins'
+  const m1 = await page.evaluate(() => el.m1)
+  expect(m1).toBe('m1')
+
+  // array props
+  const c1 = await page.evaluate(() => el.c1)
+  const c2 = await page.evaluate(() => el.c2)
+  const m2a = await page.evaluate(() => el.m2a)
+  const m2b = await page.evaluate(() => el.m2b)
+  expect(c1).toBe(undefined)
+  expect(c2).toBe(undefined)
+  expect(m2a).toBe(undefined)
+  expect(m2b).toBe(undefined)
+
+  // props proxying: set
+  await page.evaluate(() => {
+    el.p0 = 'new-p0'
+    el.p1 = 'new-p1'
+    el.m0 = 'new-m0'
+    el.m1 = 'new-m1'
+    el.c1 = 'new-c1'
+    el.m2a = 'new-m2a'
+  })
+  const newP0 = await page.evaluate(() => el.vueComponent.p0)
+  expect(newP0).toBe('new-p0')
+  const newP1 = await page.evaluate(() => el.vueComponent.p1)
+  expect(newP1).toBe('new-p1')
+  const newM0 = await page.evaluate(() => el.vueComponent.m0)
+  expect(newM0).toBe('new-m0')
+  const newM1 = await page.evaluate(() => el.vueComponent.m1)
+  expect(newM1).toBe('new-m1')
+  const newC1 = await page.evaluate(() => el.vueComponent.c1)
+  expect(newC1).toBe('new-c1')
+  const newM2a = await page.evaluate(() => el.vueComponent.m2a)
+  expect(newM2a).toBe('new-m2a')
+
+  // set via attribute
+  await page.evaluate(() => {
+    el.setAttribute('c1', 'foo')
+    el.setAttribute('p1', 'foo2')
+    el.setAttribute('m1', 'bar')
+    el.setAttribute('m2a', 'bla')
+  })
+  expect(await page.evaluate(() => el.c1)).toBe('foo')
+  expect(await page.evaluate(() => el.p1)).toBe('foo2')
+  expect(await page.evaluate(() => el.m1)).toBe('bar')
+  expect(await page.evaluate(() => el.m2a)).toBe('bla')
+})
+
 test('attributes', async () => {
   const { page } = await launchPage(`attributes`)