diff --git a/components/index.js b/components/index.js
index e37bca7b2..c86f04856 100644
--- a/components/index.js
+++ b/components/index.js
@@ -3,6 +3,7 @@ import './checkbox/style'
 import './icon/style'
 import './radio/style'
 import './grid/style'
+import './rate/style'
 
 export { default as Button } from './button'
 
@@ -13,3 +14,5 @@ export { default as Icon } from './icon'
 export { default as Radio } from './radio'
 
 export { default as Grid } from './grid'
+
+export { default as Rate } from './rate'
diff --git a/components/rate/Rate.vue b/components/rate/Rate.vue
new file mode 100644
index 000000000..ee6f9276b
--- /dev/null
+++ b/components/rate/Rate.vue
@@ -0,0 +1,119 @@
+<template>
+  <ul
+    :class="[prefixCls, disabled ? `${prefixCls}-disabled` : '', className]"
+    @mouseleave="onMouseLeave">
+    <template v-for="i in count">
+      <Star
+        ref="stars"
+        :index="i"
+        :disabled="disabled"
+        :prefix-cls="`${prefixCls}-star`"
+        :allowHalf="allowHalf"
+        :value="currentValue"
+        @onClick="onClick"
+        @onHover="onHover"
+        :key="i">
+        <template slot-scope="props">
+          <slot>
+            <Icon type="star"/>
+          </slot>
+        </template>
+      </Star>
+    </template>
+  </ul>
+</template>
+
+<script>
+import Star from './Star.vue';
+import Icon from '../icon/index';
+import { getOffsetLeft } from '../util/util';
+
+export default {
+  name: 'Rate',
+  props: {
+    count: {
+      type: Number,
+      default: 5,
+    },
+    value: {
+      type: Number,
+      default: 0,
+    },
+    defaultValue: {
+      type: Number,
+      default: 0,
+    },
+    onChange: {
+      type: Function,
+      default: () => {},
+    },
+    onHoverChange: {
+      type: Function,
+      default: () => {},
+    },
+    allowHalf: {
+      type: Boolean,
+      default: false,
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    className: String,
+  },
+  data() {
+    return {
+      prefixCls: 'ant-rate',
+      hoverValue: undefined,
+      currentValue: undefined,
+      markValue: undefined,
+    }
+  },
+  created () {
+    this.currentValue = this.markValue = this.value || this.defaultValue
+  },
+  watch: {
+    hoverValue(val) {
+      if(val === undefined) {
+        this.currentValue = this.markValue;
+        return;
+      }
+      this.currentValue = val;
+    }
+  },
+  methods: {
+    onClick(event, index) {
+      let clValue = this.getStarValue(index, event.pageX);
+      this.markValue = clValue;
+      this.onMouseLeave();
+      this.onChange(clValue);
+    },
+    onHover(event, index) {
+      this.hoverValue = this.getStarValue(index, event.pageX);
+      this.onHoverChange(this.hoverValue);
+    },
+    getStarDOM (index) {
+      return this.$refs.stars[index].$el
+    },
+    getStarValue (index, x) {
+      let value = index;
+      if (this.allowHalf) {
+        const leftEdge = getOffsetLeft(this.getStarDOM(0))
+        const width = getOffsetLeft(this.getStarDOM(1)) - leftEdge
+        if ((x - leftEdge - width * (index-1)) < width / 2) {
+          value -= 0.5
+        }
+      }
+      return value
+    },
+    onMouseLeave() {
+      this.hoverValue = undefined
+      this.onHoverChange(undefined);
+    },
+  },
+  components: {
+    Star,
+    Icon,
+  }
+}
+</script>
diff --git a/components/rate/Star.vue b/components/rate/Star.vue
new file mode 100644
index 000000000..3c4ec3d30
--- /dev/null
+++ b/components/rate/Star.vue
@@ -0,0 +1,52 @@
+<template>
+  <li
+    :class="getClassName()"
+    @click="onClick"
+    @mousemove="onHover">
+    <div :class="`${this.prefixCls}-first`"><slot></slot></div>
+    <div :class="`${this.prefixCls}-second`"><slot></slot></div>
+  </li>
+</template>
+<script>
+export default {
+  name: 'Star',
+  props: {
+    index: Number,
+    disabled: Boolean,
+    prefixCls: String,
+    allowHalf: Boolean,
+    value: Number,
+  },
+  data() {
+    return {
+    }
+  },
+  mounted() {
+  },
+  computed: {
+
+  },
+  methods: {
+    getClassName() {
+      const { prefixCls, index, value, allowHalf } = this;
+      const starValue = index;
+      if (allowHalf && value + 0.5 === starValue) {
+        return `${prefixCls} ${prefixCls}-half ${prefixCls}-active`;
+      }
+      return starValue <= value ? `${prefixCls} ${prefixCls}-full` : `${prefixCls} ${prefixCls}-zero`;
+    },
+    onClick(e) {
+      if(this.disabled) return;
+      this.$emit("onClick", e, this.index);
+    },
+    onHover(e) {
+      if(this.disabled) return;
+      this.$emit("onHover", e, this.index);
+    },
+  },
+  watch: {
+  },
+  commponents: {
+  }
+}
+</script>
diff --git a/components/rate/index.js b/components/rate/index.js
new file mode 100644
index 000000000..0c1d021f2
--- /dev/null
+++ b/components/rate/index.js
@@ -0,0 +1,3 @@
+import Rate from './Rate';
+
+export default Rate;
diff --git a/components/rate/style/index.js b/components/rate/style/index.js
new file mode 100644
index 000000000..3a3ab0de5
--- /dev/null
+++ b/components/rate/style/index.js
@@ -0,0 +1,2 @@
+import '../../style/index.less';
+import './index.less';
diff --git a/components/rate/style/index.less b/components/rate/style/index.less
new file mode 100644
index 000000000..e22dbbd0d
--- /dev/null
+++ b/components/rate/style/index.less
@@ -0,0 +1,73 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+@rate-prefix-cls: ~"@{ant-prefix}-rate";
+
+.@{rate-prefix-cls} {
+  margin: 0;
+  padding: 0;
+  list-style: none;
+  font-size: 20px;
+  display: inline-block;
+  vertical-align: middle;
+
+  &-disabled &-star {
+    cursor: not-allowed;
+    &:hover {
+      transform: scale(1);
+    }
+  }
+
+  &-star {
+    margin: 0;
+    padding: 0;
+    display: inline-block;
+    margin-right: 8px;
+    position: relative;
+    transition: all .3s;
+    color: @rate-star-bg;
+    cursor: pointer;
+
+    &-first,
+    &-second {
+      user-select: none;
+      transition: all .3s;
+    }
+
+    &:hover {
+      transform: scale(1.1);
+    }
+
+    &-first {
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 50%;
+      height: 100%;
+      overflow: hidden;
+      opacity: 0;
+    }
+
+    &-half &-first,
+    &-half &-second {
+      opacity: 1;
+    }
+
+    &-half &-first,
+    &-full &-second {
+      color: @rate-star-color;
+    }
+
+    &-half:hover &-first,
+    &-full:hover &-second {
+      color: tint(@rate-star-color, 20%);
+    }
+  }
+
+  &-text {
+    margin-left: 8px;
+    vertical-align: middle;
+    display: inline-block;
+    font-size: @font-size-base;
+  }
+}
diff --git a/components/util/util.js b/components/util/util.js
new file mode 100644
index 000000000..6d0070323
--- /dev/null
+++ b/components/util/util.js
@@ -0,0 +1,40 @@
+function getScroll(w, top) {
+  let ret = top ? w.pageYOffset : w.pageXOffset;
+  const method = top ? 'scrollTop' : 'scrollLeft';
+  if (typeof ret !== 'number') {
+    const d = w.document;
+    // ie6,7,8 standard mode
+    ret = d.documentElement[method];
+    if (typeof ret !== 'number') {
+      // quirks mode
+      ret = d.body[method];
+    }
+  }
+  return ret;
+}
+
+function getClientPosition(elem) {
+  let box;
+  let x;
+  let y;
+  const doc = elem.ownerDocument;
+  const body = doc.body;
+  const docElem = doc && doc.documentElement;
+  box = elem.getBoundingClientRect();
+  x = box.left;
+  y = box.top;
+  x -= docElem.clientLeft || body.clientLeft || 0;
+  y -= docElem.clientTop || body.clientTop || 0;
+  return {
+    left: x,
+    top: y,
+  };
+}
+
+export const getOffsetLeft = (el) => {
+  const pos = getClientPosition(el);
+  const doc = el.ownerDocument;
+  const w = doc.defaultView || doc.parentWindow;
+  pos.left += getScroll(w);
+  return pos.left;
+}
diff --git a/examples/index.js b/examples/index.js
index 4a4d55917..39fea342b 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -4,6 +4,7 @@ import Button from './button.vue'
 import Radio from './radio.vue'
 import Grid from './grid.vue'
 // import Dialog from './dialog.vue'
+import Rate from './rate.vue'
 import './index.less'
 new Vue({
   el: '#app',
@@ -13,6 +14,7 @@ new Vue({
       <Checkbox />
       <AntButton />
       <Radio />
+      <Rate />
     </div>
     `,
   components: {
@@ -21,5 +23,6 @@ new Vue({
     Checkbox,
     Grid,
     Radio,
+    Rate,
   },
 })
diff --git a/examples/rate.vue b/examples/rate.vue
new file mode 100644
index 000000000..b6f624e7f
--- /dev/null
+++ b/examples/rate.vue
@@ -0,0 +1,63 @@
+<template>
+  <div>
+    基本
+    <Rate className="custom"></Rate>
+    </br>
+    半星
+    <Rate :allowHalf="allowHalf"></Rate>
+    </br>
+    默认3颗星
+    <Rate :value="initValue"></Rate>
+    </br>
+    只读
+    <Rate :value="initValue" :disabled="disabled"></Rate>
+    </br>
+    回调函数
+    <Rate
+      :onChange="onChange"
+      :onHoverChange="onHoverChange"></Rate>
+    <span v-if="hoverValue">{{hoverValue}}stars</span>
+    <span v-if="rValue">{{rValue}}stars</span>
+    <br/>
+    <Rate
+      :allowHalf="allowHalf"
+      :onHoverChange="onHoverChangeAH"></Rate>
+    <span v-if="hoverValueAH">{{hoverValueAH}}stars</span>
+    </br>
+    自定义
+    <Rate :value="initValue">
+      <template slot-scope="props">
+        <span>A</span>
+      </template>
+    </Rate>
+  </div>
+</template>
+<script>
+import { Rate } from '../components/index'
+export default {
+  data () {
+    return {
+      allowHalf: true,
+      initValue: 3,
+      disabled: true,
+      hoverValue: undefined,
+      rValue: undefined,
+      hoverValueAH: undefined,
+    }
+  },
+  methods: {
+    onHoverChange(val) {
+      this.hoverValue = val;
+    },
+    onChange(val) {
+      this.rValue = val;
+    },
+    onHoverChangeAH(val) {
+      this.hoverValueAH = val;
+    }
+  },
+  components: {
+    Rate
+  },
+}
+</script>