diff --git a/opentech/static_src/src/app/src/api/index.js b/opentech/static_src/src/app/src/api/index.js
index cd360e714df61f83b2fe5f9eb35db4ead48b7650..a83e0854f6b31bd242c166ddbdcfefebd663b8d3 100644
--- a/opentech/static_src/src/app/src/api/index.js
+++ b/opentech/static_src/src/app/src/api/index.js
@@ -1,9 +1,10 @@
 import { fetchSubmission, fetchSubmissionsByRound } from '@api/submissions';
-import { fetchNotesForSubmission } from '@api/notes';
+import { createNoteForSubmission, fetchNotesForSubmission } from '@api/notes';
 
 export default {
     fetchSubmissionsByRound,
     fetchSubmission,
 
     fetchNotesForSubmission,
+    createNoteForSubmission,
 };
diff --git a/opentech/static_src/src/app/src/api/notes.js b/opentech/static_src/src/app/src/api/notes.js
index a797ba32ffbdf65267bb3a35252face9fc7c4721..7b51bfd2899ab70f80b839bc2b866cbce5a1b0eb 100644
--- a/opentech/static_src/src/app/src/api/notes.js
+++ b/opentech/static_src/src/app/src/api/notes.js
@@ -6,3 +6,10 @@ export function fetchNotesForSubmission(submissionID, visibility = 'internal') {
         page_size: 1000,
     });
 }
+
+
+export function createNoteForSubmission(submissionID, note) {
+    return apiFetch(`/apply/api/submissions/${submissionID}/comments/`, 'POST', {}, {
+        body: JSON.stringify(note),
+    });
+}
diff --git a/opentech/static_src/src/app/src/api/utils.js b/opentech/static_src/src/app/src/api/utils.js
index 094b3281fc9d5ee1ff4d5ea12e8ede48d92f40af..8471e751820ad9bfb9524ba4e0d8762c448c4bb1 100644
--- a/opentech/static_src/src/app/src/api/utils.js
+++ b/opentech/static_src/src/app/src/api/utils.js
@@ -1,8 +1,10 @@
+import Cookies from 'js-cookie';
+
 const getBaseUrl = () => {
     return process.env.API_BASE_URL;
 };
 
-export async function apiFetch(path, method = 'GET', params, options) {
+export async function apiFetch(path, method = 'GET', params, options = {}) {
     const url = new URL(getBaseUrl());
     url.pathname = path;
 
@@ -11,10 +13,27 @@ export async function apiFetch(path, method = 'GET', params, options) {
             url.searchParams.set(paramKey, paramValue);
         }
     }
+
+    if (['post', 'put', 'patch', 'delete'].includes(method.toLowerCase())) {
+        options.headers = {
+            ...options.headers,
+            'Content-Type': 'application/json',
+            'X-CSRFToken': getCSRFToken(),
+        };
+    }
+
     return fetch(url, {
         ...options,
+        headers: {
+            ...options.headers,
+            'Accept': 'application/json',
+        },
         method,
         mode: 'same-origin',
         credentials: 'include'
     });
 }
+
+function getCSRFToken() {
+    return Cookies.get('csrftoken');
+}
diff --git a/opentech/static_src/src/app/src/components/NoteListingItem.js b/opentech/static_src/src/app/src/components/NoteListingItem.js
index c7f88178d3821b33706d9739b0e9108ec94a71b1..17f89c38ba41cb6a9e47fb94ddb973343be6fb34 100644
--- a/opentech/static_src/src/app/src/components/NoteListingItem.js
+++ b/opentech/static_src/src/app/src/components/NoteListingItem.js
@@ -14,7 +14,7 @@ export default class NoteListingItem extends React.Component {
         return (
             <div>
                 <div style={{fontWeight: 'bold'}}>{user} - {timestamp.format('ll')}</div>
-                <div>{message}</div>
+                <div dangerouslySetInnerHTML={{__html: message}} />
             </div>
         );
     }
