diff --git a/src/components/Widgets/MarkitupReactRenderer.js b/src/components/Widgets/MarkitupReactRenderer.js
new file mode 100644
index 00000000..26137f7d
--- /dev/null
+++ b/src/components/Widgets/MarkitupReactRenderer.js
@@ -0,0 +1,69 @@
+import React, { PropTypes } from 'react';
+import MarkupIt, { Syntax, JSONUtils } from 'markup-it';
+
+const defaultRenderers = {
+ 'doc': 'article',
+ 'header_one': 'h1',
+ 'header_two': 'h2',
+ 'header_three': 'h3',
+ 'header_four': 'h4',
+ 'header_five': 'h5',
+ 'header_six': 'h6',
+ 'paragraph': 'p',
+ 'ordered_list': 'ol',
+ 'unordered_list': 'ul',
+ 'list_item': 'li',
+ 'link': 'a',
+ 'image': 'img',
+ 'BOLD': 'strong',
+ 'ITALIC': 'em',
+ 'text': null,
+ 'unstyled': null,
+};
+
+export default class MarkitupReactRenderer extends React.Component {
+
+ renderToken = (token) => {
+ const { type, data, text, tokens } = token;
+ const element = defaultRenderers[type];
+
+ // Only render if type is registered as renderer
+ if (typeof element !== 'undefined') {
+ let children = null;
+ if (Array.isArray(tokens) && tokens.length) {
+ children = tokens.map(this.renderToken);
+ } else if (type === 'text') {
+ children = text;
+ }
+ if (element !== null) {
+ // If this is a react element
+ return React.createElement(element, data, children);
+ } else {
+ // If this is a text node
+ return children;
+ }
+ }
+ return null;
+ }
+
+ render() {
+ const { value, syntax } = this.props;
+
+ if (typeof this.parser === 'undefined') {
+ this.parser = new MarkupIt(syntax);
+ }
+
+ const content = this.parser.toContent(value);
+ const json = JSONUtils.encode(content);
+ // console.log(JSON.stringify(json, null, 2));
+
+ return (
+
{this.renderToken(json.token)}
+ );
+ }
+}
+
+MarkitupReactRenderer.propTypes = {
+ value: PropTypes.string,
+ syntax: PropTypes.instanceOf(Syntax).isRequired
+};
diff --git a/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js
new file mode 100644
index 00000000..65ede560
--- /dev/null
+++ b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import markdownSyntax from 'markup-it/syntaxes/markdown';
+import htmlSyntax from 'markup-it/syntaxes/html';
+import MarkitupReactRenderer from '../MarkitupReactRenderer';
+
+describe('MarkitupReactRenderer', () => {
+ it('should render markdown', () => {
+ const value = `
+# H1
+
+Text with **bold** & _em_ elements
+
+## H2
+
+* ul item 1
+* ul item 2
+
+### H3
+
+1. ol item 1
+1. ol item 2
+1. ol item 3
+
+#### H4
+
+[link title](http://google.com)
+
+##### H5
+
+![alt text](https://pbs.twimg.com/profile_images/678903331176214528/TQTdqGwD.jpg)
+
+###### H6
+
+`;
+ const component = renderer.create(
+
+ );
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should support custom syntax', () => {
+ const value = `
+`;
+ const component = renderer.create(
+
+ );
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render HTML', () => {
+ const value = 'Paragraph with inline element
';
+ const component = renderer.create(
+
+ );
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap
new file mode 100644
index 00000000..2f959a8f
--- /dev/null
+++ b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap
@@ -0,0 +1,90 @@
+exports[`MarkitupReactRenderer should render HTML 1`] = `
+
+
+
+ Paragraph with
+
+ inline
+
+ element
+
+
+
+`;
+
+exports[`MarkitupReactRenderer should render markdown 1`] = `
+
+
+
+ H1
+
+
+ Text with
+
+ bold
+
+ &
+
+ em
+
+ elements
+
+
+ H2
+
+
+ -
+ ul item 1
+
+ -
+ ul item 2
+
+
+
+ H3
+
+
+ -
+ ol item 1
+
+ -
+ ol item 2
+
+ -
+ ol item 3
+
+
+
+ H4
+
+
+
+ link title
+
+
+
+ H5
+
+
+
+
+
+ H6
+
+
+
+`;