diff --git a/components/__tests__/__snapshots__/index.test.js.snap b/components/__tests__/__snapshots__/index.test.js.snap
index e2261c093..2e507f928 100644
--- a/components/__tests__/__snapshots__/index.test.js.snap
+++ b/components/__tests__/__snapshots__/index.test.js.snap
@@ -6,6 +6,7 @@ Array [
"Anchor",
"AutoComplete",
"Alert",
+ "Avatar",
"BackTop",
"Badge",
"Breadcrumb",
diff --git a/components/avatar/__tests__/Avatar.test.js b/components/avatar/__tests__/Avatar.test.js
new file mode 100644
index 000000000..685cb1422
--- /dev/null
+++ b/components/avatar/__tests__/Avatar.test.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Avatar from '..';
+
+describe('Avatar Render', () => {
+ it('Render long string correctly', () => {
+ const wrapper = mount(TestString);
+ const children = wrapper.find('.ant-avatar-string');
+ expect(children.length).toBe(1);
+ });
+});
diff --git a/components/avatar/__tests__/__snapshots__/demo.test.js.snap b/components/avatar/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..34f60465b
--- /dev/null
+++ b/components/avatar/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,389 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ 6
+
+
+ 7
+
+
+ 8
+
+
+ 9
+
+
+ 0
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ 6
+
+
+ 7
+
+
+ 8
+
+
+ 9
+
+
+ 0
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ 6
+
+
+ 7
+
+
+ 8
+
+
+ 9
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `
+
+
+
+ U
+
+
+
+
+`;
+
+exports[`renders ./components/avatar/demo/type.md correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+ U
+
+
+
+
+
+
+ USER
+
+
+
+
+
+
+
+
+
+
+
+
+
+ U
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/components/avatar/__tests__/demo.test.js b/components/avatar/__tests__/demo.test.js
new file mode 100644
index 000000000..bb02eea33
--- /dev/null
+++ b/components/avatar/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest';
+
+demoTest('avatar');
diff --git a/components/avatar/demo/badge.md b/components/avatar/demo/badge.md
new file mode 100644
index 000000000..11d7b1074
--- /dev/null
+++ b/components/avatar/demo/badge.md
@@ -0,0 +1,29 @@
+---
+order: 3
+title:
+ zh-CN: 带徽标的头像
+ en-US: With Badge
+---
+
+## zh-CN
+
+通常用于消息提示。
+
+## en-US
+
+Usually used for messages remind.
+
+````jsx
+import { Avatar, Badge } from 'antd';
+
+ReactDOM.render(
+
+, mountNode);
+````
diff --git a/components/avatar/demo/basic.md b/components/avatar/demo/basic.md
new file mode 100644
index 000000000..d67a9ac36
--- /dev/null
+++ b/components/avatar/demo/basic.md
@@ -0,0 +1,33 @@
+---
+order: 0
+title:
+ zh-CN: 基本
+ en-US: Basic
+---
+
+## zh-CN
+
+头像有三种尺寸,两种形状可选。
+
+## en-US
+
+Three sizes and two shapes are available.
+
+````jsx
+import { Avatar, Row, Col } from 'antd';
+
+ReactDOM.render(
+
+, mountNode);
+````
diff --git a/components/avatar/demo/dynamic.md b/components/avatar/demo/dynamic.md
new file mode 100644
index 000000000..c4eda2706
--- /dev/null
+++ b/components/avatar/demo/dynamic.md
@@ -0,0 +1,49 @@
+---
+order: 2
+title:
+ zh-CN: 自动调整字符大小
+ en-US: Autoset Font Size
+---
+
+## zh-CN
+
+对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。
+
+## en-US
+
+For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.
+
+````jsx
+import { Avatar, Button } from 'antd';
+
+const UserList = ['U', 'Lucy', 'Tom', 'Edward'];
+const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
+
+class Autoset extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ user: UserList[0],
+ color: colorList[0],
+ };
+ }
+ changeUser = () => {
+ const index = UserList.indexOf(this.state.user);
+ this.setState({
+ user: index < UserList.length - 1 ? UserList[index + 1] : UserList[0],
+ color: index < colorList.length - 1 ? colorList[index + 1] : colorList[0],
+ });
+ }
+ render() {
+ return (
+
+
{this.state.user}
+
+
+ );
+ }
+}
+
+ReactDOM.render(
+, mountNode);
+````
diff --git a/components/avatar/demo/type.md b/components/avatar/demo/type.md
new file mode 100644
index 000000000..e24ab3386
--- /dev/null
+++ b/components/avatar/demo/type.md
@@ -0,0 +1,33 @@
+---
+order: 1
+title:
+ zh-CN: 类型
+ en-US: Type
+---
+
+## zh-CN
+
+支持三种类型:图片、Icon 以及字符,其中 Icon 和字符型可以自定义图标颜色及背景色。
+
+## en-US
+
+Image, Icon and letter are supported, and the latter two kinds avatar can have custom colors and background colors.
+
+````jsx
+import { Avatar, Row, Col } from 'antd';
+
+ReactDOM.render(
+
+
+
+ U
+ USER
+
+
+
+ U
+
+
+
+, mountNode);
+````
diff --git a/components/avatar/index.en-US.md b/components/avatar/index.en-US.md
new file mode 100644
index 000000000..d16865689
--- /dev/null
+++ b/components/avatar/index.en-US.md
@@ -0,0 +1,16 @@
+---
+category: Components
+type: Data Display
+title: Avatar
+---
+
+Avatars can be used to represent people or object, which supports image, antd-Icon, or letter.
+
+## API
+
+| Property | Description | Type | Default |
+|----------- |--------------------------------------------------------- | ---------- |-------|
+| type | to set the shape of avatar | Enum{ 'circle', 'square' } | `circle` |
+| size | to set the size of avatar | Enum{ 'large', 'small', 'default' } | `default` |
+| src | the address of a image avatar | string | - |
+| icon | the icon type of a icon avatar, see `Icon` Component | string | - |
diff --git a/components/avatar/index.tsx b/components/avatar/index.tsx
new file mode 100644
index 000000000..4320b0539
--- /dev/null
+++ b/components/avatar/index.tsx
@@ -0,0 +1,120 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Icon from '../icon';
+import classNames from 'classnames';
+
+export interface AvatarProps {
+ /** Shape of avatar, options:`circle`, `square` */
+ shape?: 'circle' | 'square';
+ /** Size of avatar, options:`large`, `small`, `default` */
+ size?: 'large' | 'small' | 'default';
+ /** Src of image avatar */
+ src?: string;
+ /** Type of the Icon to be used in avatar */
+ icon?: string;
+ style?: React.CSSProperties;
+ prefixCls?: string;
+ className?: string;
+ children?: any;
+}
+
+export default class Avatar extends React.Component {
+ static defaultProps = {
+ prefixCls: 'ant-avatar',
+ shape: 'circle',
+ size: 'default',
+ };
+
+ private avatarChildren: any;
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ scale: 1,
+ };
+ }
+
+ componentDidMount() {
+ this.setScale();
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ if (prevProps.children !== this.props.children
+ || (prevState.scale !== this.state.scale && this.state.scale === 1)) {
+ this.setScale();
+ }
+ }
+
+ setScale = () => {
+ const childrenNode = this.avatarChildren;
+ if (childrenNode) {
+ const childrenWidth = childrenNode.offsetWidth;
+ const avatarWidth = ReactDOM.findDOMNode(this).getBoundingClientRect().width;
+ // add 4px gap for each side to get better performance
+ if (avatarWidth - 8 < childrenWidth) {
+ this.setState({
+ scale: (avatarWidth - 8) / childrenWidth,
+ });
+ } else {
+ this.setState({
+ scale: 1,
+ });
+ }
+ }
+ }
+
+ render() {
+ const {
+ prefixCls, shape, size, src, icon, className, ...others,
+ } = this.props;
+
+ const sizeCls = classNames({
+ [`${prefixCls}-lg`]: size === 'large',
+ [`${prefixCls}-sm`]: size === 'small',
+ });
+
+ const classString = classNames(prefixCls, className, sizeCls, {
+ [`${prefixCls}-${shape}`]: shape,
+ [`${prefixCls}-image`]: src,
+ [`${prefixCls}-icon`]: icon,
+ });
+
+ let children = this.props.children;
+ if (src) {
+ children =
;
+ } else if (icon) {
+ children = ;
+ } else {
+ const childrenNode = this.avatarChildren;
+ if (childrenNode || this.state.scale !== 1) {
+ const childrenStyle: React.CSSProperties = {
+ msTransform: `scale(${this.state.scale})`,
+ WebkitTransform: `scale(${this.state.scale})`,
+ transform: `scale(${this.state.scale})`,
+ position: 'absolute',
+ display: 'inline-block',
+ left: `calc(50% - ${Math.round(childrenNode.offsetWidth / 2)}px)`,
+ };
+ children = this.avatarChildren = span}
+ style={childrenStyle}
+ >
+ {children}
+ ;
+ } else {
+ children = this.avatarChildren = span}
+ >
+ {children}
+ ;
+ }
+ }
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/components/avatar/index.zh-CN.md b/components/avatar/index.zh-CN.md
new file mode 100644
index 000000000..a9c45e59a
--- /dev/null
+++ b/components/avatar/index.zh-CN.md
@@ -0,0 +1,17 @@
+---
+category: Components
+subtitle: 头像
+type: Data Display
+title: Avatar
+---
+
+用来代表用户或事物,支持图片、图标或字符展示。
+
+## API
+
+| 参数 | 说明 | 类型 | 默认值 |
+|----------- |--------------------------------------------------------- | ---------- | ------- |
+| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
+| size | 设置头像的大小 | Enum{ 'large', 'small', 'default' } | `default` |
+| src | 图片类头像的资源地址 | string | - |
+| icon | 设置头像的图标类型,参考 `Icon` 组件 | string | - |
diff --git a/components/avatar/style/index.less b/components/avatar/style/index.less
new file mode 100644
index 000000000..cf9fce56c
--- /dev/null
+++ b/components/avatar/style/index.less
@@ -0,0 +1,47 @@
+@import "../../style/themes/default";
+
+@avatar-prefix-cls: ~"@{ant-prefix}-avatar";
+
+.@{avatar-prefix-cls} {
+ display: inline-block;
+ text-align: center;
+ background: @avatar-bg;
+ color: @avatar-color;
+ white-space: nowrap;
+ position: relative;
+ overflow: hidden;
+
+ .avatar-size(@avatar-size-base, @avatar-font-size-base);
+
+ &-lg {
+ .avatar-size(@avatar-size-lg, @avatar-font-size-lg);
+ }
+
+ &-sm {
+ .avatar-size(@avatar-size-sm, @avatar-font-size-sm);
+ }
+
+ &-square {
+ border-radius: @avatar-border-radius;
+ }
+
+ & > img {
+ width: 100%;
+ height: 100%;
+ }
+}
+
+.avatar-size(@size, @font-size) {
+ width: @size;
+ height: @size;
+ line-height: @size;
+ border-radius: @size / 2;
+
+ & > * {
+ line-height: @size;
+ }
+
+ &.@{avatar-prefix-cls}-icon {
+ font-size: @font-size;
+ }
+}
diff --git a/components/avatar/style/index.tsx b/components/avatar/style/index.tsx
new file mode 100644
index 000000000..3a3ab0de5
--- /dev/null
+++ b/components/avatar/style/index.tsx
@@ -0,0 +1,2 @@
+import '../../style/index.less';
+import './index.less';
diff --git a/components/index.tsx b/components/index.tsx
index 0d951e2c9..5264e5ce4 100644
--- a/components/index.tsx
+++ b/components/index.tsx
@@ -19,6 +19,8 @@ export { default as AutoComplete } from './auto-complete';
export { default as Alert } from './alert';
+export { default as Avatar } from './avatar';
+
export { default as BackTop } from './back-top';
export { default as Badge } from './badge';
diff --git a/components/style/themes/default.less b/components/style/themes/default.less
index 089fca9ad..b82dc1396 100644
--- a/components/style/themes/default.less
+++ b/components/style/themes/default.less
@@ -335,3 +335,14 @@
@back-top-color: #fff;
@back-top-bg: rgba(64, 64, 64, 0.4);
@back-top-hover-bg: rgba(64, 64, 64, 0.6);
+
+// Avatar
+@avatar-size-base: 32px;
+@avatar-size-lg: 40px;
+@avatar-size-sm: 24px;
+@avatar-font-size-base: 18px;
+@avatar-font-size-lg: 24px;
+@avatar-font-size-sm: 14px;
+@avatar-bg: #ccc;
+@avatar-color: #fff;
+@avatar-border-radius: @border-radius-base;
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index fc474223e..ff1355fb9 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -6,6 +6,7 @@ Array [
"Anchor",
"AutoComplete",
"Alert",
+ "Avatar",
"BackTop",
"Badge",
"Breadcrumb",