diff --git a/opentech/static_src/src/app/src/components/RichTextForm/index.js b/opentech/static_src/src/app/src/components/RichTextForm/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..6f333e15b1766c2e660ad4c3e3ce28dace5be1e0
--- /dev/null
+++ b/opentech/static_src/src/app/src/components/RichTextForm/index.js
@@ -0,0 +1,57 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import RichTextEditor from 'react-rte';
+
+export default class RichTextForm extends React.Component {
+    static defaultProps = {
+        disabled: false,
+        initialValue: '',
+    };
+
+    static propTypes = {
+        disabled: PropTypes.bool.isRequired,
+        onValueChange: PropTypes.func,
+        value: PropTypes.string,
+        onSubmit: PropTypes.func,
+    };
+
+    state = {
+        value: RichTextEditor.createEmptyValue(),
+    };
+
+    resetEditor = () => {
+        this.setState({value: RichTextEditor.createEmptyValue()});
+    }
+
+    render() {
+        const passProps = {
+            disabled: this.props.disabled,
+            onChange: this.handleValueChange,
+            value: this.state.value,
+        };
+
+        return (
+            <div>
+                <RichTextEditor {...passProps} />
+                <button
+                    disabled={this.isEmpty() || this.props.disabled}
+                    onClick={this.handleSubmit}
+                >
+                    Submit
+                </button>
+            </div>
+        );
+    }
+
+    isEmpty = () => {
+        return !this.state.value;
+    }
+
+    handleValueChange = value => {
+        this.setState({value});
+    }
+
+    handleSubmit = () => {
+        this.props.onSubmit(this.state.value.toString('markdown'), this.resetEditor);
+    }
+}
diff --git a/opentech/static_src/src/app/src/containers/AddNoteForm.js b/opentech/static_src/src/app/src/containers/AddNoteForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..88c94af365a1348ec70a1d0f59f1a1af3a09ff83
--- /dev/null
+++ b/opentech/static_src/src/app/src/containers/AddNoteForm.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+
+import { createNoteForSubmission } from '@actions/notes';
+import RichTextForm from '@components/RichTextForm';
+
+import {
+    getNoteCreatingErrorForSubmission,
+    getNoteCreatingStateForSubmission,
+} from '@selectors/notes';
+
+class AddNoteForm extends React.Component {
+    static propTypes = {
+        submitNote: PropTypes.func,
+        submissionID: PropTypes.number,
+        error: PropTypes.any,
+        isCreating: PropTypes.bool,
+    };
+
+    render() {
+        const { error, isCreating } = this.props;
+        return (
+            <>
+                {Boolean(error) && <p>{error}</p>}
+                <RichTextForm
+                    disabled={isCreating}
+                    onSubmit={this.onSubmit}
+                />
+            </>
+        );
+    }
+
+    onSubmit = async (message, resetEditor) => {
+        const action = await this.props.submitNote(this.props.submissionID, {
+            message,
+            visibility: 'internal',
+        });
+
+        if (action === true) {
+            resetEditor();
+        }
+    }
+}
+
+const mapStateToProps = (state, ownProps) => ({
+    error: getNoteCreatingErrorForSubmission(ownProps.submissionID)(state),
+    isCreating: getNoteCreatingStateForSubmission(ownProps.submissionID)(state),
+});
+
+const mapDispatchToProps = dispatch => ({
+    submitNote: (submissionID, note) => dispatch(createNoteForSubmission(submissionID, note)),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(AddNoteForm);
diff --git a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
index 0da90bb5bb0c4b23687ad2d7302be2320ee5b645..7ffbd0e622031a5afaf7f795c7ce3b0b1d60551a 100644
--- a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
+++ b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
@@ -13,6 +13,7 @@ import {
 } from '@selectors/submissions';
 
 import CurrentSubmissionDisplay from '@containers/CurrentSubmissionDisplay'
+import AddNoteForm from '@containers/AddNoteForm';
 import NoteListing from '@containers/NoteListing';
 import Tabber, {Tab} from '@components/Tabber'
 import './style.scss';
@@ -38,6 +39,7 @@ class DisplayPanel extends React.Component  {
         let tabs = [
             <Tab button="Notes" key="note">
                 <NoteListing submissionID={submissionID} />
+                <AddNoteForm submissionID={submissionID} />
             </Tab>,
             <Tab button="Status" key="status">
                 <p>Status</p>
diff --git a/opentech/static_src/src/app/src/containers/Note.js b/opentech/static_src/src/app/src/containers/Note.js
index 9f297145bc34b0939cef8354166084d8db41435d..58551e46f22ee5c19ebe6694e86e70938ff79d29 100644
--- a/opentech/static_src/src/app/src/containers/Note.js
+++ b/opentech/static_src/src/app/src/containers/Note.js
@@ -2,6 +2,7 @@ import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import moment from 'moment';
+import { markdown } from 'markdown';
 
 import { getNoteOfID } from '@selectors/notes';
 import NoteListingItem from '@components/NoteListingItem';
@@ -20,7 +21,7 @@ class Note extends React.Component {
 
         return <NoteListingItem
                 user={note.user}
-                message={note.message}
+                message={markdown.toHTML(note.message)}
                 timestamp={moment(note.timestamp)}
         />;
     }
diff --git a/opentech/static_src/src/app/src/containers/NoteListing.js b/opentech/static_src/src/app/src/containers/NoteListing.js
index 7f16d6c648ca597a8856d8eec3d77c2530c6d97f..c2f2b5c9a8d5e509ee92953a9e1976f238357e4a 100644
--- a/opentech/static_src/src/app/src/containers/NoteListing.js
+++ b/opentech/static_src/src/app/src/containers/NoteListing.js
@@ -21,14 +21,22 @@ class NoteListing extends React.Component {
     };
 
     componentDidUpdate(prevProps) {
-        const { submissionID } = this.props;
+        const { isLoading, loadNotes, submissionID } = this.props;
         const prevSubmissionID = prevProps.submissionID;
 
         if(
             submissionID !== null && submissionID !== undefined &&
-            prevSubmissionID !== submissionID && !this.props.isLoading
+            prevSubmissionID !== submissionID && !isLoading
         ) {
-            this.props.loadNotes(submissionID);
+            loadNotes(submissionID);
+        }
+    }
+
+    componentDidMount() {
+        const { isLoading, loadNotes, submissionID } = this.props;
+
+        if (submissionID && !isLoading) {
+            loadNotes(submissionID);
         }
     }
 
diff --git a/opentech/static_src/src/app/src/redux/actions/notes.js b/opentech/static_src/src/app/src/redux/actions/notes.js
index ecac7ad5fc3bac9f8cc82fc7db1a6fe8af6b96d6..49b3bacde145424d05dfbedaacac0b5aef7f1380 100644
--- a/opentech/static_src/src/app/src/redux/actions/notes.js
+++ b/opentech/static_src/src/app/src/redux/actions/notes.js
@@ -1,4 +1,4 @@
-import { updateSubmission } from '@actions/submissions';
+import { updateSubmission, appendNoteIDForSubmission } from '@actions/submissions';
 import api from '@api';
 
 export const FAIL_FETCHING_NOTES = 'FAIL_FETCHING_NOTES';
@@ -6,6 +6,9 @@ export const START_FETCHING_NOTES = 'START_FETCHING_NOTES';
 export const UPDATE_NOTES = 'UPDATE_NOTES';
 export const UPDATE_NOTE = 'UPDATE_NOTE';
 
+export const START_CREATING_NOTE_FOR_SUBMISSION = 'START_CREATING_NOTE_FOR_SUBMISSION';
+export const FAIL_CREATING_NOTE_FOR_SUBMISSION = 'FAIL_CREATING_NOTE_FOR_SUBMISSION';
+
 const startFetchingNotes = () => ({
     type: START_FETCHING_NOTES,
 });
@@ -32,7 +35,7 @@ export const fetchNotesForSubmission = submissionID => {
 };
 
 const updateNotesForSubmission = (submissionID, data) => {
-    return async function(dispatch) {
+    return function(dispatch) {
         dispatch(updateNotes(data));
         dispatch(updateSubmission(submissionID, {
             comments: data.results.map(v => v.id),
@@ -44,3 +47,48 @@ export const updateNotes = data => ({
     type: UPDATE_NOTES,
     data,
 });
+
+const startCreatingNoteForSubmission = submissionID => ({
+    type: START_CREATING_NOTE_FOR_SUBMISSION,
+    submissionID
+});
+
+const failCreatingNoteForSubmission = (submissionID, error) => ({
+    type: FAIL_CREATING_NOTE_FOR_SUBMISSION,
+    submissionID,
+    error
+});
+
+const updateNote = (data, submissionID) => ({
+    type: UPDATE_NOTE,
+    submissionID,
+    data,
+});
+
+const createdNoteForSubmission = (submissionID, data) => {
+    return function(dispatch) {
+        dispatch(updateNote(data, submissionID));
+        dispatch(appendNoteIDForSubmission(submissionID, data.id));
+        return true;
+    };
+};
+
+export const createNoteForSubmission = (submissionID, note) => {
+    return async function(dispatch) {
+        dispatch(startCreatingNoteForSubmission(submissionID));
+        try {
+            const response = await api.createNoteForSubmission(submissionID, note);
+            const json = await response.json();
+            if (!response.ok) {
+                return dispatch(failCreatingNoteForSubmission(
+                    submissionID,
+                    JSON.stringify(json)
+                ));
+            }
+            return dispatch(createdNoteForSubmission(submissionID, json));
+        } catch (e) {
+            console.error(e);
+            return dispatch(failCreatingNoteForSubmission(submissionID, e.message));
+        }
+    }
+};
diff --git a/opentech/static_src/src/app/src/redux/actions/submissions.js b/opentech/static_src/src/app/src/redux/actions/submissions.js
index 181f755f4fb588a3338a82c948b5ecd6a9b9a8ec..0070c2d50c0351217172f982064310e893cd4dd8 100644
--- a/opentech/static_src/src/app/src/redux/actions/submissions.js
+++ b/opentech/static_src/src/app/src/redux/actions/submissions.js
@@ -20,6 +20,9 @@ export const FAIL_LOADING_SUBMISSION = 'FAIL_LOADING_SUBMISSION';
 export const UPDATE_SUBMISSION = 'UPDATE_SUBMISSION';
 export const CLEAR_CURRENT_SUBMISSION = 'CLEAR_CURRENT_SUBMISSION';
 
+// Notes
+export const ADD_NOTE_FOR_SUBMISSION = 'ADD_NOTE_FOR_SUBMISSION';
+
 export const setCurrentSubmissionRound = id => ({
     type: SET_CURRENT_SUBMISSION_ROUND,
     id,
@@ -132,3 +135,9 @@ export const updateSubmission = (submissionID, data) => ({
 export const clearCurrentSubmission = () => ({
     type: CLEAR_CURRENT_SUBMISSION,
 });
+
+export const appendNoteIDForSubmission = (submissionID, noteID) => ({
+    type: ADD_NOTE_FOR_SUBMISSION,
+    submissionID,
+    noteID,
+});
diff --git a/opentech/static_src/src/app/src/redux/reducers/notes.js b/opentech/static_src/src/app/src/redux/reducers/notes.js
index 4137c6103999494d66ad54784cbb75397b974a0e..4c4a196fde9887c9862d70f3e6a84b7bd6b682b3 100644
--- a/opentech/static_src/src/app/src/redux/reducers/notes.js
+++ b/opentech/static_src/src/app/src/redux/reducers/notes.js
@@ -5,6 +5,8 @@ import {
     UPDATE_NOTES,
     START_FETCHING_NOTES,
     FAIL_FETCHING_NOTES,
+    START_CREATING_NOTE_FOR_SUBMISSION,
+    FAIL_CREATING_NOTE_FOR_SUBMISSION,
 } from '@actions/notes';
 
 function notesFetching(state = false, action) {
@@ -43,8 +45,44 @@ function note(state, action) {
     }
 }
 
+function notesCreating(state = [], action) {
+    switch (action.type) {
+        case START_CREATING_NOTE_FOR_SUBMISSION:
+            return [
+                ...state,
+                action.submissionID,
+            ];
+        case UPDATE_NOTE:
+        case FAIL_CREATING_NOTE_FOR_SUBMISSION:
+            return state.filter(v => v !== action.submissionID);
+        default:
+            return state
+    }
+}
+
+
+function notesFailedCreating(state = {}, action) {
+    switch (action.type) {
+        case UPDATE_NOTE:
+        case START_CREATING_NOTE_FOR_SUBMISSION:
+            return Object.entries(state).reduce((acc, [k, v]) => {
+                if (parseInt(k) !== action.submissionID) {
+                    acc[k] = v;
+                }
+                return acc;
+            }, {});
+        case FAIL_CREATING_NOTE_FOR_SUBMISSION:
+            return {
+                ...state,
+                [action.submissionID]: action.error,
+            };
+        default:
+            return state
+    }
+}
+
 function notesByID(state = {}, action) {
-    switch(action.type) {
+    switch (action.type) {
         case UPDATE_NOTES:
             return {
                 ...state,
@@ -56,6 +94,14 @@ function notesByID(state = {}, action) {
                     return newNotesAccumulator;
                 }, {}),
             };
+        case UPDATE_NOTE:
+            return {
+                ...state,
+                [action.data.id]: note(state[action.data.id], {
+                    type: UPDATE_NOTE,
+                    data: action.data,
+                }),
+            };
         default:
             return state;
     }
@@ -65,4 +111,6 @@ export default combineReducers({
     byID: notesByID,
     isFetching: notesFetching,
     isErrored: notesErrored,
+    createError: notesFailedCreating,
+    isCreating: notesCreating,
 });
diff --git a/opentech/static_src/src/app/src/redux/reducers/submissions.js b/opentech/static_src/src/app/src/redux/reducers/submissions.js
index 7563d1e25f0ac19364f82d0c96af3624f2125d4c..0c5253fd60552f4e909819a19728c3a2ca36e7f1 100644
--- a/opentech/static_src/src/app/src/redux/reducers/submissions.js
+++ b/opentech/static_src/src/app/src/redux/reducers/submissions.js
@@ -7,6 +7,7 @@ import {
     UPDATE_SUBMISSIONS_BY_ROUND,
     UPDATE_SUBMISSION,
     SET_CURRENT_SUBMISSION,
+    ADD_NOTE_FOR_SUBMISSION,
 } from '@actions/submissions';
 
 
@@ -31,6 +32,14 @@ function submission(state, action) {
                 isFetching: false,
                 isErrored: false,
             };
+        case ADD_NOTE_FOR_SUBMISSION:
+            return {
+                ...state,
+                comments: [
+                    action.noteID,
+                    ...(state.comments || []),
+                ]
+            };
         default:
             return state;
     }
@@ -41,6 +50,7 @@ function submissionsByID(state = {}, action) {
     switch(action.type) {
         case START_LOADING_SUBMISSION:
         case FAIL_LOADING_SUBMISSION:
+        case ADD_NOTE_FOR_SUBMISSION:
         case UPDATE_SUBMISSION:
             return {
                 ...state,
diff --git a/opentech/static_src/src/app/src/redux/selectors/notes.js b/opentech/static_src/src/app/src/redux/selectors/notes.js
index cf5ccc2fd82a54c81ba08884242409ac6b565117..cd035999ff96834f3262b618a24f43750aa251b8 100644
--- a/opentech/static_src/src/app/src/redux/selectors/notes.js
+++ b/opentech/static_src/src/app/src/redux/selectors/notes.js
@@ -16,3 +16,15 @@ export const getNoteIDsForSubmissionOfID = submissionID => createSelector(
     [getSubmissionOfID(submissionID)],
     submission => (submission || {}).comments || []
 );
+
+const getNoteCreatingErrors = state => state.notes.createError;
+
+export const getNoteCreatingErrorForSubmission = submissionID => createSelector(
+    [getNoteCreatingErrors], errors => errors[submissionID]
+);
+
+const getNoteCreatingState = state => state.notes.isCreating;
+
+export const getNoteCreatingStateForSubmission = submissionID => createSelector(
+    [getNoteCreatingState], creatingStates => creatingStates.includes(submissionID)
+);
diff --git a/package-lock.json b/package-lock.json
index 0d81ac8c189c5dbddb368f1447109a1b8f4eb150..2fb42fde9af8433c14839665a5110f1b4ca96206 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1456,6 +1456,11 @@
             "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
             "dev": true
         },
+        "asap": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+            "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+        },
         "asn1": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -1604,6 +1609,22 @@
                 "util.promisify": "1.0.0"
             }
         },
+        "babel-runtime": {
+            "version": "6.26.0",
+            "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+            "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+            "requires": {
+                "core-js": "^2.4.0",
+                "regenerator-runtime": "^0.11.0"
+            },
+            "dependencies": {
+                "regenerator-runtime": {
+                    "version": "0.11.1",
+                    "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+                    "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+                }
+            }
+        },
         "bach": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
@@ -2166,6 +2187,11 @@
             "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
             "dev": true
         },
+        "class-autobind": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/class-autobind/-/class-autobind-0.1.4.tgz",
+            "integrity": "sha1-NFFsSRZ8+NP2Od3Bhrz6Imiv/zQ="
+        },
         "class-utils": {
             "version": "0.3.6",
             "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -2187,6 +2213,11 @@
                 }
             }
         },
+        "classnames": {
+            "version": "2.2.6",
+            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+            "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+        },
         "clean-css": {
             "version": "4.2.1",
             "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
@@ -3109,6 +3140,63 @@
                 "domelementtype": "1.3.1"
             }
         },
+        "draft-js": {
+            "version": "0.10.5",
+            "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz",
+            "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==",
+            "requires": {
+                "fbjs": "^0.8.15",
+                "immutable": "~3.7.4",
+                "object-assign": "^4.1.0"
+            }
+        },
+        "draft-js-export-html": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/draft-js-export-html/-/draft-js-export-html-1.2.0.tgz",
+            "integrity": "sha1-HL4reOH+10/CnHzcv9dUBGjsogk=",
+            "requires": {
+                "draft-js-utils": "^1.2.0"
+            }
+        },
+        "draft-js-export-markdown": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/draft-js-export-markdown/-/draft-js-export-markdown-1.3.0.tgz",
+            "integrity": "sha512-kOiDGQ9KehcbYYcwzlkR+Gja6svEwIgId1gz3EtEVsZ09cxZaV13Qlkydm0J5wPy5Omthvdpj0Iw1B2E4BZRZQ==",
+            "requires": {
+                "draft-js-utils": "^1.2.0"
+            }
+        },
+        "draft-js-import-element": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/draft-js-import-element/-/draft-js-import-element-1.2.2.tgz",
+            "integrity": "sha512-atwfQFg5YWsKdBiOIkIYxYh9lOsS5gzzDaqV89GgG1UIb/E1689FI9PsH2OmuJ4DUhHouzBWAAPSa5DerGNnBQ==",
+            "requires": {
+                "draft-js-utils": "^1.2.4",
+                "synthetic-dom": "^1.2.0"
+            }
+        },
+        "draft-js-import-html": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/draft-js-import-html/-/draft-js-import-html-1.2.1.tgz",
+            "integrity": "sha512-FP1y9kdmOVDvOxoI4ny+H0g4CVoTQwdW++Zjf+qMsnz07NsYOCLcQ34j7TiwuPfArFAcOjBOc41Mn+qOa1G14w==",
+            "requires": {
+                "draft-js-import-element": "^1.2.1"
+            }
+        },
+        "draft-js-import-markdown": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/draft-js-import-markdown/-/draft-js-import-markdown-1.2.3.tgz",
+            "integrity": "sha512-NPcXwWSsIA+uwASzdJWLQM4y+xW1vTDtDdIDHCHfP76i9cx8zYpH75GW8Ezz8L9SW2qetNcFW056Hj2yxRZ+2g==",
+            "requires": {
+                "draft-js-import-element": "^1.2.1",
+                "synthetic-dom": "^1.2.0"
+            }
+        },
+        "draft-js-utils": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/draft-js-utils/-/draft-js-utils-1.2.4.tgz",
+            "integrity": "sha512-oy7WL6VCcSJ1WOUVCxkU0t/nGoLs/Kv0+zKalC61WjFTN4I2Lt1I8Oj5m4oUFBxfF7K9+0C0U5ilgvb4F4rovg=="
+        },
         "duplexer": {
             "version": "0.1.1",
             "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
@@ -3180,6 +3268,14 @@
             "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
             "dev": true
         },
+        "encoding": {
+            "version": "0.1.12",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+            "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+            "requires": {
+                "iconv-lite": "~0.4.13"
+            }
+        },
         "end-of-stream": {
             "version": "1.4.1",
             "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@@ -3921,6 +4017,27 @@
                 "websocket-driver": "0.7.0"
             }
         },
+        "fbjs": {
+            "version": "0.8.17",
+            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+            "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+            "requires": {
+                "core-js": "^1.0.0",
+                "isomorphic-fetch": "^2.1.1",
+                "loose-envify": "^1.0.0",
+                "object-assign": "^4.1.0",
+                "promise": "^7.1.1",
+                "setimmediate": "^1.0.5",
+                "ua-parser-js": "^0.7.18"
+            },
+            "dependencies": {
+                "core-js": {
+                    "version": "1.2.7",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+                    "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+                }
+            }
+        },
         "figgy-pudding": {
             "version": "3.5.1",
             "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
@@ -5711,7 +5828,6 @@
             "version": "0.4.24",
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
             "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-            "dev": true,
             "requires": {
                 "safer-buffer": "2.1.2"
             }
@@ -5749,6 +5865,11 @@
             "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
             "dev": true
         },
+        "immutable": {
+            "version": "3.7.6",
+            "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
+            "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
+        },
         "import-fresh": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
@@ -6260,8 +6381,7 @@
         "is-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-            "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-            "dev": true
+            "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
         },
         "is-symbol": {
             "version": "1.0.2",
@@ -6320,6 +6440,15 @@
             "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
             "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
         },
+        "isomorphic-fetch": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+            "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+            "requires": {
+                "node-fetch": "^1.0.1",
+                "whatwg-fetch": ">=0.10.0"
+            }
+        },
         "isstream": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -6330,6 +6459,11 @@
             "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz",
             "integrity": "sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g=="
         },
