diff --git a/opentech/apply/activity/models.py b/opentech/apply/activity/models.py
index ea3e7101324996b16082ff1d3694b1d6b388ec6c..159bd219cf8270dd8e977cd087e633050363973d 100644
--- a/opentech/apply/activity/models.py
+++ b/opentech/apply/activity/models.py
@@ -41,6 +41,9 @@ class BaseActivityQuerySet(models.QuerySet):
     def visible_to(self, user):
         return self.filter(visibility__in=self.model.visibility_for(user))
 
+    def newer(self, activity):
+        return self.filter(timestamp__gt=activity.timestamp)
+
 
 class ActivityQuerySet(BaseActivityQuerySet):
     def comments(self):
diff --git a/opentech/apply/funds/api_views.py b/opentech/apply/funds/api_views.py
index ece53fb38815dfe6cac8a8578e7adc86c99cba00..cb989821e468641c770e06f16299d216132d7dd6 100644
--- a/opentech/apply/funds/api_views.py
+++ b/opentech/apply/funds/api_views.py
@@ -108,13 +108,22 @@ class RoundLabList(generics.ListAPIView):
     pagination_class = StandardResultsSetPagination
 
 
+class NewerThanFilter(filters.ModelChoiceFilter):
+    def filter(self, qs, value):
+        if not value:
+            return qs
+
+        return qs.newer(value)
+
+
 class CommentFilter(filters.FilterSet):
     since = filters.DateTimeFilter(field_name="timestamp", lookup_expr='gte')
     before = filters.DateTimeFilter(field_name="timestamp", lookup_expr='lte')
+    newer = NewerThanFilter(queryset=Activity.comments.all())
 
     class Meta:
         model = Activity
-        fields = ['submission', 'visibility', 'since', 'before']
+        fields = ['submission', 'visibility', 'since', 'before', 'newer']
 
 
 class CommentList(generics.ListAPIView):
@@ -138,7 +147,7 @@ class CommentListCreate(generics.ListCreateAPIView):
         permissions.IsAuthenticated, IsApplyStaffUser,
     )
     filter_backends = (filters.DjangoFilterBackend,)
-    filter_fields = ('visibility',)
+    filter_class = CommentFilter
     pagination_class = StandardResultsSetPagination
 
     def get_queryset(self):
diff --git a/opentech/static_src/src/app/src/api/index.js b/opentech/static_src/src/app/src/api/index.js
index e71a708a4cda32b21936364955ec21d3e5bd8fd9..037ed1a4e92fa98a06c62ccd0bbebe783c2c4f14 100644
--- a/opentech/static_src/src/app/src/api/index.js
+++ b/opentech/static_src/src/app/src/api/index.js
@@ -1,6 +1,6 @@
 import { fetchSubmission, fetchSubmissionsByRound, fetchSubmissionsByStatuses } from '@api/submissions';
 import { fetchRound, fetchRounds } from '@api/rounds';