+        "js-cookie": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.0.tgz",
+            "integrity": "sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s="
+        },
         "js-levenshtein": {
             "version": "1.1.4",
             "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.4.tgz",
@@ -6788,6 +6922,24 @@
                 "object-visit": "1.0.1"
             }
         },
+        "markdown": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz",
+            "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=",
+            "requires": {
+                "nopt": "~2.1.1"
+            },
+            "dependencies": {
+                "nopt": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz",
+                    "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=",
+                    "requires": {
+                        "abbrev": "1"
+                    }
+                }
+            }
+        },
         "matchdep": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
@@ -7244,6 +7396,15 @@
             "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
             "dev": true
         },
+        "node-fetch": {
+            "version": "1.7.3",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+            "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+            "requires": {
+                "encoding": "^0.1.11",
+                "is-stream": "^1.0.1"
+            }
+        },
         "node-forge": {
             "version": "0.7.5",
             "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@@ -8146,6 +8307,14 @@
             "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
             "dev": true
         },
+        "promise": {
+            "version": "7.3.1",
+            "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+            "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+            "requires": {
+                "asap": "~2.0.3"
+            }
+        },
         "promise-inflight": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -8406,6 +8575,30 @@
                 }
             }
         },
+        "react-rte": {
+            "version": "0.16.1",
+            "resolved": "https://registry.npmjs.org/react-rte/-/react-rte-0.16.1.tgz",
+            "integrity": "sha512-CD5kf+6CHqOgJ1yB0i9tkMMch13wOXW5/FQx60gb7nzhXC1ZeFJjtW9dYfCVlfw1AvksHf+lMmKTjhIwyfZR7w==",
+            "requires": {
+                "babel-runtime": "^6.23.0",
+                "class-autobind": "^0.1.4",
+                "classnames": "^2.2.5",
+                "draft-js": ">=0.10.0",
+                "draft-js-export-html": ">=0.6.0",
+                "draft-js-export-markdown": ">=0.3.0",
+                "draft-js-import-html": ">=0.4.0",
+                "draft-js-import-markdown": ">=0.3.0",
+                "draft-js-utils": ">=0.2.0",
+                "immutable": "^3.8.1"
+            },
+            "dependencies": {
+                "immutable": {
+                    "version": "3.8.2",
+                    "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+                    "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
+                }
+            }
+        },
         "react-transition-group": {
             "version": "2.5.3",
             "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz",
@@ -9430,8 +9623,7 @@
         "setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
-            "dev": true
+            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
         },
         "setprototypeof": {
             "version": "1.1.0",
@@ -10072,6 +10264,11 @@
             "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
             "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
         },
+        "synthetic-dom": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/synthetic-dom/-/synthetic-dom-1.2.0.tgz",
+            "integrity": "sha1-81iar+K14pnzN7sylzqb5C3VYl4="
+        },
         "table": {
             "version": "4.0.3",
             "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz",
@@ -10503,6 +10700,11 @@
             "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
+        "ua-parser-js": {
+            "version": "0.7.19",
+            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz",
+            "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ=="
+        },
         "uglify-js": {
             "version": "3.4.9",
             "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
@@ -11549,6 +11751,11 @@
             "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
             "dev": true
         },
+        "whatwg-fetch": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+            "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+        },
         "which": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
diff --git a/package.json b/package.json
index cc620cb9e9bb1712650f2f7ff4e3598e0cacaf46..5757f29e394fe322874f8c8f567b4f1bbcb6871f 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,8 @@
         "gulp-size": "^3.0.0",
         "gulp-touch-cmd": "0.0.1",
         "gulp-uglify": "^3.0.1",
+        "js-cookie": "^2.2.0",
+        "markdown": "^0.5.0",
         "moment": "^2.24.0",
         "moment-timezone": "^0.5.23",
         "node-sass-import-once": "^1.2.0",
@@ -30,6 +32,7 @@
         "react": "^16.7.0",
         "react-dom": "^16.7.0",
         "react-redux": "^6.0.0",
+        "react-rte": "^0.16.1",
         "react-transition-group": "^2.5.3",
         "react-window-size-listener": "^1.2.3",
         "redux": "^4.0.1",