-import { createNoteForSubmission, fetchNotesForSubmission } from '@api/notes';
+import { createNoteForSubmission, fetchNotesForSubmission, fetchNewNotesForSubmission } from '@api/notes';
 
 export default {
     fetchSubmissionsByRound,
@@ -11,5 +11,6 @@ export default {
     fetchRounds,
 
     fetchNotesForSubmission,
+    fetchNewNotesForSubmission,
     createNoteForSubmission,
 };
diff --git a/opentech/static_src/src/app/src/api/notes.js b/opentech/static_src/src/app/src/api/notes.js
index ec431cf28874a8b7baed6976de7a1988a751c5ab..436c45909048736786d4c85064a16664dc0d4f4d 100644
--- a/opentech/static_src/src/app/src/api/notes.js
+++ b/opentech/static_src/src/app/src/api/notes.js
@@ -9,6 +9,18 @@ export function fetchNotesForSubmission(submissionID, visibility = 'internal') {
 }
 
 
+export function fetchNewNotesForSubmission(submissionID, latestID, visibility = 'internal') {
+    return {
+        path: `/apply/api/submissions/${submissionID}/comments/`,
+        params: {
+            visibility,
+            newer: latestID,
+            page_size: 1000,
+        }
+    };
+}
+
+
 export function createNoteForSubmission(submissionID, note) {
     return {
         path: `/apply/api/submissions/${submissionID}/comments/`,
diff --git a/opentech/static_src/src/app/src/components/InlineLoading/index.js b/opentech/static_src/src/app/src/components/InlineLoading/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..14ec266d66299f7d1cb8c4b1dcb2087a7b0869dc
--- /dev/null
+++ b/opentech/static_src/src/app/src/components/InlineLoading/index.js
@@ -0,0 +1,17 @@
+import React from 'react'
+
+import OTFLoadingIcon from '@components/OTFLoadingIcon'
+
+import './styles.scss';
+
+const InlineLoading = () => {
+    return (
+        <div className="loading-inline">
+            <div className="loading-inline__icon">
+                <OTFLoadingIcon />
+            </div>
+        </div>
+    )
+}
+
+export default InlineLoading
diff --git a/opentech/static_src/src/app/src/components/InlineLoading/styles.scss b/opentech/static_src/src/app/src/components/InlineLoading/styles.scss
new file mode 100644
index 0000000000000000000000000000000000000000..06f6542444ea8378cf307419d05c0cb6f7554301
--- /dev/null
+++ b/opentech/static_src/src/app/src/components/InlineLoading/styles.scss
@@ -0,0 +1,13 @@
+.loading-inline {
+    width: 100%;
+    text-align: center;
+
+    &__icon {
+        padding: 5px;
+
+        svg {
+            height: 30px;
+            width: 30px;
+        }
+    }
+}
diff --git a/opentech/static_src/src/app/src/components/Listing/index.js b/opentech/static_src/src/app/src/components/Listing/index.js
index a4d86373e65bac32b4f10ca16c9a5518f08c9c40..5e247b5b99a0fd049fe1d84ddbd4e6f42e7bb56c 100644
--- a/opentech/static_src/src/app/src/components/Listing/index.js
+++ b/opentech/static_src/src/app/src/components/Listing/index.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import { TransitionGroup } from 'react-transition-group';
 
 import LoadingPanel from '@components/LoadingPanel';
+import InlineLoading from '@components/InlineLoading'
 import EmptyPanel from '@components/EmptyPanel';
 
 import SadNoteIcon from 'images/sad-note.svg';
@@ -34,30 +35,52 @@ export default class Listing extends React.Component {
             listRef,
         } = this.props;
 
-        if (isLoading) {
-            return (
-                <div className="listing__list">
-                    <LoadingPanel />
-                </div>
-            );
-        } else if (isError) {
-            return this.renderError();
-        } else if (items.length === 0) {
-            return <EmptyPanel column={this.props.column} />;
+        if ( items.length === 0 ) {
+            if (isLoading) {
+                return (
+                    <div className="listing__list">
+                        <LoadingPanel />
+                    </div>
+                );
+            } else if (isError) {
+                return this.renderError();
+            } else {
+                return <EmptyPanel column={this.props.column} />;
+            }
         }
 
         return (
-            <ul className={`listing__list listing__list--${column}`} ref={listRef}>
-                <TransitionGroup>
-                    {items.map(v => renderItem(v))}
-                </TransitionGroup>
-            </ul>
+            <>
+                { isLoading && <InlineLoading /> }
+                <ul className={`listing__list listing__list--${column}`} ref={listRef}>
+                    { isError && this.renderErrorItem() }
+                    <TransitionGroup component={null} >
+                        {items.map(v => renderItem(v))}
+                    </TransitionGroup>
+                </ul>
+            </>
         );
     }
 
+    renderRetryButton = () => {
+        const { handleRetry } = this.props;
+        return <a className="listing__help-link" onClick={handleRetry}>Refresh</a>;
+    }
+
+    renderErrorItem = () => {
+        const { handleRetry, error } = this.props;
+        return (
+            <li className={`listing__item listing__item--error`}>
+                <h5>Something went wrong!</h5>
+                <p>{ error }</p>
+                { !navigator.onLine && <p>You appear to be offline.</p>}
+                { handleRetry && this.renderRetryButton() }
+            </li>
+        )
+    }
+
     renderError = () => {
         const { handleRetry, error, column } = this.props;
-        const retryButton = <a className="listing__help-link" onClick={handleRetry}>Refresh</a>;
 
         return (
             <div className={`listing__list listing__list--${column} is-blank`}>
@@ -67,14 +90,14 @@ export default class Listing extends React.Component {
                     <p>Something went wrong!</p>
                 }
 
-                {handleRetry && retryButton &&
+                {handleRetry &&
                     <>
                         <div className="listing__blank-icon">
                             <SadNoteIcon  />
                         </div>
                         <p className="listing__help-text listing__help-text--standout">Something went wrong!</p>
                         <p className="listing__help-text">Sorry we couldn&apos;t load the notes</p>
-                        {retryButton}
+                        { this.renderRetryButton() }
                     </>
                 }
             </div>
diff --git a/opentech/static_src/src/app/src/components/Listing/style.scss b/opentech/static_src/src/app/src/components/Listing/style.scss
index 618ac101961b8804509915306bdf585d3f97ae57..0f3b6ca6b3689069d14a53e3390c7334d496cc86 100644
--- a/opentech/static_src/src/app/src/components/Listing/style.scss
+++ b/opentech/static_src/src/app/src/components/Listing/style.scss
@@ -53,6 +53,14 @@
     &__item {
         @include submission-list-item;
 
+        &--error {
+            padding: 5px 20px 5px;
+
+            p {
+                margin: 5px 0px;
+            }
+        }
+
         &.is-active {
             @include target-edge {
                 margin-left: 8px;
@@ -71,6 +79,7 @@
         }
     }
 
+
     // <a> tags
     &__link {
         display: block;
diff --git a/opentech/static_src/src/app/src/containers/NoteListing.js b/opentech/static_src/src/app/src/containers/NoteListing.js
index a6f80fbf6135cabc2ef70e86f5443ee42ef29756..23eb7fc19699442759727c9f8b2f410d482ea282 100644
--- a/opentech/static_src/src/app/src/containers/NoteListing.js
+++ b/opentech/static_src/src/app/src/containers/NoteListing.js
@@ -1,54 +1,43 @@
-import React from 'react';
+import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import { CSSTransition } from 'react-transition-group';
 
-import { fetchNotesForSubmission } from '@actions/notes';
+import useInterval from "@rooks/use-interval"
+
+import { fetchNewNotesForSubmission } from '@actions/notes';
 import Listing from '@components/Listing';
 import Note from '@containers/Note';
 import {
     getNotesErrorState,
+    getNotesErrorMessage,
     getNoteIDsForSubmissionOfID,
     getNotesFetchState,
 } from '@selectors/notes';
 
-class NoteListing extends React.Component {
-    static propTypes = {
-        loadNotes: PropTypes.func,
-        submissionID: PropTypes.number,
-        noteIDs: PropTypes.array,
-        isErrored: PropTypes.bool,
-        isLoading: PropTypes.bool,
-    };
-
-    componentDidUpdate(prevProps) {
-        const { isLoading, loadNotes, submissionID } = this.props;
-        const prevSubmissionID = prevProps.submissionID;
 
-        if(
-            submissionID !== null && submissionID !== undefined &&
-            prevSubmissionID !== submissionID && !isLoading
-        ) {
-            loadNotes(submissionID);
-        }
-    }
+const NoteListing = ({loadNotes, submissionID, noteIDs, isErrored, errorMessage, isLoading }) => {
+    const fetchNotes = () => loadNotes(submissionID)
 
-    componentDidMount() {
-        const { isLoading, loadNotes, submissionID } = this.props;
+    const {start, stop } = useInterval(fetchNotes, 30000)
 
-        if (submissionID && !isLoading) {
-            loadNotes(submissionID);
+    useEffect( () => {
+        if ( submissionID ) {
+            fetchNotes()
+            start()
+        } else {
+            stop()
         }
-    }
+    }, [submissionID])
+
 
-    handleRetry = () => {
-        if (this.props.isLoading || !this.props.isErrored) {
-            return;
+    const handleRetry = () => {
+        if (!isLoading || isErrored) {
+            fetchNotes()
         }
-        this.props.loadNotes(this.props.submissionID);
     }
 
-    renderItem = noteID => {
+    const renderItem = noteID => {
         return (
             <CSSTransition key={`note-${noteID}`} timeout={200} classNames="add-note">
                 <Note key={`note-${noteID}`} noteID={noteID} />
@@ -56,29 +45,38 @@ class NoteListing extends React.Component {
         );
     }
 
-    render() {
-        const { noteIDs } = this.props;
-        const passProps = {
-            isLoading: this.props.isLoading,
-            isError: this.props.isErrored,
-            handleRetry: this.handleRetry,
-            renderItem: this.renderItem,
-            items: noteIDs,
-        };
-        return (
-            <Listing {...passProps} column="notes" />
-        );
-    }
+    return (
+        <Listing
+            isLoading={ isLoading }
+            isError={ isErrored }
+            error={ errorMessage }
+            handleRetry={ handleRetry }
+            renderItem={ renderItem }
+            items={ noteIDs }
+            column="notes"
+        />
+    );
 }
 
+NoteListing.propTypes = {
+    loadNotes: PropTypes.func,
+    submissionID: PropTypes.number,
+    noteIDs: PropTypes.array,
+    isErrored: PropTypes.bool,
+    errorMessage: PropTypes.string,
+    isLoading: PropTypes.bool,
+};
+
+
 const mapDispatchToProps = dispatch => ({
-    loadNotes: submissionID => dispatch(fetchNotesForSubmission(submissionID)),
+    loadNotes: submissionID => dispatch(fetchNewNotesForSubmission(submissionID)),
 });
 
 const mapStateToProps = (state, ownProps) => ({
     noteIDs: getNoteIDsForSubmissionOfID(ownProps.submissionID)(state),
     isLoading: getNotesFetchState(state),
     isErrored: getNotesErrorState(state),
+    errorMessage: getNotesErrorMessage(state),
 });
 
 export default connect(mapStateToProps, mapDispatchToProps)(NoteListing);
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 b0200db84a47cf27a91b4162eaa4fe1bb79c8384..ac9b90a867324d84076b33e45e0884272ae5ef4e 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,5 @@
 import { CALL_API } from '@middleware/api'
+import { getLatestNoteForSubmissionOfID } from '@selectors/notes'
 
 import api from '@api';
 
@@ -34,3 +35,22 @@ const createNote = (submissionID, note) => ({
     },
     submissionID,
 })
+
+
+export const fetchNewNotesForSubmission = (submissionID) => (dispatch, getState) => {
+    const latestNoteID = getLatestNoteForSubmissionOfID(submissionID)(getState());
+    if ( latestNoteID ) {
+        return dispatch(fetchNewerNotes(submissionID, latestNoteID))
+    } else {
+        return dispatch(fetchNotes(submissionID))
+    }
+}
+
+
+const fetchNewerNotes = (submissionID, latestID) => ({
+    [CALL_API]: {
+        types: [ START_FETCHING_NOTES, UPDATE_NOTES, FAIL_FETCHING_NOTES],
+        endpoint: api.fetchNewNotesForSubmission(submissionID, latestID),
+    },
+    submissionID,
+})
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 4c4a196fde9887c9862d70f3e6a84b7bd6b682b3..f27e5d351d4ad7a9a90173a4aa72fdeeda94dee6 100644
--- a/opentech/static_src/src/app/src/redux/reducers/notes.js
+++ b/opentech/static_src/src/app/src/redux/reducers/notes.js
@@ -21,13 +21,20 @@ function notesFetching(state = false, action) {
     }
 }
 
-function notesErrored(state = false, action) {
+function notesErrored(state = {errored: false, message: null}, action) {
     switch (action.type) {
         case UPDATE_NOTES:
         case START_FETCHING_NOTES:
-            return false;
+        return {
+            ...state,
+            errored: false,
+        };
         case FAIL_FETCHING_NOTES:
-            return true;
+            return {
+                ...state,
+                message: action.error,
+                errored: true,
+            };
         default:
             return state;
     }
@@ -110,7 +117,7 @@ function notesByID(state = {}, action) {
 export default combineReducers({
     byID: notesByID,
     isFetching: notesFetching,
-    isErrored: notesErrored,
+    error: 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 946039d231a22cffcd039e6b96c4c212fd94df09..7d76f0f3eaa44fc2785366054093b05541c1ff60 100644
--- a/opentech/static_src/src/app/src/redux/reducers/submissions.js
+++ b/opentech/static_src/src/app/src/redux/reducers/submissions.js
@@ -15,7 +15,7 @@ import {
 import { UPDATE_NOTES, UPDATE_NOTE } from '@actions/notes'
 
 
-function submission(state, action) {
+function submission(state={comments: []}, action) {
     switch(action.type) {
         case START_LOADING_SUBMISSION:
             return {
@@ -39,7 +39,10 @@ function submission(state, action) {
         case UPDATE_NOTES:
             return {
                 ...state,
-                comments: action.data.results.map(note => note.id),
+                comments: action.data.results
+                    .map(note => note.id)
+                    .filter(id => !state.comments.includes(id))
+                    .concat(state.comments)
             };
         case UPDATE_NOTE:
             return {
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 cd035999ff96834f3262b618a24f43750aa251b8..dd91f2cc0efbe2c19b2d125c450ce2a23ab8b5d4 100644
--- a/opentech/static_src/src/app/src/redux/selectors/notes.js
+++ b/opentech/static_src/src/app/src/redux/selectors/notes.js
@@ -10,13 +10,20 @@ export const getNoteOfID = (noteID) => createSelector(
 
 export const getNotesFetchState = state => state.notes.isFetching === true;
 
-export const getNotesErrorState = state => state.notes.isErrored === true;
+export const getNotesErrorState = state => state.notes.error.errored === true;
+
+export const getNotesErrorMessage = state => state.notes.error.message;
 
 export const getNoteIDsForSubmissionOfID = submissionID => createSelector(
     [getSubmissionOfID(submissionID)],
     submission => (submission || {}).comments || []
 );
 
+export const getLatestNoteForSubmissionOfID = submissionID => createSelector(
+    [getNoteIDsForSubmissionOfID(submissionID)],
+    notes => notes[0] || null
+);
+
 const getNoteCreatingErrors = state => state.notes.createError;
 
 export const getNoteCreatingErrorForSubmission = submissionID => createSelector(
diff --git a/package-lock.json b/package-lock.json
index 407c8c2cc2a5689eb9f05e8b66e40a3d8925f530..19268ff324e2a31f659e208d18d2d138b7e91404 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -813,6 +813,11 @@
                 "through2": "^2.0.3"
             }
         },
+        "@rooks/use-interval": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@rooks/use-interval/-/use-interval-1.2.0.tgz",
+            "integrity": "sha512-gtYrJUNdYgflZRGwT7NjVqLRiD3EVT1ZxDqDCTStMN5jWtgIZulm8OJ3ZzZn62UTXtoSPcE/pJkQDccaD/NzhA=="
+        },
         "@svgr/babel-plugin-add-jsx-attribute": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.0.0.tgz",
@@ -8505,20 +8510,20 @@
             }
         },
         "react-dom": {
-            "version": "16.7.0",
-            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz",
-            "integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==",
+            "version": "16.8.1",
+            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.1.tgz",
+            "integrity": "sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg==",
             "requires": {
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1",
                 "prop-types": "^15.6.2",
-                "scheduler": "^0.12.0"
+                "scheduler": "^0.13.1"
             }
         },
         "react-hot-loader": {
-            "version": "4.6.3",
-            "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.6.3.tgz",
-            "integrity": "sha512-FUvRO8dwbeLnc3mgLn8ARuSh8NnLBYJyiRjFn+grY/5GupSyPqv0U7ixgwXro77hwDplhm8z9wU7FlJ8kZqiAQ==",
+            "version": "4.6.5",
+            "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.6.5.tgz",
+            "integrity": "sha512-ZPAJEWVd8KDdm6dcK0iWrnJiGHruLrcbkIpqn/wQmNjnROpsm2nzrWh23Yh3I/XAjB+35pMa/ZgariwGqwFD9A==",
             "dev": true,
             "requires": {
                 "fast-levenshtein": "^2.0.6",
@@ -9439,9 +9444,9 @@
             "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
         },
         "scheduler": {
-            "version": "0.12.0",
-            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz",
-            "integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==",
+            "version": "0.13.1",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz",
+            "integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==",
             "requires": {
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1"
diff --git a/package.json b/package.json
index de38ee37b999164cb0a0d60975d79d09e9d0f1ae..a80398b653ac9d44a095ebeb03afd7cea9f11cd5 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
     ],
     "dependencies": {
         "@babel/polyfill": "^7.2.5",
+        "@rooks/use-interval": "^1.2.0",
         "@svgr/webpack": "^4.1.0",
         "del": "^3.0.0",
         "gulp": "^4.0.0",
@@ -30,7 +31,7 @@
         "node-sass-import-once": "^1.2.0",
         "prop-types": "^15.6.2",
         "react": "^16.8.1",
-        "react-dom": "^16.7.0",
+        "react-dom": "^16.8.1",
         "react-redux": "^6.0.0",
         "react-rte": "^0.16.1",
         "react-transition-group": "^2.5.3",
@@ -55,7 +56,7 @@
         "gulp-sass-lint": "^1.4.0",
         "gulp-sourcemaps": "^2.6.4",
         "node-sass": "^4.11.0",
-        "react-hot-loader": "^4.6.3",
+        "react-hot-loader": "^4.6.5",
         "redux-devtools-extension": "^2.13.7",
         "sass-loader": "^7.1.0",
         "style-loader": "^0.23.1",